Class: RailsAiContext::Tools::GetPartialInterface
- Defined in:
- lib/rails_ai_context/tools/get_partial_interface.rb
Constant Summary
Constants inherited from BaseTool
BaseTool::SESSION_CONTEXT, BaseTool::SHARED_CACHE
Class Method Summary collapse
Methods inherited from BaseTool
abstract!, abstract?, cache_key, cached_context, config, extract_method_source_from_file, extract_method_source_from_string, find_closest_match, fuzzy_find_key, inherited, not_found_response, paginate, rails_app, registered_tools, reset_all_caches!, reset_cache!, session_queries, session_record, session_reset!, set_call_params, text_response
Class Method Details
.call(partial:, detail: "standard", server_context: nil) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/rails_ai_context/tools/get_partial_interface.rb', line 28 def self.call(partial:, detail: "standard", server_context: nil) # Guard: required parameter if partial.nil? || partial.strip.empty? return text_response("The `partial` parameter is required. Provide a partial path relative to app/views (e.g. 'shared/status_badge').") end # Reject path traversal attempts if partial.include?("..") || partial.start_with?("/") return text_response("Path not allowed: #{partial}") end root = Rails.root.to_s views_dir = File.join(root, "app", "views") unless Dir.exist?(views_dir) return text_response("No app/views/ directory found.") end # Resolve partial to actual file path file_path = resolve_partial_path(views_dir, partial) unless file_path available = find_available_partials(views_dir, root) return not_found_response("Partial", partial, available, recovery_tool: "Call rails_get_view(detail:\"summary\") to see all views and partials") end if File.size(file_path) > max_file_size return text_response("Partial file too large: #{file_path} (#{File.size(file_path)} bytes, max: #{max_file_size})") end source = safe_read(file_path) return text_response("Could not read partial file.") unless source relative_path = file_path.sub("#{root}/", "") partial_name = file_path.sub("#{views_dir}/", "") # Parse the partial's interface magic_locals = extract_magic_comment_locals(source) render_sites = find_render_sites(views_dir, partial, root) method_calls = {} # Primary: locals from render call sites (ground truth) render_locals = render_sites.flat_map { |rs| rs[:locals] || [] }.uniq # Secondary: local_assigns checks + defined? guards in partial source source_locals = extract_local_variable_references(source) # Combine: render-site locals first, then source-detected locals # Filter out noise: single chars, capitalized words, known helpers all_locals = (magic_locals + render_locals + source_locals).uniq .reject { |l| l.length <= 1 || l.match?(/\A[A-Z]/) || l.match?(/\Arender_/) } .sort # Extract method calls only for confirmed locals method_calls = extract_method_calls_on_locals(source, all_locals) if all_locals.any? case detail when "summary" format_summary(partial_name, all_locals, magic_locals, render_sites) when "standard" format_standard(partial_name, relative_path, source, all_locals, magic_locals, render_sites, method_calls) when "full" format_full(partial_name, relative_path, source, all_locals, magic_locals, render_sites, method_calls) else text_response("Unknown detail level: #{detail}. Use summary, standard, or full.") end end |