Class: Rigor::Plugin::Loader
- Inherits:
-
Object
- Object
- Rigor::Plugin::Loader
- Defined in:
- lib/rigor/plugin/loader.rb,
sig/rigor/plugin/loader.rbs
Overview
Resolves the project's .rigor.yml plugins: entries into
instantiated plugin instances. The other Plugin namespaces
(Services, Registry, Base, etc.) are not yet
sig-covered, so reference them as untyped for now. The
critical declaration here is self.load: without it, Steep
matches against Kernel#load's (::String, ?(::Module | bool)) -> bool
signature and reports MethodParameterMismatch for the
keyword-only shape.
Constant Summary collapse
- FEATURE_RESOLVER =
#194 slice 1 — resolve the file that satisfied
require <gem>by scanning$LOADED_FEATURESfor the entry ending in/<gem>.rb, LAST match preferred. Provenance-agnostic: it pins the actual file for a Bundler path gem, an installed gem, and a-I-injected checkout alike, and — because the feature is already present from the first load — it still resolves whenrequireno-ops on a repeat in-process load (ADR-88 WD4b). Returns nil, never raises, in the degenerate cases: a gem whose entry-file basename differs from the gem name, and a spec's fake requirer that never populated$LOADED_FEATURES. lambda do |gem_name| suffix = "/#{gem_name}.rb" $LOADED_FEATURES.rfind { |feature| feature.end_with?(suffix) } end
- ENGINE_ROOT =
#194 slice 2 (ADR-93 WD5) — the engine's own root, anchored from THIS file's location: the loader lives at
<root>/lib/rigor/plugin/loader.rb, so three levels up is the engine root. It resolves identically in a git checkout and inside an installedrigortypegem, because the gem packages theplugins/tree at the same relative path — which is exactly what makes it a trustworthy anchor for the engine's own bundled plugin copies. File.("../../..", __dir__)
Instance Attribute Summary collapse
-
#feature_resolver ⇒ Object
readonly
rubocop:disable Metrics/ClassLength.
-
#requirer ⇒ ^(String) -> untyped
readonly
rubocop:disable Metrics/ClassLength.
-
#services ⇒ Object
readonly
rubocop:disable Metrics/ClassLength.
Class Method Summary collapse
-
.bundled_plugin_path(gem_name) ⇒ Object
#194 slice 2 (ADR-93 WD5) — the absolute path of the engine's OWN bundled copy of a plugin gem (
<ENGINE_ROOT>/plugins/<gem>/lib/<gem>.rb), or nil when the engine does not bundle it. - .load(configuration:, services:, requirer: ->(name) { require name }, feature_resolver: FEATURE_RESOLVER) ⇒ Object
Instance Method Summary collapse
-
#initialize(services:, requirer: ->(name) { require name }, feature_resolver: FEATURE_RESOLVER) ⇒ Loader
constructor
A new instance of Loader.
- #load(entries) ⇒ Registry
Constructor Details
#initialize(services:, requirer: ->(name) { require name }, feature_resolver: FEATURE_RESOLVER) ⇒ Loader
Returns a new instance of Loader.
54 55 56 57 58 |
# File 'lib/rigor/plugin/loader.rb', line 54 def initialize(services:, requirer: ->(name) { require name }, feature_resolver: FEATURE_RESOLVER) @services = services @requirer = requirer @feature_resolver = feature_resolver end |
Instance Attribute Details
#feature_resolver ⇒ Object (readonly)
rubocop:disable Metrics/ClassLength
26 27 28 |
# File 'lib/rigor/plugin/loader.rb', line 26 def feature_resolver @feature_resolver end |
#requirer ⇒ ^(String) -> untyped (readonly)
rubocop:disable Metrics/ClassLength
26 27 28 |
# File 'lib/rigor/plugin/loader.rb', line 26 def requirer @requirer end |
#services ⇒ Object (readonly)
rubocop:disable Metrics/ClassLength
26 27 28 |
# File 'lib/rigor/plugin/loader.rb', line 26 def services @services end |
Class Method Details
.bundled_plugin_path(gem_name) ⇒ Object
#194 slice 2 (ADR-93 WD5) — the absolute path of the engine's OWN bundled copy of a plugin gem
(<ENGINE_ROOT>/plugins/<gem>/lib/<gem>.rb), or nil when the engine does not bundle it. A bundled
plugin is required by this path rather than by gem name, so a stale installed rigortype gem can
never displace the engine's own versioned copy through RubyGems name resolution — the engine and its
bundled plugins are versioned together. Returns nil for a third-party / project-bundle plugin and for
a trimmed packaging (the ADR-27 single-binary target that ships no plugins/ tree), where the caller
falls back to today's gem-name require so no install mode regresses. The rule is uniform across the
auto-wired rigor-rbs-inline default and every user-listed entry, because the skew mechanism is
identical for both. Also read by rigor doctor's skew check (#194 slice 3) to decide which loaded
plugins the engine bundles.
74 75 76 77 |
# File 'lib/rigor/plugin/loader.rb', line 74 def self.bundled_plugin_path(gem_name) path = File.join(ENGINE_ROOT, "plugins", gem_name, "lib", "#{gem_name}.rb") File.file?(path) ? path : nil end |
.load(configuration:, services:, requirer: ->(name) { require name }, feature_resolver: FEATURE_RESOLVER) ⇒ Object
60 61 62 |
# File 'lib/rigor/plugin/loader.rb', line 60 def self.load(configuration:, services:, requirer: ->(name) { require name }, feature_resolver: FEATURE_RESOLVER) new(services: services, requirer: requirer, feature_resolver: feature_resolver).load(configuration.plugins) end |
Instance Method Details
#load(entries) ⇒ Registry
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/rigor/plugin/loader.rb', line 81 def load(entries) plugins = [] load_errors = [] seen_ids = {} # #194 slice 1 — `gem name => resolved file path` for every gem the loader successfully required. # A frozen plugin instance (e.g. `rigor-rbs-inline` self-freezes per ADR-32) can't carry the path, # so it rides on the Registry keyed by gem name; the `rigor plugins` loaded row reads it back. @resolved_gem_paths = {} Array(entries).each_with_index do |raw, index| entry = normalise_entry(raw, index) rescue LoadError => e load_errors << e else # ADR-93 WD2 — `enabled: false` opts a listed plugin out entirely: it is neither required nor # trusted nor id-checked. This is the project-level opt-out for the auto-wired `rigor-rbs-inline` # default (a user re-lists it with `enabled: false`), but it works for any entry. next unless entry[:enabled] begin plugin = resolve_and_instantiate(entry, seen_ids) plugins << plugin if plugin rescue LoadError => e load_errors << e end end # ADR-9 slice 5 — topological sort by `manifest(consumes:)` so producers run before consumers, plus # early `missing-producer` validation. Cycles surface as `dependency-cycle` LoadErrors. When # validation fails, the offending plugin(s) drop from the returned plugins list and the LoadError # surfaces alongside any earlier failure. plugins, sort_errors = topo_sort_plugins(plugins) load_errors.concat(sort_errors) blueprints = plugins.map { |plugin| Blueprint.new(klass_name: plugin.class.name, config: plugin.config) } Registry.new(plugins: plugins, blueprints: blueprints, load_errors: load_errors, resolved_gem_paths: @resolved_gem_paths) end |