Class: Hecks::Runtime::Registry
- Inherits:
-
Object
- Object
- Hecks::Runtime::Registry
- Includes:
- SagaPersistence, Verification
- Defined in:
- lib/hecks/runtime/registry.rb,
lib/hecks/runtime/registry/verification.rb,
lib/hecks/runtime/registry/saga_persistence.rb
Overview
The collections a boot gathers — bluebooks, hexagons, ports, adapters, worlds, the logs — and how a repository is resolved from them. The wiring gate lives in registry/verification.rb, saga persistence resolution in registry/saga_persistence.rb.
Defined Under Namespace
Modules: SagaPersistence, Verification
Instance Attribute Summary collapse
-
#adapters ⇒ Object
readonly
Returns the value of attribute adapters.
-
#bluebooks ⇒ Object
readonly
Returns the value of attribute bluebooks.
-
#event_log ⇒ Object
readonly
Returns the value of attribute event_log.
-
#hecksagons ⇒ Object
readonly
Returns the value of attribute hecksagons.
-
#policy_dispatch_log ⇒ Object
readonly
Returns the value of attribute policy_dispatch_log.
-
#ports ⇒ Object
readonly
Returns the value of attribute ports.
-
#reaction_log ⇒ Object
readonly
Returns the value of attribute reaction_log.
-
#root ⇒ Object
readonly
Returns the value of attribute root.
-
#saga_dispatch_log ⇒ Object
readonly
Returns the value of attribute saga_dispatch_log.
-
#saga_instances ⇒ Object
readonly
Returns the value of attribute saga_instances.
-
#saga_log ⇒ Object
readonly
Returns the value of attribute saga_log.
-
#saga_mutex ⇒ Object
readonly
Returns the value of attribute saga_mutex.
-
#translations ⇒ Object
readonly
Returns the value of attribute translations.
-
#worlds ⇒ Object
readonly
Returns the value of attribute worlds.
Instance Method Summary collapse
- #add_adapter(item) ⇒ Object
- #add_bluebook(item) ⇒ Object
-
#add_hecksagon(item) ⇒ Object
MERGED, NOT REPLACED — RECOVERED, not new (see Runtime::Loader .boot's own comment for the provenance).
- #add_port(item) ⇒ Object
- #add_translation(item) ⇒ Object
-
#add_world(item) ⇒ Object
MERGED, NOT REPLACED — the same generalization for
Worldthatadd_hecksagonabove recovers forHecksagon: anenvironments/<name>.worldoverlay (or a host-owned tenancy overlay world, same mechanism) can now add or override settings for a domain a base.worldfile already declared, without restating everything the base file said. - #bluebook(name) ⇒ Object
-
#bluebook_builder(name) ⇒ Object
THE BUILDER STAYS OPEN FOR THE LIFE OF THIS REGISTRY, keyed by chapter name — see the comment on
BluebookBuilder.build. - #capability_graph ⇒ Object
- #hecksagon(name) ⇒ Object
-
#initialize(root: nil) ⇒ Registry
constructor
A new instance of Registry.
-
#merge_hecksagons(a, b) ⇒ Object
RECOVERED — see
add_hecksagon's own comment for provenance. -
#merge_worlds(a, b) ⇒ Object
RECOVERED AND GENERALIZED — see
add_world's own comment. - #projection_current?(projection, authoritative) ⇒ Boolean
- #read_repository(domain, aggregate) ⇒ Object
- #repository(domain, aggregate) ⇒ Object
-
#reset_runtime_state! ⇒ Object
EVERYTHING A DISPATCH WROTE, CLEARED; NOTHING A BOOT DECLARED, TOUCHED.
-
#resolved_eras ⇒ Object
name => era ordinal as resolved by the boot-time era gate.
- #verbs ⇒ Object
- #world(name) ⇒ Object
Methods included from SagaPersistence
#rehydrate_sagas!, #saga_persistence
Methods included from Verification
#adapter_class, #check_settings, #check_verb, #port_for, #verify!, #verify_default_adapter!
Constructor Details
#initialize(root: nil) ⇒ Registry
Returns a new instance of Registry.
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/hecks/runtime/registry.rb', line 20 def initialize(root: nil) @root = root @bluebooks = {} @hecksagons = {} @ports = {} @adapters = {} @worlds = {} @translations = [] @event_log = [] @reaction_log = [] @saga_log = [] # ADDITIVE, RUBY-ONLY — never merged into saga_log/reaction_log. # rust/src/kernel/orchestrate.rs ports THOSE two arrays' exact # shape byte-for-byte (spec/rust_conformance_spec.rb's own # equality check) — a landmine found by reading that spec before # touching anything, not by hitting it. These carry the raw # inputs a dispatch's own argument binding was resolved from # (SagaInterpreter#deliver_saga_dispatch / PolicyInterpreter# # trigger_args), for Properties.dispatch_binding_fidelity's own # independent re-derivation — a fact neither existing log # records at all, so there is nothing here for Rust to have # matched or drifted from. @saga_dispatch_log = [] @policy_dispatch_log = [] @saga_instances = Hash.new { |h, k| h[k] = {} } # GUARDS `saga_instances`' OWN mutation+checkpoint sequence # (`SagaInterpreter`'s 4 write points, §7) — the same shape of # hazard this codebase's own prior audit already flagged for # `@reaction_depth`, a thread-shared dispatcher ivar with no # lock, made meaningfully easier to hit once a persistence write # sits in the same critical section. Held across the in-memory # mutation AND the checkpoint write together, never across a # saga's own dispatch cascade — see `SagaInterpreter#advance_saga`'s # own comment for why that distinction matters (non-reentrant # Mutex, recursive re-entry is real). @saga_mutex = Mutex.new @repositories = {} @projection_repositories = {} @bluebook_builders = {} end |
Instance Attribute Details
#adapters ⇒ Object (readonly)
Returns the value of attribute adapters.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def adapters @adapters end |
#bluebooks ⇒ Object (readonly)
Returns the value of attribute bluebooks.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def bluebooks @bluebooks end |
#event_log ⇒ Object (readonly)
Returns the value of attribute event_log.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def event_log @event_log end |
#hecksagons ⇒ Object (readonly)
Returns the value of attribute hecksagons.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def hecksagons @hecksagons end |
#policy_dispatch_log ⇒ Object (readonly)
Returns the value of attribute policy_dispatch_log.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def policy_dispatch_log @policy_dispatch_log end |
#ports ⇒ Object (readonly)
Returns the value of attribute ports.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def ports @ports end |
#reaction_log ⇒ Object (readonly)
Returns the value of attribute reaction_log.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def reaction_log @reaction_log end |
#root ⇒ Object (readonly)
Returns the value of attribute root.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def root @root end |
#saga_dispatch_log ⇒ Object (readonly)
Returns the value of attribute saga_dispatch_log.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def saga_dispatch_log @saga_dispatch_log end |
#saga_instances ⇒ Object (readonly)
Returns the value of attribute saga_instances.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def saga_instances @saga_instances end |
#saga_log ⇒ Object (readonly)
Returns the value of attribute saga_log.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def saga_log @saga_log end |
#saga_mutex ⇒ Object (readonly)
Returns the value of attribute saga_mutex.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def saga_mutex @saga_mutex end |
#translations ⇒ Object (readonly)
Returns the value of attribute translations.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def translations @translations end |
#worlds ⇒ Object (readonly)
Returns the value of attribute worlds.
16 17 18 |
# File 'lib/hecks/runtime/registry.rb', line 16 def worlds @worlds end |
Instance Method Details
#add_adapter(item) ⇒ Object
84 |
# File 'lib/hecks/runtime/registry.rb', line 84 def add_adapter(item) = @adapters[item.name] = item |
#add_bluebook(item) ⇒ Object
70 |
# File 'lib/hecks/runtime/registry.rb', line 70 def add_bluebook(item) = @bluebooks[item.name] = item |
#add_hecksagon(item) ⇒ Object
MERGED, NOT REPLACED — RECOVERED, not new (see Runtime::Loader
.boot's own comment for the provenance). A domain's hecksagon can
now load in more than one block for the same domain (base file
plus an environments/<name>.hecksagon overlay), and the second
block should ADD to what the first declared, not silently
discard it.
78 79 80 81 |
# File 'lib/hecks/runtime/registry.rb', line 78 def add_hecksagon(item) existing = @hecksagons[item.domain] @hecksagons[item.domain] = existing ? merge_hecksagons(existing, item) : item end |
#add_port(item) ⇒ Object
83 |
# File 'lib/hecks/runtime/registry.rb', line 83 def add_port(item) = @ports[item.name] = item |
#add_translation(item) ⇒ Object
101 |
# File 'lib/hecks/runtime/registry.rb', line 101 def add_translation(item) = @translations << item |
#add_world(item) ⇒ Object
MERGED, NOT REPLACED — the same generalization for World that
add_hecksagon above recovers for Hecksagon: an
environments/<name>.world overlay (or a host-owned tenancy
overlay world, same mechanism) can now add or override settings
for a domain a base .world file already declared, without
restating everything the base file said. Settings merge shallow,
keyed exactly the way WorldBuilder already stores them (both the
bare verb key and the "verb:adapter" qualified key point at the
same resolved hash) — an overlay's key wins over the base's same
key; a key only the base declares survives untouched.
96 97 98 99 |
# File 'lib/hecks/runtime/registry.rb', line 96 def add_world(item) existing = @worlds[item.domain] @worlds[item.domain] = existing ? merge_worlds(existing, item) : item end |
#bluebook(name) ⇒ Object
109 |
# File 'lib/hecks/runtime/registry.rb', line 109 def bluebook(name) = @bluebooks[name.to_s] |
#bluebook_builder(name) ⇒ Object
THE BUILDER STAYS OPEN FOR THE LIFE OF THIS REGISTRY, keyed by chapter
name — see the comment on BluebookBuilder.build. A chapter split across
several files (language/bluebook/*.bluebook, all Hecks.bluebook "Bluebook")
needs its declarations to accumulate into ONE builder rather than each
file minting its own and silently discarding the one before.
66 67 68 |
# File 'lib/hecks/runtime/registry.rb', line 66 def bluebook_builder(name) @bluebook_builders[name.to_s] ||= yield end |
#capability_graph ⇒ Object
149 150 151 |
# File 'lib/hecks/runtime/registry.rb', line 149 def capability_graph @capability_graph ||= CapabilityGraph.new(self) end |
#hecksagon(name) ⇒ Object
110 |
# File 'lib/hecks/runtime/registry.rb', line 110 def hecksagon(name) = @hecksagons[name.to_s] |
#merge_hecksagons(a, b) ⇒ Object
RECOVERED — see add_hecksagon's own comment for provenance.
Concatenates every list-shaped fact; binds in particular is
additive because an overlay REBINDING an aggregate (a new
persisted_by for the same aggregate/verb) is meant to shadow
the base's own bind at resolution time, not erase it outright —
Ports::Persistence::BindingPolicy.resolve's own "exactly one
authoritative bind" check is what actually catches a genuine
double-bind; this merge only concatenates, it does not itself
decide which of two binds for the same aggregate wins.
191 192 193 194 195 196 197 198 199 |
# File 'lib/hecks/runtime/registry.rb', line 191 def merge_hecksagons(a, b) Bluebook::Hecksagon.new( domain: a.domain, binds: a.binds + b.binds, subscriptions: a.subscriptions + b.subscriptions, framework_members: a.framework_members + b.framework_members, vendored_bluebooks: a.vendored_bluebooks + b.vendored_bluebooks ) end |
#merge_worlds(a, b) ⇒ Object
RECOVERED AND GENERALIZED — see add_world's own comment. realm/
latest are scalars, so the overlay's value wins when present,
else the base's survives; settings is a shallow merge keyed by
verb (and "verb:adapter") — an overlay entry for a key the base
also declares REPLACES that key's whole resolved hash (the same
all-or-nothing shape WorldBuilder#method_missing already builds
each entry as), it does not deep-merge field by field within it.
208 209 210 211 212 213 214 215 |
# File 'lib/hecks/runtime/registry.rb', line 208 def merge_worlds(a, b) Bluebook::World.new( domain: a.domain, realm: b.realm || a.realm, latest: b.latest || a.latest, settings: a.settings.merge(b.settings) ) end |
#projection_current?(projection, authoritative) ⇒ Boolean
167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/hecks/runtime/registry.rb', line 167 def projection_current?(projection, ) projected_entries = projection.entries source_entries = .entries return false unless projected_entries.length == source_entries.length return false unless projected_entries.zip(source_entries).all? do |projected, source| projected.operation == source.operation && projected.id == source.id && projected.state == source.state end projected_rows = projection.all.map(&:to_h).sort_by { |row| row.fetch(:id).to_s } source_rows = .all.map(&:to_h).sort_by { |row| row.fetch(:id).to_s } projected_rows == source_rows rescue StandardError false end |
#read_repository(domain, aggregate) ⇒ Object
153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/hecks/runtime/registry.rb', line 153 def read_repository(domain, aggregate) key = [domain.to_s, aggregate.hecks_name] binding = Ports::Projection.binds_for(self, domain, aggregate).first return repository(domain, aggregate) unless binding projection = (@projection_repositories[key] ||= begin projection = Ports::Persistence::RepositoryFactory.build(self, domain, aggregate, binding, recover: true, settings_verb: Ports::Projection::VERB) projection end) = repository(domain, aggregate) projection_current?(projection, ) ? projection : end |
#repository(domain, aggregate) ⇒ Object
115 116 117 |
# File 'lib/hecks/runtime/registry.rb', line 115 def repository(domain, aggregate) @repositories[[domain.to_s, aggregate.hecks_name]] ||= Ports::Persistence.repository(self, domain, aggregate) end |
#reset_runtime_state! ⇒ Object
EVERYTHING A DISPATCH WROTE, CLEARED; NOTHING A BOOT DECLARED,
TOUCHED. Bluebooks, hecksagons, ports, adapters, worlds and the
resolved eras are what loading the files produced and stay as
they are; the logs, the saga instances and the repositories are
what running commands against them produced, and go back to
exactly what a fresh boot of the same files hands out. Dropping
the repositories (rather than emptying each) is deliberate: a
fresh boot's own repositories are new adapter instances too, so
a Memory adapter starts empty and a durable one sees whatever
it persisted — the same reading either way. Sagas rehydrate off
that store again, the way Loader.boot_files does after
verify!.
What this is for: a test runner that used to boot a runtime per
test to get isolation (Behaviors::Expectations.run_one) — ~2s a
boot, 76 chess behaviours = two and a half minutes of booting the
same two files — can now boot once and reset between tests.
136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/hecks/runtime/registry.rb', line 136 def reset_runtime_state! @event_log.clear @reaction_log.clear @saga_log.clear @saga_dispatch_log.clear @policy_dispatch_log.clear @saga_instances.clear @repositories = {} @projection_repositories = {} rehydrate_sagas! self end |
#resolved_eras ⇒ Object
name => era ordinal as resolved by the boot-time era gate. A lineage adapter writes into ITS OWN era's partition — which, for an old checkout booting a held-but-superseded shape, is not the newest one.
107 |
# File 'lib/hecks/runtime/registry.rb', line 107 def resolved_eras = @resolved_eras ||= {} |
#verbs ⇒ Object
113 |
# File 'lib/hecks/runtime/registry.rb', line 113 def verbs = @bluebooks.values.flat_map(&:verbs).sort |
#world(name) ⇒ Object
111 |
# File 'lib/hecks/runtime/registry.rb', line 111 def world(name) = @worlds[name.to_s] |