Module: Hecks::Bluebook::Behaviour::Command

Includes:
Indexed
Included in:
Command
Defined in:
lib/hecks/bluebook/behaviour/command.rb

Overview

WHAT A COMMAND DOES. EXTENDED, not included — a command is a CLASS, one per declared verb.

Instance Method Summary collapse

Methods included from Indexed

#attribute, #command, #index_attributes, #index_by_hecks_name, #query

Instance Method Details

#acts_onObject

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

Returns:

  • (Boolean)


40
# File 'lib/hecks/bluebook/behaviour/command.rb', line 40

def creates? = @references.nil?

#guard_descriptionsObject

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

#settleObject

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