Class: Rigor::Plugin::Registry

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/plugin/registry.rb

Overview

Read-side query API over the plugins loaded for a single ‘Analysis::Runner.run`. Constructed by Loader.load and exposed downstream so the contribution merger (slice 3) and diagnostic provenance (slice 5) can iterate over loaded plugin instances in deterministic order.

The registry is read-only after construction; ordering is the order in which Loader resolved configuration entries, which is project-config order with plugin-id alphabetical as the tie-breaker.

ADR-15 Phase 3 — alongside the instantiated ‘plugins`, the registry carries `blueprints`: a frozen, Ractor-shareable `Array<Blueprint>` that records how to re-instantiate the same plugin set in a worker Ractor. The eventual Phase 4 pool ships `blueprints` across the boundary and calls Registry.materialize per-Ractor; the live `plugins` carriage on the coordinator registry stays unchanged.

Constant Summary collapse

EMPTY =
new.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(plugins: [], load_errors: [], blueprints: []) ⇒ Registry

Returns a new instance of Registry.

Parameters:

  • plugins (Array<Rigor::Plugin::Base>) (defaults to: [])

    instantiated plugin instances in deterministic order.

  • load_errors (Array<Rigor::Plugin::LoadError>) (defaults to: [])

    failures surfaced during loading. Each error is also turned into a diagnostic by the runner.

  • blueprints (Array<Rigor::Plugin::Blueprint>) (defaults to: [])

    frozen, Ractor-shareable replay descriptors aligned 1:1 with ‘plugins`. The loader fills this in; callers that construct Registry manually MAY pass `[]` and accept that materialize cannot replay the set.



39
40
41
42
43
44
# File 'lib/rigor/plugin/registry.rb', line 39

def initialize(plugins: [], load_errors: [], blueprints: [])
  @plugins = plugins.dup.freeze
  @load_errors = load_errors.dup.freeze
  @blueprints = blueprints.dup.freeze
  freeze
end

Instance Attribute Details

#blueprintsObject (readonly)

Returns the value of attribute blueprints.



27
28
29
# File 'lib/rigor/plugin/registry.rb', line 27

def blueprints
  @blueprints
end

#load_errorsObject (readonly)

Returns the value of attribute load_errors.



27
28
29
# File 'lib/rigor/plugin/registry.rb', line 27

def load_errors
  @load_errors
end

#pluginsObject (readonly)

Returns the value of attribute plugins.



27
28
29
# File 'lib/rigor/plugin/registry.rb', line 27

def plugins
  @plugins
end

Class Method Details

.materialize(blueprints:, services:) ⇒ Object

ADR-15 Phase 3 — build a fresh Registry from the supplied blueprint set by replaying Blueprint#materialize per entry against ‘services`. The returned registry carries NEW plugin instances (mutable per-Ractor accumulators included) and the same blueprint set, so a worker can hand the materialised registry to Environment without losing the replay handle. `load_errors` is intentionally empty: load-time failures already surfaced in the coordinator registry and don’t repeat per worker.



55
56
57
58
# File 'lib/rigor/plugin/registry.rb', line 55

def self.materialize(blueprints:, services:)
  plugins = blueprints.map { |bp| bp.materialize(services: services) }
  new(plugins: plugins, blueprints: blueprints, load_errors: [])
end

Instance Method Details

#any_load_errors?Boolean

Returns:

  • (Boolean)


73
74
75
# File 'lib/rigor/plugin/registry.rb', line 73

def any_load_errors?
  !load_errors.empty?
end

#contracts_for_path(path) ⇒ Object

