Module: Hecks::Bluebook::Behaviour::Command
Overview
WHAT A COMMAND DOES. EXTENDED, not included — a command is a CLASS, one per declared verb.
Instance Method Summary collapse
-
#acts_on ⇒ Object
The construct this verb acts upon — the construct itself, not its name.
-
#addressing_key_for(aggregate_name) ⇒ Object
THE ARGUMENT NAME THAT ADDRESSES an instance of
aggregate_namefor THIS command — the one factPolicyInterpreter's ownfor_eachfan-out needs and, until this reading existed, had to guess at (see git blame:Behaviour::Policy #fan_out_reference_key, which hardcoded<aggregate>_idunconditionally and refused every dispatch to a self- addressing command as a result —Account.Freeze, addressed bynumber/account, notaccount_id). - #creates? ⇒ Boolean
-
#guard_descriptions ⇒ Object
EVERY REASON THIS VERB CAN REFUSE ON A RULE — the descriptions of its givens AND its ensures, the exact text the runtime quotes after "refused — " when a guard is not met (see command_rules/admissibility.rb's GivenNotMet/EnsuresNotMet).
-
#settle ⇒ Object
Indexed once — attributes are final once absorbed, and every dispatch asks this finder by name.
Methods included from Indexed
#attribute, #command, #index_attributes, #index_by_hecks_name, #query
Instance Method Details
#acts_on ⇒ Object
The construct this verb acts upon — the construct itself, not its name.
A verb declared on an ENTITY always acts on that piece. It never
self-references, because an element is addressed THROUGH its parent —
which means creates? answers true for every one of them, and reading
acts_on off creates? alone would report that LedgerEntry.Amend
brings a ledger entry into being. Three of banking's commands were
about to say exactly that, and nothing would have contradicted them.
On an aggregate, a creating command acts on no existing root, so nil is the truth: there is nothing there yet.
29 30 31 32 33 34 35 36 37 38 |
# File 'lib/hecks/bluebook/behaviour/command.rb', line 29 def acts_on # FULLY QUALIFIED, and it has to be: inside `module Behaviour` # the bare name `Entity` resolves to Behaviour::Entity — this # module's SIBLING — not to the construct. Comparing a Class to # a Module answers nil, so the guard silently fell through and # every entity verb reported acting on nothing. return hecks_owner if hecks_owner.is_a?(Class) && hecks_owner < Bluebook::Entity creates? ? nil : hecks_owner end |
#addressing_key_for(aggregate_name) ⇒ Object
THE ARGUMENT NAME THAT ADDRESSES an instance of aggregate_name
for THIS command — the one fact PolicyInterpreter's own
for_each fan-out needs and, until this reading existed, had
to guess at (see git blame: Behaviour::Policy #fan_out_reference_key, which hardcoded <aggregate>_id
unconditionally and refused every dispatch to a self-
addressing command as a result — Account.Freeze, addressed
by number/account, not account_id).
TWO SHAPES, the same two CommandBuilder#reference_to already
tells apart at declare time (command_builder.rb's own comment
on cross_reference — "as: MEANS a named attribute... a
command can point at another instance of its OWN kind"):
SELF-ADDRESSING — `references == aggregate_name` (this verb
is declared ON the very aggregate it acts on, `reference_to
Account` on a command Account itself owns). No attribute was
minted for it at all; the SAME bare key
`CommandInterpreter::ArgumentGate#reference_key` already
accepts as "addressing, not describing" is reused here
rather than re-derived — one door, not two.
CROSS-REFERENCING — a real, declared reference-typed
attribute whose OWN target is `aggregate_name` (`customer_id`
on `Account.Open`, or whatever `as:` named it). Its NAME is
the key, exactly as declared — never re-derived from the
target's name, because an `as:` reference's name and its
target's snake case can legitimately differ (`Transfer`'s own
`source`/`destination`, both `Reference<Account>`).
nil when neither shape matches — a CREATING command (nothing
to address yet) or one that simply never references this
aggregate at all. A caller minting a fan-out dispatch is
expected to treat nil as "this command cannot be addressed by
a row of this aggregate," not to fall back on a guess.
87 88 89 90 91 92 |
# File 'lib/hecks/bluebook/behaviour/command.rb', line 87 def addressing_key_for(aggregate_name) return Naming.reference_key(aggregate_name) if references.to_s == aggregate_name.to_s attributes.find { |attribute| attribute.reference? && attribute.type.target_name.to_s == aggregate_name.to_s } &.name end |
#creates? ⇒ Boolean
40 |
# File 'lib/hecks/bluebook/behaviour/command.rb', line 40 def creates? = @references.nil? |
#guard_descriptions ⇒ Object
EVERY REASON THIS VERB CAN REFUSE ON A RULE — the descriptions of
its givens AND its ensures, the exact text the runtime quotes
after "refused — " when a guard is not met (see
command_rules/admissibility.rb's GivenNotMet/EnsuresNotMet). A
property that asks "did the runtime only ever refuse for a rule
the language wrote" reads this rather than re-deriving the two
collections; compact because a rule's description is optional
(behavior.bluebook's Rule) and an unnamed one quotes nothing.
50 |
# File 'lib/hecks/bluebook/behaviour/command.rb', line 50 def guard_descriptions = (@givens + @ensures).map(&:description).compact |
#settle ⇒ Object
Indexed once — attributes are final once absorbed, and every dispatch asks this finder by name.
13 14 15 16 |
# File 'lib/hecks/bluebook/behaviour/command.rb', line 13 def settle index_attributes(@attributes) self end |