Class: Ace::Support::Fs::Atoms::PathExpander
- Inherits:
-
Object
- Object
- Ace::Support::Fs::Atoms::PathExpander
- Defined in:
- lib/ace/support/fs/atoms/path_expander.rb
Overview
Path expansion and resolution with automatic context inference
Supports:
-
Instance-based API for context-aware resolution
-
Protocol URIs (wfi://, guide://, tmpl://, task://, prompt://)
-
Source-relative paths (./, ../)
-
Project-relative paths (no prefix)
-
Environment variables ($VAR, $VAR)
-
Backward compatible class methods for utilities
Constant Summary collapse
- PROTOCOL_PATTERN =
Protocol pattern for URI detection
%r{^[a-z][a-z0-9+.-]*://}
Instance Attribute Summary collapse
-
#project_root ⇒ Object
readonly
Instance attributes.
-
#source_dir ⇒ Object
readonly
Instance attributes.
Class Method Summary collapse
-
.absolute?(path) ⇒ Boolean
Check if path is absolute.
-
.basename(path, suffix = nil) ⇒ String
Get base name from path.
-
.class_get_env(var_name) ⇒ String?
Access environment variable by name (class-level) Extracted to allow test stubbing without modifying global ENV.
-
.dirname(path) ⇒ String
Get directory name from path.
-
.expand(path) ⇒ String
Expand path with tilde and environment variables Legacy stateless method for backward compatibility.
-
.for_cli(project_root: nil) ⇒ PathExpander
Create expander for CLI context (no source file) Uses current directory as source_dir.
-
.for_file(source_file, project_root: nil) ⇒ PathExpander
Create expander for a source file (config, workflow, template, prompt) Automatically infers source_dir, uses provided or default project_root.
-
.join(*parts) ⇒ String
Join path components safely.
-
.normalize(path) ⇒ String
Normalize path (remove .., ., duplicates slashes).
-
.protocol?(path) ⇒ Boolean
Check if path is a protocol URI.
-
.protocol_resolver ⇒ Object?
Get the current protocol resolver (thread-safe).
-
.register_protocol_resolver(resolver) ⇒ Object
Register a protocol resolver (e.g., ace-nav) Thread-safe registration using mutex.
-
.relative(path, base) ⇒ String
Make path relative to base.
-
.reset_protocol_resolver! ⇒ Object
private
Reset protocol resolver (for testing).
Instance Method Summary collapse
-
#initialize(source_dir:, project_root:) ⇒ PathExpander
constructor
Initialize with explicit context.
-
#resolve(path) ⇒ String
Resolve path using instance context Handles: protocols, source-relative (./), project-relative, env vars, absolute.
Constructor Details
#initialize(source_dir:, project_root:) ⇒ PathExpander
Initialize with explicit context
76 77 78 79 80 81 82 83 84 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 76 def initialize(source_dir:, project_root:) if source_dir.nil? || project_root.nil? raise ArgumentError, "PathExpander requires both 'source_dir' and 'project_root' " \ "(got source_dir: #{source_dir.inspect}, project_root: #{project_root.inspect})" end @source_dir = source_dir @project_root = project_root end |
Instance Attribute Details
#project_root ⇒ Object (readonly)
Instance attributes
23 24 25 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 23 def project_root @project_root end |
#source_dir ⇒ Object (readonly)
Instance attributes
23 24 25 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 23 def source_dir @source_dir end |
Class Method Details
.absolute?(path) ⇒ Boolean
Check if path is absolute
222 223 224 225 226 227 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 222 def self.absolute?(path) return false if path.nil? path_str = path.to_s Pathname.new(path_str).absolute? end |
.basename(path, suffix = nil) ⇒ String
Get base name from path
208 209 210 211 212 213 214 215 216 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 208 def self.basename(path, suffix = nil) return nil if path.nil? if suffix File.basename(path.to_s, suffix) else File.basename(path.to_s) end end |
.class_get_env(var_name) ⇒ String?
Access environment variable by name (class-level) Extracted to allow test stubbing without modifying global ENV
179 180 181 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 179 def self.class_get_env(var_name) ENV[var_name] end |
.dirname(path) ⇒ String
Get directory name from path
198 199 200 201 202 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 198 def self.dirname(path) return nil if path.nil? File.dirname(path.to_s) end |
.expand(path) ⇒ String
Expand path with tilde and environment variables Legacy stateless method for backward compatibility
160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 160 def self.(path) return nil if path.nil? = path.to_s.dup # Expand environment variables (uses class_get_env for testability) .gsub!(/\$([A-Z_][A-Z0-9_]*)/i) do |match| class_get_env(match[1..-1]) || match end # Expand tilde File.() end |
.for_cli(project_root: nil) ⇒ PathExpander
Create expander for CLI context (no source file) Uses current directory as source_dir
60 61 62 63 64 65 66 67 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 60 def self.for_cli(project_root: nil) resolved_root = project_root || Dir.pwd new( source_dir: Dir.pwd, project_root: resolved_root ) end |
.for_file(source_file, project_root: nil) ⇒ PathExpander
Create expander for a source file (config, workflow, template, prompt) Automatically infers source_dir, uses provided or default project_root
43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 43 def self.for_file(source_file, project_root: nil) = File.(source_file) source_dir = File.dirname() # Use provided project_root or fall back to current directory # Note: For full project root detection, caller should use # Ace::Support::Fs::Molecules::ProjectRootFinder and pass the result resolved_root = project_root || Dir.pwd new(source_dir: source_dir, project_root: resolved_root) end |
.join(*parts) ⇒ String
Join path components safely
187 188 189 190 191 192 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 187 def self.join(*parts) parts = parts.flatten.compact.map(&:to_s) return "" if parts.empty? File.join(*parts) end |
.normalize(path) ⇒ String
Normalize path (remove .., ., duplicates slashes)
250 251 252 253 254 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 250 def self.normalize(path) return nil if path.nil? Pathname.new(path.to_s).cleanpath.to_s end |
.protocol?(path) ⇒ Boolean
Check if path is a protocol URI
123 124 125 126 127 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 123 def self.protocol?(path) return false if path.nil? || path.empty? !!(path.to_s =~ PROTOCOL_PATTERN) end |
.protocol_resolver ⇒ Object?
Get the current protocol resolver (thread-safe)
141 142 143 144 145 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 141 def self.protocol_resolver protocol_resolver_mutex.synchronize do @protocol_resolver end end |
.register_protocol_resolver(resolver) ⇒ Object
Register a protocol resolver (e.g., ace-nav) Thread-safe registration using mutex.
133 134 135 136 137 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 133 def self.register_protocol_resolver(resolver) protocol_resolver_mutex.synchronize do @protocol_resolver = resolver end end |
.relative(path, base) ⇒ String
Make path relative to base
234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 234 def self.relative(path, base) return nil if path.nil? || base.nil? path_obj = Pathname.new((path)) base_obj = Pathname.new((base)) path_obj.relative_path_from(base_obj).to_s rescue ArgumentError # Paths are on different drives or one is relative path end |
.reset_protocol_resolver! ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Reset protocol resolver (for testing)
149 150 151 152 153 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 149 def self.reset_protocol_resolver! protocol_resolver_mutex.synchronize do @protocol_resolver = nil end end |
Instance Method Details
#resolve(path) ⇒ String
Resolve path using instance context Handles: protocols, source-relative (./), project-relative, env vars, absolute
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/ace/support/fs/atoms/path_expander.rb', line 92 def resolve(path) return nil if path.nil? || path.empty? path_str = path.to_s # Check for protocol URIs first if self.class.protocol?(path_str) return resolve_protocol(path_str) end # Expand environment variables = (path_str) # Handle absolute paths return File.() if Pathname.new().absolute? # Handle source-relative paths (./ or ../) if .start_with?("./", "../") return File.(, @source_dir) end # Default: project-relative paths File.(, @project_root) end |