Class: Rigor::Plugin::Base
- Inherits:
-
Object
- Object
- Rigor::Plugin::Base
- Defined in:
- lib/rigor/plugin/base.rb
Overview
Base class every Rigor plugin subclasses. The plugin gem subclasses Base, declares its identity through Base.manifest, registers the subclass with register, and overrides #init to wire up any state it needs from the injected service container.
Slice 1 ships only the registration / loading plumbing. The protocol hooks (dynamic-return contributions, type-specifying contributions, dynamic reflection) land in subsequent v0.1.0 slices and arrive as additional methods on this class.
Example plugin:
class MyRailsPlugin < Rigor::Plugin::Base
manifest(
id: "rails",
version: "0.1.0",
description: "Rails framework support for Rigor"
)
def init(services)
@reflection = services.reflection
@type = services.type
end
end
Rigor::Plugin.register(MyRailsPlugin)
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#services ⇒ Object
readonly
Returns the value of attribute services.
Class Method Summary collapse
-
.manifest(**fields) ⇒ Object
Declares the plugin’s manifest.
-
.producer(id, serialize: nil, deserialize: nil, &block) ⇒ Object
ADR-7 § “Slice 6-A” — DSL declaration of a cached producer.
-
.producers ⇒ Object
Frozen snapshot of the producer table.
Instance Method Summary collapse
-
#cache_for(producer_id, params: {}, descriptor: nil) ⇒ Object
ADR-7 § “Slice 6-A” — returns a callable that performs a ‘Cache::Store#fetch_or_compute` round-trip for the named producer.
-
#diagnostics_for_file(path:, scope:, root:) ⇒ Object
ADR-7 § “Slice 5-A” — per-file diagnostic emission hook.
-
#init(services) ⇒ Object
Override in subclasses to wire any state the plugin needs from the injected service container.
-
#initialize(services:, config: {}) ⇒ Base
constructor
A new instance of Base.
-
#io_boundary ⇒ Object
ADR-7 § “Slice 6-A/6-B” — per-plugin IoBoundary.
-
#manifest ⇒ Object
Convenience accessor — ‘manifest` on the instance returns the class-level manifest declaration.
Constructor Details
#initialize(services:, config: {}) ⇒ Base
Returns a new instance of Base.
101 102 103 104 |
# File 'lib/rigor/plugin/base.rb', line 101 def initialize(services:, config: {}) @services = services @config = config.freeze end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
99 100 101 |
# File 'lib/rigor/plugin/base.rb', line 99 def config @config end |
#services ⇒ Object (readonly)
Returns the value of attribute services.
99 100 101 |
# File 'lib/rigor/plugin/base.rb', line 99 def services @services end |
Class Method Details
.manifest(**fields) ⇒ Object
43 44 45 46 47 48 49 50 51 |
# File 'lib/rigor/plugin/base.rb', line 43 def manifest(**fields) if fields.empty? raise ArgumentError, "plugin #{self} did not declare a manifest" unless defined?(@manifest) && @manifest return @manifest end @manifest = Manifest.new(**fields) end |
.producer(id, serialize: nil, deserialize: nil, &block) ⇒ Object
ADR-7 § “Slice 6-A” — DSL declaration of a cached producer. Plugin authors write
class MyPlugin < Rigor::Plugin::Base
manifest(id: "rails", version: "0.1.0")
producer :schema_table do |params|
schema = io_boundary.read_file("db/schema.rb")
parse(schema, params)
end
end
The block runs through ‘instance_exec` so `self` inside the body is the plugin instance — `io_boundary`, `services`, `manifest`, `config` are all in scope. The block receives the call-site `params` Hash as its sole argument; the same params Hash mixes into the cache key per `Cache::Descriptor#cache_key_for`.
‘serialize:` / `deserialize:` are forwarded verbatim to `Cache::Store#fetch_or_compute`. Default round-trip is `Marshal.dump` / `Marshal.load` per the v0.0.9 callable surface; producers whose return values are not Marshal- clean must supply their own pair.
Producer ids are auto-prefixed ‘plugin.<manifest.id>.` at the cache layer (slice 6-C) so plugin-side ids cannot collide with built-in producers.
81 82 83 84 85 86 87 |
# File 'lib/rigor/plugin/base.rb', line 81 def producer(id, serialize: nil, deserialize: nil, &block) raise ArgumentError, "Plugin::Base.producer requires a block body" if block.nil? @producers ||= {} @producers[id.to_sym] = { block: block, serialize: serialize, deserialize: deserialize }.freeze id.to_sym end |
.producers ⇒ Object
Frozen snapshot of the producer table. Inherited producers from a superclass are intentionally NOT surfaced — Plugin::Base subclasses do not chain producers, and the loader instantiates one subclass per registration.
94 95 96 |
# File 'lib/rigor/plugin/base.rb', line 94 def producers (@producers || {}).dup.freeze end |
Instance Method Details
#cache_for(producer_id, params: {}, descriptor: nil) ⇒ Object
ADR-7 § “Slice 6-A” — returns a callable that performs a ‘Cache::Store#fetch_or_compute` round-trip for the named producer. The descriptor (per ADR-7 § “Slice 6-B”) is auto-assembled from the plugin’s ‘PluginEntry` template (id, version, config_hash) and the IoBoundary read history. The producer id is auto-prefixed `plugin.<manifest.id>.` per ADR-7 §“Slice 6-C” so plugin caches stay sandboxed from built-in producers.
When ‘services.cache_store` is `nil` (e.g. CLI `–no-cache`), the callable bypasses the cache and runs the producer block every time — same semantics as the v0.0.9 cache surface for built-in producers.
‘descriptor:` (optional, ADR-7 § “Slice 6” follow-up) supplies extra `Cache::Descriptor` rows the plugin author wants to compose into the auto-built descriptor — typically gem-version `GemEntry`, configuration-file `FileEntry` digests, or `ConfigEntry` rows for external state the IoBoundary cannot capture itself. The passed descriptor composes via `Cache::Descriptor.compose` with the auto-built one (PluginEntry template + boundary reads); per-slot conflicts raise `Cache::Descriptor::Conflict` to make divergent inputs visible rather than silently shadowing.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/rigor/plugin/base.rb', line 178 def cache_for(producer_id, params: {}, descriptor: nil) producer = self.class.producers[producer_id.to_sym] unless producer raise ArgumentError, "plugin #{manifest.id.inspect} did not declare producer #{producer_id.inspect}" end compute = -> { instance_exec(params, &producer[:block]) } store = services.cache_store return compute unless store prefixed_id = "plugin.#{manifest.id}.#{producer_id}" composed_descriptor = compose_cache_descriptor(descriptor) lambda do store.fetch_or_compute( producer_id: prefixed_id, params: params, descriptor: composed_descriptor, serialize: producer[:serialize], deserialize: producer[:deserialize], &compute ) end end |
#diagnostics_for_file(path:, scope:, root:) ⇒ Object
ADR-7 § “Slice 5-A” — per-file diagnostic emission hook. Override in plugin subclasses to return an array of ‘Rigor::Analysis::Diagnostic` rows for the analysed file. The runner stamps each returned diagnostic with `source_family: “plugin.<manifest.id>”` automatically per ADR-7 § “Slice 5-B”; plugin authors should construct diagnostics without setting `source_family` (any value they pass is overwritten).
‘path` is the analysed file path; `scope` is the entry `Rigor::Scope` after `ScopeIndexer` ran; `root` is the parsed `Prism::Node` root. Plugin authors traverse `root` themselves if they need node-scoped rules — the `Rule<TNode>` API ADR-2 § “Custom rules” mentions stays deferred to v0.1.x.
Default returns ‘[]` so plugins that contribute through other channels (e.g. slice-4 narrowing contributions, slice-6 cache producers) do not have to override.
133 134 135 |
# File 'lib/rigor/plugin/base.rb', line 133 def diagnostics_for_file(path:, scope:, root:) # rubocop:disable Lint/UnusedMethodArgument [] end |
#init(services) ⇒ Object
Override in subclasses to wire any state the plugin needs from the injected service container. Default is a no-op so plugins that only contribute through later-slice protocol hooks do not have to define an explicit body.
110 111 112 |
# File 'lib/rigor/plugin/base.rb', line 110 def init(services) # rubocop:disable Lint/UnusedMethodArgument nil end |
#io_boundary ⇒ Object
ADR-7 § “Slice 6-A/6-B” — per-plugin IoBoundary. Memoised so the boundary’s accumulated ‘FileEntry` rows persist across producer invocations within the same plugin instance and feed cache invalidation via `cache_for`.
148 149 150 |
# File 'lib/rigor/plugin/base.rb', line 148 def io_boundary @io_boundary ||= services.io_boundary_for(manifest.id) end |
#manifest ⇒ Object
Convenience accessor — ‘manifest` on the instance returns the class-level manifest declaration.
139 140 141 |
# File 'lib/rigor/plugin/base.rb', line 139 def manifest self.class.manifest end |