ADR-28 — the subset of ‘protocol_contracts` whose `path_glob` matches `path`. Contract globs are authored project-root-relative (`lib/controller/*/.rb`); the analyzer may hand this method either a project-relative path (`rigor check` run from the project root) or an absolute one (run from elsewhere, or a spec tmpdir), so the glob is matched both directly and as a `**/`-prefixed path suffix. `File::FNM_PATHNAME` keeps `*` from crossing `/`; `File::FNM_EXTGLOB` enables `a,b` groups. Returns `[]` for a nil path so the binder can call this unconditionally.



156
157
158
159
160
161
# File 'lib/rigor/plugin/registry.rb', line 156

def contracts_for_path(path)
  return [] if path.nil?

  path_s = path.to_s
  protocol_contracts.select { |contract| path_matches_glob?(contract.path_glob, path_s) }
end

#empty?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/rigor/plugin/registry.rb', line 69

def empty?
  plugins.empty?
end

#find(id) ⇒ Object



60
61
62
63
# File 'lib/rigor/plugin/registry.rb', line 60

def find(id)
  id_s = id.to_s
  plugins.find { |plugin| plugin.manifest.id == id_s }
end

#hkt_overlay_registryObject

ADR-20 slice 6 — aggregate every loaded plugin’s manifest-declared HKT registrations + definitions into a single ‘Inference::HktRegistry` overlay that `Environment#hkt_registry` merges on top of the bundled `Builtins::HktBuiltins.registry`. Last plugin to register a URI wins (registration order determined by the user’s ‘plugins:` list); user `.rbs` overlays merge on top of this overlay last. Returns `Inference::HktRegistry::EMPTY` when no plugin contributes HKT entries so callers can skip the merge.



99
100
101
102
103
104
105
# File 'lib/rigor/plugin/registry.rb', line 99

def hkt_overlay_registry
  registrations = plugins.flat_map { |plugin| plugin.manifest.hkt_registrations }
  definitions = plugins.flat_map { |plugin| plugin.manifest.hkt_definitions }
  return Inference::HktRegistry::EMPTY if registrations.empty? && definitions.empty?

  Inference::HktRegistry.new(registrations: registrations, definitions: definitions)
end

#idsObject



65
66
67
# File 'lib/rigor/plugin/registry.rb', line 65

def ids
  plugins.map { |plugin| plugin.manifest.id }
end

#open_receiver?(class_name) ⇒ Boolean

Returns:

  • (Boolean)


127
128
129
130
131
# File 'lib/rigor/plugin/registry.rb', line 127

def open_receiver?(class_name)
  return false if class_name.nil?

  open_receivers.include?(class_name.to_s)
end

#open_receiversObject

ADR-26 — the aggregate set of “open” receiver class names declared across loaded plugins (manifest ‘open_receivers:`). A class is open when a plugin vouches that it responds beyond its RBS-declared method surface. `open_receiver?` is the membership predicate `Analysis::CheckRules` consults to skip the `call.undefined-method` rule for such a class.



123
124
125
# File 'lib/rigor/plugin/registry.rb', line 123

def open_receivers
  plugins.flat_map { |plugin| plugin.manifest.open_receivers }
end

#protocol_contractsObject

ADR-28 — flat, ordered list of every loaded plugin’s path-scoped method-protocol contracts, in plugin registration order. Read from each plugin’s ‘#protocol_contracts` (which the manifest backs by default but a plugin MAY override to fold in per-project config). Consumed by `Inference::MethodParameterBinder` (the parameter-type provision) and by contributing plugins’ ‘#diagnostics_for_file` hooks (the presence + return-type check).



142
143
144
# File 'lib/rigor/plugin/registry.rb', line 142

def protocol_contracts
  plugins.flat_map(&:protocol_contracts)
end

#signature_pathsObject

ADR-25 — flat, ordered list of every loaded plugin’s resolved RBS signature directories (absolute paths), in plugin registration order. ‘Environment.for_project` merges these into the signature-path set fed to `RbsLoader`, alongside the configuration’s ‘signature_paths:` and the `bundler:` / `rbs_collection:` discovery output.



113
114
115
# File 'lib/rigor/plugin/registry.rb', line 113

def signature_paths
  plugins.flat_map(&:signature_paths)
end

#type_node_resolversObject

ADR-13 slice 2 — flat ordered list of every loaded plugin’s manifest-declared TypeNodeResolver instances, in plugin registration order. Slice 3 wires this into the parser’s resolver chain; until then the method is a read-side aggregator only. The first non-nil ‘#resolve(node, scope)` return wins per ADR-13 WD3 / WD5 — registration order is the user’s lever.



84
85
86
# File 'lib/rigor/plugin/registry.rb', line 84

def type_node_resolvers
  plugins.flat_map { |plugin| plugin.manifest.type_node_resolvers }
end