Class: Hecks::Runtime::DependencyPlanning::Analyzer
- Inherits:
-
Object
- Object
- Hecks::Runtime::DependencyPlanning::Analyzer
- Defined in:
- lib/hecks/runtime/dependency_planning.rb
Constant Summary collapse
- STATEFUL_MUTATIONS =
%i[append increment decrement multiply clamp remove].freeze
Class Method Summary collapse
-
.call(aggregate:, command:, root_aggregate: aggregate) ⇒ Object
root_aggregate:— Wave 8's own audit surfaced a real bug here, not merely a missing feature: for an ENTITY-owned command,EntityInterpretercalls this withaggregate:set to the ENTITY itself (element_interpreter.rb's ownAnalyzer.call (aggregate: entity, command:)), soowner_fieldswas always the entity's own attribute set.
Instance Method Summary collapse
- #call ⇒ Object
-
#initialize(aggregate, command, root_aggregate = aggregate) ⇒ Analyzer
constructor
A new instance of Analyzer.
Constructor Details
#initialize(aggregate, command, root_aggregate = aggregate) ⇒ Analyzer
Returns a new instance of Analyzer.
81 82 83 84 85 86 87 88 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 |
# File 'lib/hecks/runtime/dependency_planning.rb', line 81 def initialize(aggregate, command, root_aggregate = aggregate) @aggregate = aggregate @command = command @owner_fields = aggregate.attributes.to_set(&:name) @owner_fields << aggregate.lifecycle.field.to_sym if aggregate.lifecycle @root_owner_fields = root_aggregate.attributes.to_set(&:name) @root_owner_fields << root_aggregate.lifecycle.field.to_sym if root_aggregate.lifecycle # `projects` FIELDS (S12, ADR 0025) ARE OWNER STATE TOO — a # `given`/`ensures` reading one (e.g. `customer_status == # "active"`) is reading this record's own stored field, same # as any attribute, even though nothing here WRITES it via a # declared mutation (`CommandInterpreter#seed_projected_fields` # populates it outside this analysis entirely). Left out of # `known_writes` deliberately: `add_preservation_reads` then # correctly treats it as a prior-state read that must survive # a partial mutation, which is exactly right — a projected # field's freshness comes from the interpreter reseeding it on # save, not from anything a caller-supplied write set carries. # Applies to BOTH `owner_fields` and `root_owner_fields` — an # entity's own `parent.*` read can name the root aggregate's # projected field just as easily as one of its real attributes # (`Banking::Withdrawal.Dispute`'s own `parent.account_customer_ # status`, ATMCard's projected field, is a real, live example). if aggregate.respond_to?(:projected_fields) aggregate.projected_fields.each { |field| @owner_fields << field.name } end if root_aggregate.respond_to?(:projected_fields) root_aggregate.projected_fields.each { |field| @root_owner_fields << field.name } end @payload_fields = command.attributes.to_set(&:name) @state_reads = Set.new @payload_reads = Set.new @writes = Set.new @known_writes = Set.new @unresolved = Set.new end |
Class Method Details
.call(aggregate:, command:, root_aggregate: aggregate) ⇒ Object
root_aggregate: — Wave 8's own audit surfaced a real bug here,
not merely a missing feature: for an ENTITY-owned command,
EntityInterpreter calls this with aggregate: set to the
ENTITY itself (element_interpreter.rb's own Analyzer.call (aggregate: entity, command:)), so owner_fields was always
the entity's own attribute set. A given/ensures reading
parent.X legitimately means the ROOT aggregate's own field —
a genuinely different owner — but classify_path's :parent
branch checked that read against owner_fields (the entity's),
which can never contain a root-level field, so every entity
command with a real, legitimate parent.* read was refused as
unresolved regardless of correctness. Defaults to aggregate
(a no-op) for the plain-aggregate case — CommandInterpreter's
own call site never needed to change.
79 |
# File 'lib/hecks/runtime/dependency_planning.rb', line 79 def self.call(aggregate:, command:, root_aggregate: aggregate) = new(aggregate, command, root_aggregate).call |
Instance Method Details
#call ⇒ Object
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/hecks/runtime/dependency_planning.rb', line 118 def call analyze_initial_state analyze_mutations analyze_lifecycle analyze_rules(command.givens, phase: :before) analyze_rules(command.ensures, phase: :after) analyze_rules(aggregate.invariants, phase: :after) add_preservation_reads complete = unresolved.empty? && owner_fields.subset?(known_writes) independent = complete && state_reads.empty? Plan.new( read_set: sorted(state_reads), write_set: sorted(writes), payload_read_set: sorted(payload_reads), complete_state: complete, state_independent: independent, unresolved_dependencies: unresolved.to_a.sort.freeze ).freeze end |