Class: Ace::Support::Fs::Molecules::ProjectRootFinder
- Inherits:
-
Object
- Object
- Ace::Support::Fs::Molecules::ProjectRootFinder
- Defined in:
- lib/ace/support/fs/molecules/project_root_finder.rb
Overview
Find project root directory based on markers
Constant Summary collapse
- DEFAULT_MARKERS =
Common project root markers in order of preference
%w[ .git Gemfile package.json Cargo.toml pyproject.toml go.mod .hg .svn Rakefile Makefile ].freeze
Class Attribute Summary collapse
-
.cache_mutex ⇒ Object
readonly
Returns the value of attribute cache_mutex.
Class Method Summary collapse
- .cache ⇒ Object
-
.clear_cache! ⇒ Object
Clear the cache (thread-safe).
-
.find(start_path: nil, markers: DEFAULT_MARKERS) ⇒ String?
Class method for convenience.
-
.find_or_current(start_path: nil, markers: DEFAULT_MARKERS) ⇒ String
Class method to find or use current directory.
Instance Method Summary collapse
-
#find ⇒ String?
Find project root directory with caching.
-
#find_or_current ⇒ String
Find project root or fall back to current directory.
-
#in_project? ⇒ Boolean
Check if we’re in a project directory.
-
#initialize(markers: DEFAULT_MARKERS, start_path: nil) ⇒ ProjectRootFinder
constructor
Initialize finder with optional custom markers.
-
#relative_path(path) ⇒ String?
Get the relative path from project root to a given path.
Constructor Details
#initialize(markers: DEFAULT_MARKERS, start_path: nil) ⇒ ProjectRootFinder
Initialize finder with optional custom markers
42 43 44 45 46 47 48 |
# File 'lib/ace/support/fs/molecules/project_root_finder.rb', line 42 def initialize(markers: DEFAULT_MARKERS, start_path: nil) if markers.nil? || markers.empty? raise ArgumentError, "markers cannot be nil or empty" end @markers = markers @start_path = start_path ? Atoms::PathExpander.(start_path) : Dir.pwd end |
Class Attribute Details
.cache_mutex ⇒ Object (readonly)
Returns the value of attribute cache_mutex.
31 32 33 |
# File 'lib/ace/support/fs/molecules/project_root_finder.rb', line 31 def cache_mutex @cache_mutex end |
Class Method Details
.cache ⇒ Object
33 34 35 |
# File 'lib/ace/support/fs/molecules/project_root_finder.rb', line 33 def cache @cache ||= {} end |
.clear_cache! ⇒ Object
Clear the cache (thread-safe)
105 106 107 108 109 |
# File 'lib/ace/support/fs/molecules/project_root_finder.rb', line 105 def self.clear_cache! cache_mutex.synchronize do cache.clear end end |
.find(start_path: nil, markers: DEFAULT_MARKERS) ⇒ String?
Class method for convenience
113 114 115 |
# File 'lib/ace/support/fs/molecules/project_root_finder.rb', line 113 def self.find(start_path: nil, markers: DEFAULT_MARKERS) new(start_path: start_path, markers: markers).find end |
.find_or_current(start_path: nil, markers: DEFAULT_MARKERS) ⇒ String
Class method to find or use current directory
119 120 121 |
# File 'lib/ace/support/fs/molecules/project_root_finder.rb', line 119 def self.find_or_current(start_path: nil, markers: DEFAULT_MARKERS) new(start_path: start_path, markers: markers).find_or_current end |
Instance Method Details
#find ⇒ String?
Find project root directory with caching
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/ace/support/fs/molecules/project_root_finder.rb', line 52 def find # Check environment variable first project_root_env = env_project_root if project_root_env && !project_root_env.empty? project_root = Atoms::PathExpander.(project_root_env) if Dir.exist?(project_root) && path_within_root?(@start_path, project_root) return project_root end end cache_key = "#{@start_path}:#{@markers.join(",")}" # Thread-safe cache access self.class.cache_mutex.synchronize do # Return cached result if available return self.class.cache[cache_key] if self.class.cache.key?(cache_key) # Find and cache the result result = find_without_cache self.class.cache[cache_key] = result result end end |
#find_or_current ⇒ String
Find project root or fall back to current directory
78 79 80 |
# File 'lib/ace/support/fs/molecules/project_root_finder.rb', line 78 def find_or_current find || Dir.pwd end |
#in_project? ⇒ Boolean
Check if we’re in a project directory
84 85 86 |
# File 'lib/ace/support/fs/molecules/project_root_finder.rb', line 84 def in_project? !find.nil? end |
#relative_path(path) ⇒ String?
Get the relative path from project root to a given path
91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/ace/support/fs/molecules/project_root_finder.rb', line 91 def relative_path(path) root = find return nil unless root # Use realpath to handle symlinks and resolve paths real_root = File.realpath(root) real_path = File.realpath(Atoms::PathExpander.(path)) return nil unless real_path.start_with?(real_root) Pathname.new(real_path).relative_path_from(Pathname.new(real_root)).to_s end |