Class: Hecks::Bluebook::DSL::ProcessManagerBuilder
- Inherits:
-
Object
- Object
- Hecks::Bluebook::DSL::ProcessManagerBuilder
- Includes:
- WordGate
- Defined in:
- lib/hecks/bluebook/dsl/process_manager_builder.rb
Defined Under Namespace
Classes: HandlerBuilder, InvalidProcessManager
Constant Summary collapse
- GRAMMAR_CONTEXT =
"ProcessManager"
Constants included from WordGate
Class Method Summary collapse
Instance Method Summary collapse
- #build ⇒ Object
-
#ends_on_impl(event_ref) ⇒ Object
ends_on— same reasoning asstarts_on_impl, above. -
#initialize(name) ⇒ ProcessManagerBuilder
constructor
A new instance of ProcessManagerBuilder.
-
#starts_on_impl(event_ref) ⇒ Object
starts_on Transfer::TransferRequested— BARE CONSTANT ACCEPTED (ADR 0025, S6 — "events first-class"), resolved throughConstShimthe same wayon_impl/transition_implresolve an event reference — but through `Naming. -
#transition_impl(mapping, &block) ⇒ Object
ONE STATE-MACHINE VOCABULARY (S7, ADR 0025 — "events and reactions"): the SAME word
Lifecycle#transitionalready carries, one level over — `transition "AccountDebited" => "awaiting_credit", from: "requested" do ...
Constructor Details
#initialize(name) ⇒ ProcessManagerBuilder
Returns a new instance of ProcessManagerBuilder.
12 13 14 15 |
# File 'lib/hecks/bluebook/dsl/process_manager_builder.rb', line 12 def initialize(name) @name = name @handlers = [] end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class Hecks::Bluebook::DSL::WordGate
Class Method Details
.build(name, &block) ⇒ Object
151 152 153 154 155 |
# File 'lib/hecks/bluebook/dsl/process_manager_builder.rb', line 151 def self.build(name, &block) builder = new(name) builder.instance_eval(&block) if block builder.build end |
Instance Method Details
#build ⇒ Object
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/hecks/bluebook/dsl/process_manager_builder.rb', line 138 def build validate! ProcessManager.new( name: @name, correlates_by: @correlates_by, starts_on: @starts_on, ends_on: @ends_on, states: derived_states, handlers: @handlers ) end |
#ends_on_impl(event_ref) ⇒ Object
ends_on — same reasoning as starts_on_impl, above.
32 33 34 |
# File 'lib/hecks/bluebook/dsl/process_manager_builder.rb', line 32 def ends_on_impl(event_ref) @ends_on = Naming.event_name_ref(event_ref) end |
#starts_on_impl(event_ref) ⇒ Object
starts_on Transfer::TransferRequested — BARE CONSTANT
ACCEPTED (ADR 0025, S6 — "events first-class"), resolved
through ConstShim the same way on_impl/transition_impl
resolve an event reference — but through Naming. event_name_ref, NOT Naming.event_ref (that method's own
header has the full account: SagaInterpreter matches
pm.starts_on/pm.ends_on against a BARE event.name, never
a "." qualified one). A plain String still passes through
unchanged, both for shadow_parse and for any corpus site a
future pass hasn't migrated yet.
27 28 29 |
# File 'lib/hecks/bluebook/dsl/process_manager_builder.rb', line 27 def starts_on_impl(event_ref) @starts_on = Naming.event_name_ref(event_ref) end |
#transition_impl(mapping, &block) ⇒ Object
ONE STATE-MACHINE VOCABULARY (S7, ADR 0025 — "events and
reactions"): the SAME word Lifecycle#transition already
carries, one level over — transition "AccountDebited" => "awaiting_credit", from: "requested" do ... end replaces on "AccountDebited", transition: { "requested" => "awaiting_ credit" } do ... end. Same bare rocket-pair argument shape
(not a NAMED transition: kwarg wrapping a second Hash), same
from: — including the array form Lifecycle's own commands
could already take and a process manager's own events could
not — and the states a procedure runs on are DERIVED from the
transitions that name them, the same way Behaviour::Lifecycle #states already derives an aggregate's ; state "x" lines
duplicated exactly what the transition list already said,
and could drift from it (validate!'s own "undeclared state"
check existed only because they could).
starts_on/ends_on are NOT unified into this — verified
against the real corpus rather than assumed: Settlement's own
ends_on "TransferSettled" names an event NONE of its own
transitions ever handle (Transfer.Settle's own emission, a
full step downstream of the transition that dispatches it),
so "the terminal state's own event" is not a fact the
transition graph carries — deriving it would either be wrong
for this exact corpus member or need a second new word to
cover the case, which is not less vocabulary than keeping the
one that already says it correctly.
EXPANDS IMMEDIATELY, unlike Lifecycle#transition (which
defers to Behaviour::Lifecycle#expand, called at emission
time) — ProcessManager's own IR constructor takes states:/
handlers: exactly as it always has, so the runtime
(Behaviour::ProcessManager, SagaInterpreter, saga
persistence/rehydration) needs no change at all: what changed
is how the DECLARATION reaches that same shape, not the shape
a real run ever sees or persists.
RENAMED FROM transition — item #13's full metaprogrammed
dispatch (slice 4c). Not bootstrap-reachable (checked
directly — no core/attached chapter declares a ProcessManager
of its own).
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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/hecks/bluebook/dsl/process_manager_builder.rb', line 89 def transition_impl(mapping, &block) mapping = mapping.dup from = mapping.delete(:from) # ALWAYS REQUIRED, unlike `Lifecycle#transition`'s own `from:` # — an aggregate's unconstrained transition is admitted from # ANY current state (`Behaviour::Lifecycle#applies_from?` # returns true for a nil `from`), a reading `SagaInterpreter# # advance_saga`'s own admission check does not share: it tests # `instance[:state] == handler.from_state` by plain equality, # nothing softer. Leaving that check unchanged (this slice's # own scope decision — see the class-level comment on why the # runtime stays untouched) means an unconstrained PM # transition would build cleanly and then match no instance # ever, silently — refused here instead, at the one point that # can still see the mistake. if from.nil? raise InvalidProcessManager, "#{@name}'s transition #{mapping.inspect} names no from: — a process manager's own " \ "admission checks a saga instance's CURRENT state exactly, so a transition with no " \ "from: would match no instance ever, silently" end handler = HandlerBuilder.new handler.instance_eval(&block) if block mapping.each do |event_type, target| # BARE CONSTANT ACCEPTED (ADR 0025, S6 — "events first- # class"), `transition Account::AccountDebited => "state"` — # `Naming.event_name_ref`, NOT the DOTTED `Naming.event_ref` # transform `PolicyBuilder#on_impl` uses (that method's own # header has the full account, found live wiring a real # migrated corpus site into `bin/model_check` for the first # time: `SagaInterpreter#advance_saga` matches `handler. # event_type` against a BARE `event.name`, never a "." # qualified one — a policy's own cross-aggregate match # works differently, splitting the qualifier apart from the # name rather than comparing the whole string). Writing the # qualifier is still worth it (the same provenance `trigger # Account::Debit` gives a reader) — `event_name_ref` keeps # only the final segment, so `Account::AccountDebited` and a # bare `AccountDebited` store identically. A plain String # still passes through unchanged, both for `shadow_parse` # and for every corpus site this pass didn't migrate. state_transition = StateTransition.new(target: target, from: from) (Naming.event_name_ref(event_type), state_transition, handler.dispatches).each { |row| @handlers << row } end end |