Class: RailsAiBridge::Introspector

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_ai_bridge/introspector.rb,
lib/rails_ai_bridge/introspector/timed_runner.rb,
lib/rails_ai_bridge/introspector/parallel_runner.rb

Overview

Orchestrates all sub-introspectors to build a complete picture of the Rails application for AI consumption.

Depending on configuration, introspectors are run sequentially (default) or concurrently via ParallelRunner. Sequential runs are timed via TimedRunner so duration data is available in debug logs regardless of the execution strategy.

:reek:RepeatedConditional -- static_mode? guards in build_metadata/run_single/run_parallel are the cleanest way to branch on app type

Examples:

Running all standard introspectors

context = RailsAiBridge::Introspector.new(Rails.application).call
context[:app_name]  #=> "MyApp"
context[:schema]    #=> { tables: { ... } }

Running a subset of introspectors

context = RailsAiBridge::Introspector.new(app).call(only: %i[schema routes])

Defined Under Namespace

Classes: ParallelRunner, TimedRunner

Constant Summary collapse

BUILTIN_INTROSPECTORS =

Registry of all built-in introspector classes, keyed by symbolic name.

The :standard preset uses 9 of these; the :full preset uses 27. Opt-in-only keys (e.g. :database_stats, :non_ar_models) are present here but excluded from both presets by default.

Returns:

  • (Hash{Symbol => Class})
{
  schema: Introspectors::SchemaIntrospector,
  models: Introspectors::ModelIntrospector,
  non_ar_models: Introspectors::NonArModelsIntrospector,
  routes: Introspectors::RouteIntrospector,
  jobs: Introspectors::JobIntrospector,
  gems: Introspectors::GemIntrospector,
  conventions: Introspectors::ConventionDetector,
  stimulus: Introspectors::StimulusIntrospector,
  database_stats: Introspectors::DatabaseStatsIntrospector,
  controllers: Introspectors::ControllerIntrospector,
  views: Introspectors::ViewIntrospector,
  turbo: Introspectors::TurboIntrospector,
  i18n: Introspectors::I18nIntrospector,
  config: Introspectors::ConfigIntrospector,
  active_storage: Introspectors::ActiveStorageIntrospector,
  action_text: Introspectors::ActionTextIntrospector,
  auth: Introspectors::AuthIntrospector,
  api: Introspectors::ApiIntrospector,
  tests: Introspectors::TestIntrospector,
  rake_tasks: Introspectors::RakeTaskIntrospector,
  assets: Introspectors::AssetPipelineIntrospector,
  devops: Introspectors::DevOpsIntrospector,
  action_mailbox: Introspectors::ActionMailboxIntrospector,
  migrations: Introspectors::MigrationIntrospector,
  seeds: Introspectors::SeedsIntrospector,
  middleware: Introspectors::MiddlewareIntrospector,
  engines: Introspectors::EngineIntrospector,
  multi_database: Introspectors::MultiDatabaseIntrospector,
  semantic: Introspectors::SemanticIntrospector
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ Introspector

Returns a new instance of Introspector.

Parameters:

  • app (Rails::Application)

    the Rails application to introspect



28
29
30
31
32
33
34
35
# File 'lib/rails_ai_bridge/introspector.rb', line 28

def initialize(app)
  @app    = app
  # archspec:disable dependencies.forbid -- FP: RailsAiBridge namespace accessor, not a cross-component dependency
  # archspec:disable dependencies.no_cycles -- FP: cycle from namespace reopening, not a real cross-component cycle
  @config = RailsAiBridge.configuration
  # archspec:enable dependencies.forbid
  # archspec:enable dependencies.no_cycles
end

Instance Attribute Details

#appRails::Application (readonly)

Returns the host application passed at construction.

Returns:

  • (Rails::Application)

    the host application passed at construction



22
23
24
# File 'lib/rails_ai_bridge/introspector.rb', line 22

def app
  @app
end

#configRailsAiBridge::Configuration (readonly)

Returns active gem configuration.

Returns:



25
26
27
# File 'lib/rails_ai_bridge/introspector.rb', line 25

def config
  @config
end

Instance Method Details

#app_nameString

Returns the application name derived from the Rails application class.

Tries module_parent_name first (Rails 6+), falling back to deconstantize on the full class name.

Returns:

  • (String)

    the application module name (e.g. "MyApp")



109
110
111
112
113
114
115
# File 'lib/rails_ai_bridge/introspector.rb', line 109

def app_name
  if app.class.respond_to?(:module_parent_name)
    app.class.module_parent_name
  else
    app.class.name.deconstantize
  end
end

#call(only: nil) ⇒ Hash

Runs all configured (or a specified subset of) introspectors and returns a unified context hash.

Metadata keys (+:app_name+, :ruby_version, :rails_version, etc.) are always present. Introspector results are merged in at the top level, keyed by their symbolic name (e.g. :schema, :routes).

When parallel introspection is enabled and more than one introspector is requested, execution is delegated to ParallelRunner. Otherwise each introspector runs sequentially, wrapped by TimedRunner for observability.

Parameters:

  • only (Array<Symbol>, nil) (defaults to: nil)

    optional subset of introspector keys to run; passes through #selected_introspectors

Returns:

  • (Hash)

    complete application context merged with metadata



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/rails_ai_bridge/introspector.rb', line 51

def call(only: nil)
  context = 

  names   = selected_introspectors(only)
  results = if parallel_enabled? && names.size > 1
              run_parallel(names)
            else
              run_sequential(names)
            end

  context.merge(results)
end

#resolve_introspector(name) ⇒ Object

Looks up and instantiates an introspector by name.

Checks config.additional_introspectors before falling back to BUILTIN_INTROSPECTORS.

Parameters:

  • name (Symbol)

    introspector key

Returns:

  • (Object)

    an instantiated introspector

Raises:



145
146
147
148
149
150
# File 'lib/rails_ai_bridge/introspector.rb', line 145

def resolve_introspector(name)
  introspector_class = config.additional_introspectors[name] || BUILTIN_INTROSPECTORS[name]
  raise ConfigurationError, "Unknown introspector: #{name}" unless introspector_class

  introspector_class.new(app)
end

#selected_introspectors(only) ⇒ Array<Symbol>

Resolves the list of introspector keys to run.

Returns config.effective_introspectors when only is blank. A non-blank only list is intersected with effective_introspectors (plus additional_introspectors keys) so MCP fetch_section cannot run schema or models when they were disabled by :regulated or disabled_introspection_categories, while host-registered extras remain fetchable.

Parameters:

  • only (Array<Symbol>, nil)

Returns:

  • (Array<Symbol>)


128
129
130
131
132
133
134
135
# File 'lib/rails_ai_bridge/introspector.rb', line 128

def selected_introspectors(only)
  allowed = config.effective_introspectors
  names = Array(only).compact
  return allowed if names.empty?

  extra = config.additional_introspectors.keys
  names.select { |name| allowed.include?(name) || extra.include?(name) }
end