Class: RailsAiBridge::PathResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_ai_bridge/path_resolver.rb

Overview

Resolves configured Rails logical paths to filesystem paths without leaking machine-specific absolute paths into generated assistant context.

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ PathResolver

Returns a new instance of PathResolver.

Parameters:

  • app (Rails::Application)

    host Rails application



114
115
116
117
# File 'lib/rails_ai_bridge/path_resolver.rb', line 114

def initialize(app)
  @app = app
  @root = app.root.to_s
end

Instance Method Details

#directories_for(logical_path) ⇒ Array<String>

Resolves directories for a logical Rails path.

Configured +app.paths+ entries are preferred. When none are configured, the conventional root-relative directory is returned.

Parameters:

  • logical_path (String)

    Rails path key, such as +"app/models"+

Returns:

  • (Array<String>)

    absolute directory paths



126
127
128
129
130
131
# File 'lib/rails_ai_bridge/path_resolver.rb', line 126

def directories_for(logical_path)
  entries = configured_paths_for(logical_path)
  entries = [logical_path] if entries.empty?

  entries.map { |path| File.expand_path(path.to_s, @root) }.uniq
end

#existing_file_for(logical_path, relative_file) ⇒ String?

Finds the first existing file under a logical Rails path.

Parameters:

  • logical_path (String)

    Rails path key, such as +"app/models"+

  • relative_file (String)

    file path relative to the resolved directory

Returns:

  • (String, nil)

    absolute file path when found

Raises:

  • (ArgumentError)

    when +relative_file+ is absolute or contains traversal segments



165
166
167
168
169
170
171
172
# File 'lib/rails_ai_bridge/path_resolver.rb', line 165

def existing_file_for(logical_path, relative_file)
  safe_file = SafeRelativePath.new(relative_file, argument_name: 'relative_file').to_s

  directories_for(logical_path).find do |path|
    candidate = SafeJoin.new(path, safe_file).to_s
    return candidate if File.exist?(candidate)
  end
end

#files_for(logical_path, extension:) ⇒ Array<String>

Finds files under every directory for a logical Rails path.

Parameters:

  • logical_path (String)

    Rails path key, such as +"app/models"+

  • extension (String)

    file extension without a leading dot

Returns:

  • (Array<String>)

    absolute file paths



138
139
140
# File 'lib/rails_ai_bridge/path_resolver.rb', line 138

def files_for(logical_path, extension:)
  glob_for(logical_path, "**/*.#{extension}")
end

#glob_for(logical_path, pattern) ⇒ Array<String>

Finds files matching a glob under every directory for a logical Rails path.

The pattern must be a safe relative glob. Absolute paths and traversal segments are rejected before +Dir.glob+ runs.

Parameters:

  • logical_path (String)

    Rails path key, such as +"app/views"+

  • pattern (String)

    glob pattern relative to each resolved directory

Returns:

  • (Array<String>)

    absolute file paths

Raises:

  • (ArgumentError)

    when +pattern+ is absolute or contains traversal segments



151
152
153
154
155
156
157
# File 'lib/rails_ai_bridge/path_resolver.rb', line 151

def glob_for(logical_path, pattern)
  safe_pattern = SafeRelativePath.new(pattern, argument_name: 'pattern').to_s

  directories_for(logical_path).flat_map do |path|
    Dir.exist?(path) ? Dir.glob(File.join(path, safe_pattern)) : []
  end
end

#logical_file_path(absolute_path, logical_path:) ⇒ String

Converts an absolute file path under a logical Rails path into a stable logical path for generated context.

Parameters:

  • absolute_path (String)

    filesystem path to map

  • logical_path (String)

    Rails path key, such as +"app/models"+

Returns:

  • (String)

    logical context path, root-relative path, or basename fallback



180
181
182
183
184
185
186
187
# File 'lib/rails_ai_bridge/path_resolver.rb', line 180

def logical_file_path(absolute_path, logical_path:)
  path = File.expand_path(absolute_path.to_s)
  matching_root = matching_root_for(path, logical_path)

  return logical_path_from_root(path, matching_root, logical_path) if matching_root

  relative_to_root(path)
end