Class: Hecks::Bluebook::DSL::PortOperationBuilder

Inherits:
Object
  • Object
show all
Includes:
AttributeCollector, WordGate
Defined in:
lib/hecks/bluebook/dsl/port_operation_builder.rb

Constant Summary collapse

GRAMMAR_CONTEXT =
"PortOperation"

Constants included from WordGate

WordGate::NOT_ADMITTED

Class Method Summary collapse

Instance Method Summary collapse

Methods included from AttributeCollector

#attribute_impl, #attributes, #closed_sets, #list_of_impl, #one_of_impl

Constructor Details

#initialize(name, to: nil, owner: nil, direction: :inbound) ⇒ PortOperationBuilder

to: — THE SANCTIONED REPLACEMENT for reference_to inside an operation body, added here rather than left as a documented-but- unbuilt promise: reference_to_impl's own refusal message has told authors to "pass the receiving aggregate in to:" since #335, but no to: argument existed anywhere in DomainPort's own grammar (domain_port.bluebook) for operation/tells/asks to receive it — confirmed by grep across every lib/hecks/language file, not assumed.

GENUINE ROUTING METADATA, NOT AN ATTRIBUTE — matching rust/parser/src/parse/domain_port.rs's own header comment ("The receiving aggregate is routing metadata supplied by to:, not an operation attribute"), which anticipated this shape before either side actually built it. Stored separately (below, threaded to PortOperation as to:) rather than reusing reference_to_impl's own attribute-adding path — Dispatcher#port_invocation (lib/hecks/runtime/dispatcher.rb) is the ONE place that resolves routing at dispatch time; it gained a second, purely additive branch for this (falls back to a plain attribute NAMED for the owning aggregate's own identified_by field, the same "declare only external facts with attribute" the refusal message describes) rather than folding to: into identity_attribute's existing Reference-attribute scan, which every operation already in the corpus (Banking, pizzas, lifeadelics' own vendored PaymentGateway) still relies on unchanged.



36
37
38
39
40
41
42
# File 'lib/hecks/bluebook/dsl/port_operation_builder.rb', line 36

def initialize(name, to: nil, owner: nil, direction: :inbound)
  @name      = name
  @to        = to && Naming.demodulise(to)
  @owner     = owner
  @direction = direction
  @emits     = []
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, to: nil, owner: nil, direction: :inbound, &block) ⇒ Object



134
135
136
137
138
# File 'lib/hecks/bluebook/dsl/port_operation_builder.rb', line 134

def self.build(name, to: nil, owner: nil, direction: :inbound, &block)
  builder = new(name, to: to, owner: owner, direction: direction)
  builder.instance_eval(&block) if block
  builder.build
end

Instance Method Details

#buildObject

THE TWO HALVES OF AN asks. An outbound call has exactly two endings and the chapter names both — answers for what came back, refuses for what the outside said instead. They are separate words rather than two emits because a reader has to be able to tell them apart without reading the adapter: one is the thing you wanted, the other is the thing you have to handle. answers/refuses — item #13's full metaprogrammed dispatch, slice 1 (whole-project table-unification survey): both a bare, kind-driven coerce-and-assign with nothing else, now executed by GenericDispatch.

Raises:



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/hecks/bluebook/dsl/port_operation_builder.rb', line 84

def build
  outbound = @direction == :outbound
  refuse_wrong_words!(outbound)

  operation = PortOperation.new(
    name: @name, attributes: attributes, emits: @emits,
    direction: @direction, answers: @answers, refuses: @refuses, to: @to
  )

  # AN INBOUND OPERATION STILL HAS TO SAY SOMETHING. Only inbound: an
  # `asks` says it with `answers`/`refuses` instead, and
  # `refuse_wrong_words!` above has already insisted on both.
  raise Malformed,
        "#{@name} declares no emits — an operation with nothing to say " \
        "afterward is a call into nothing" if !outbound && @emits.empty?

  operation
end

#emits(event_name) ⇒ Object



71
# File 'lib/hecks/bluebook/dsl/port_operation_builder.rb', line 71

def emits(event_name) = @emits << event_name.to_s

#reference_to_impl(type, as: nil) ⇒ Object

ALWAYS an attribute, never the self-reference CommandBuilder spells — a port operation has no creates?/acts_on distinction to protect, so there is nothing for the self-reference branch to be FOR here. reference_to Payment, as: :payment_id reads the same even though the target happens to equal the owning aggregate. RENAMED FROM reference_to — item #13's full metaprogrammed dispatch (slice 4b). Bootstrap-reachable, in GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.

DISABLED, #335 — kept only for MetaValidator's own shadow-parsing pass (the self-hosted grammar's own KeywordSeed/ArgumentSeed rows for "reference_to" in this context are themselves declared using this construct, one level up — deleting the Ruby method would break the language describing itself, not just old domain authors). Every REAL domain author reaches to: instead, above — a genuinely different mechanism now, not a relocated spelling of this one (see to:'s own comment).



61
62
63
64
65
66
67
68
69
# File 'lib/hecks/bluebook/dsl/port_operation_builder.rb', line 61

def reference_to_impl(type, as: nil)
  unless MetaValidator.shadow_parsing?
    raise Malformed,
          "#{@name}.reference_to is behavioral routing, not retained domain state — " \
          "pass the receiving aggregate in to: and declare only external facts with attribute"
  end

  add_reference!(type, as: as)
end