Class: Rigor::Effects::Registry
- Inherits:
-
Object
- Object
- Rigor::Effects::Registry
- Defined in:
- lib/rigor/effects/registry.rb
Overview
The effect-label vocabulary: which spellings are recognised, which have been retired, and who
may open a new root (ADR-103 WD2; normative in
docs/type-specification/effect-labels.md).
The shared layer is hand-written in data/effects/registry.yml — Steins' v1 set verbatim,
Ruby's mutate leaves, the proposed shared core leaves and the application-meaning roots —
so a later slice or a plugin extends the data, not this Ruby. Extensions arrive through
#with, which is the one place root ownership is enforced.
A Registry is a frozen value object; #with returns a new one rather than mutating.
Defined Under Namespace
Classes: Error, InvalidLabelError, OwnershipError
Constant Summary collapse
- DATA_PATH =
File.("../../../data/effects/registry.yml", __dir__)
- SUGGESTION_DISTANCE_CAP =
How far a misspelling may be from a known label before #suggest declines to guess. Two edits catches a transposition or a dropped segment character ("io.nte", "nondet.tim") without proposing an unrelated label for a genuinely new spelling.
2
Instance Attribute Summary collapse
-
#labels ⇒ Object
readonly
Every declared label, sorted.
-
#roots ⇒ Object
readonly
The roots of the vocabulary — the outermost segments #with treats as already owned.
-
#vocabulary_version ⇒ Object
readonly
Returns the value of attribute vocabulary_version.
Class Method Summary collapse
-
.default ⇒ Object
The shared registry as shipped.
-
.for_configuration(configuration, plugin_facts: nil) ⇒ Object
The vocabulary one run works in: the shipped registry plus whatever
effects.labels:opened (ADR-103 WD2 / #385). -
.load_file(path) ⇒ Object
Build a registry from a registry-shaped YAML file.
Instance Method Summary collapse
-
#initialize(vocabulary_version:, labels:, retired: {}) ⇒ Registry
constructor
A new instance of Registry.
-
#known?(label) ⇒ Boolean
Whether the vocabulary recognises
label: an exact row, or an ancestor of one. -
#retired(label) ⇒ Object
The replacement labels for a retired spelling, or
nilwhen the spelling was never retired. -
#suggest(label) ⇒ Object
The nearest recognised label to a misspelling, within SUGGESTION_DISTANCE_CAP edits, or
nilwhen nothing is close enough. -
#with(labels:, owner:) ⇒ Object
A new registry carrying
labelson top of this one.
Constructor Details
#initialize(vocabulary_version:, labels:, retired: {}) ⇒ Registry
Returns a new instance of Registry.
83 84 85 86 87 88 89 90 |
# File 'lib/rigor/effects/registry.rb', line 83 def initialize(vocabulary_version:, labels:, retired: {}) @vocabulary_version = vocabulary_version @labels = labels.map(&:to_s).uniq.sort.freeze @known = build_known(@labels) @roots = @known.select { |label| Label.parent(label).nil? }.sort.freeze @retired = build_retired(retired) freeze end |
Instance Attribute Details
#labels ⇒ Object (readonly)
Every declared label, sorted. Implied ancestors (email, because email.send is declared)
are recognised by #known? but are not rows of the vocabulary and are not listed here.
94 95 96 |
# File 'lib/rigor/effects/registry.rb', line 94 def labels @labels end |
#roots ⇒ Object (readonly)
The roots of the vocabulary — the outermost segments #with treats as already owned.
97 98 99 |
# File 'lib/rigor/effects/registry.rb', line 97 def roots @roots end |
#vocabulary_version ⇒ Object (readonly)
Returns the value of attribute vocabulary_version.
81 82 83 |
# File 'lib/rigor/effects/registry.rb', line 81 def vocabulary_version @vocabulary_version end |
Class Method Details
.default ⇒ Object
The shared registry as shipped. Memoised: the YAML parse is a once-per-process cost, and
nothing consumes the registry yet, so it stays lazy rather than paying at require "rigor".
40 41 42 |
# File 'lib/rigor/effects/registry.rb', line 40 def self.default @default ||= load_file(DATA_PATH) end |
.for_configuration(configuration, plugin_facts: nil) ⇒ Object
The vocabulary one run works in: the shipped registry plus whatever effects.labels: opened
(ADR-103 WD2 / #385). The project is the one extender that may open ANY root — listing a label in
its own configuration is the vouching act — so this can only fail on a spelling Configuration
already rejected at load, and a failure degrades to the shipped vocabulary rather than taking the
run down.
Memoised on the label list, because the answer is a frozen value object and every effects surface in a run asks for the same one: the envelope pass, the unknown-label check, the snapshot header.
plugin_facts (#387) folds in every loaded plugin's effect_labels: FIRST, each under its own
owner, so that a project's effects.labels: can then name a plugin-opened root and an envelope may
bound rails.activejob.enqueue without the project having to re-declare the framework's vocabulary.
The plugin layer is not memoised on its own — PluginFacts is already per-process — and the
project layer keeps its memo keyed on the pair.
58 59 60 61 62 63 64 65 66 |
# File 'lib/rigor/effects/registry.rb', line 58 def self.for_configuration(configuration, plugin_facts: nil) base = plugin_facts.nil? || plugin_facts.empty? ? default : plugin_facts.extend_registry(default) labels = configuration.effects_labels return base if labels.nil? || labels.empty? (@extended ||= {})[[labels, base.labels]] ||= base.with(labels: labels, owner: nil) rescue Error default end |
.load_file(path) ⇒ Object
Build a registry from a registry-shaped YAML file. Missing or unreadable data degrades to an empty vocabulary rather than raising, matching the built-in catalogues' posture for a bare install that opted data out; every label then reads as unknown, which is fail-open.
71 72 73 74 75 76 77 78 79 |
# File 'lib/rigor/effects/registry.rb', line 71 def self.load_file(path) raw = File.exist?(path) ? YAML.safe_load_file(path) : nil raw = {} unless raw.is_a?(Hash) new( vocabulary_version: raw.fetch("vocabulary", 0), labels: raw.fetch("labels", nil) || [], retired: raw.fetch("retired", nil) || {} ) end |
Instance Method Details
#known?(label) ⇒ Boolean
Whether the vocabulary recognises label: an exact row, or an ancestor of one. A declared
io is recognised because io.net exists, so a bound may name an interior node the data
file never spells out on its own line.
102 103 104 |
# File 'lib/rigor/effects/registry.rb', line 102 def known?(label) @known.include?(label) end |
#retired(label) ⇒ Object
The replacement labels for a retired spelling, or nil when the spelling was never
retired. A rename or a removal bumps #vocabulary_version and records the old spelling
here so a snapshot written by an older Rigor still reads.
128 129 130 |
# File 'lib/rigor/effects/registry.rb', line 128 def retired(label) @retired[label] end |
#suggest(label) ⇒ Object
The nearest recognised label to a misspelling, within SUGGESTION_DISTANCE_CAP edits, or
nil when nothing is close enough. A recognised label suggests nothing — ask #known?
first.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/rigor/effects/registry.rb', line 109 def suggest(label) return nil unless Label.valid?(label) return nil if known?(label) best = nil best_distance = SUGGESTION_DISTANCE_CAP + 1 @known.each do |candidate| distance = levenshtein(label, candidate, best_distance) next unless distance < best_distance best = candidate best_distance = distance end best end |
#with(labels:, owner:) ⇒ Object
A new registry carrying labels on top of this one.
owner is the identity opening a root: a plugin id, or — for a first-party plugin that
models a framework — the framework root it owns. nil is the project, which may open any
root. Every added label must either descend from a root this registry already knows or
open a root equal to owner.
138 139 140 141 142 143 144 145 146 |
# File 'lib/rigor/effects/registry.rb', line 138 def with(labels:, owner:) added = labels.map(&:to_s) added.each do |label| raise InvalidLabelError, "not a well-formed effect label: #{label.inspect}" unless Label.valid?(label) check_ownership(label, owner) end self.class.new(vocabulary_version: @vocabulary_version, labels: @labels + added, retired: @retired) end |