Class: Hecks::Bluebook::DSL::HecksagonBuilder

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

Constant Summary collapse

GRAMMAR_CONTEXT =
"Hecksagon"

Constants included from WordGate

WordGate::NOT_ADMITTED

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(domain) ⇒ HecksagonBuilder

Returns a new instance of HecksagonBuilder.



16
17
18
19
20
21
22
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 16

def initialize(domain)
  @domain             = domain
  @binds              = []
  @subscriptions      = []
  @framework_members  = []
  @vendored_bluebooks = []
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(verb, *args, **kwargs, &block) ⇒ Object

DOMAIN-LEVEL DEFAULT BINDS — persisted_by "Heki" bare, at the top of a hecksagon block, applies to every aggregate in this domain that doesn't declare its own override. Mirrors BindingProxy's own method_missing one level down (aggregate: filled in there, nil here) — generic over verb name, not hardcoded to persisted_by/projected_by specifically, so any future verb gets a domain-level default for free too. See Hecksagon#bind_for for the fallback lookup this feeds.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 126

def method_missing(verb, *args, **kwargs, &block)
  # A closed-set grammar word (`port`, today) gets first refusal
  # — item #13's full metaprogrammed dispatch (slice 5); see
  # `WordGate#word_gate_dispatch`'s own header for why this class
  # needs to call it explicitly rather than including it the
  # ordinary way. Only once THAT says "not admitted" does the
  # genuinely open-ended `persisted_by "Heki"`-style bind
  # vocabulary below get a turn.
  result = word_gate_dispatch(verb, args, kwargs, block)
  return result unless result.equal?(WordGate::NOT_ADMITTED)

  return super unless args.first

  @binds << Bind.new(aggregate: nil, verb: verb.to_s, adapter: args.first.to_s, role: kwargs[:role]&.to_s)
  block&.call
  self
end

Class Attribute Details

.collectorObject

Returns the value of attribute collector.



11
12
13
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 11

def collector
  @collector
end

Instance Attribute Details

#bindsObject (readonly)

Returns the value of attribute binds.



14
15
16
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 14

def binds
  @binds
end

#framework_membersObject (readonly)

Returns the value of attribute framework_members.



14
15
16
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 14

def framework_members
  @framework_members
end

#subscriptionsObject (readonly)

Returns the value of attribute subscriptions.



14
15
16
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 14

def subscriptions
  @subscriptions
end

#vendored_bluebooksObject (readonly)

Returns the value of attribute vendored_bluebooks.



14
15
16
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 14

def vendored_bluebooks
  @vendored_bluebooks
end

Class Method Details

.build(domain, &block) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 146

def self.build(domain, &block)
  builder  = new(domain)
  resolver = ->(name) { BindingProxy.namespace(name, builder.binds) }

  previous       = collector
  self.collector = builder.binds
  begin
    ConstShim.with(resolver) { builder.instance_eval(&block) } if block
  ensure
    self.collector = previous
  end

  builder.build
end

Instance Method Details

#buildObject

NO ungoverned-role check here anymore — see Registry::Verification#refuse_ungoverned_roles!. Moved out of per-block build, recovered alongside environment: (Runtime::Loader.boot's comment has the provenance): a domain split across multiple hecksagon blocks (base + an environments/<name>.hecksagon overlay) would have every block but the one declaring uses_framework "Governance" refused HERE, even though Registry#add_hecksagon merges them into one Hecksagon before anything ever dispatches against it. Checking the MERGED result once, at verify! time — after every file for this domain has loaded — is both more permissive (no need to repeat uses_framework in every file) and strictly more correct (a check against an incomplete, not-yet-merged hecksagon can never see the real final shape).



113
114
115
116
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 113

def build
  Hecksagon.new(domain: @domain, binds: @binds, subscriptions: @subscriptions,
                framework_members: @framework_members, vendored_bluebooks: @vendored_bluebooks)
end

#port_impl(name, &block) ⇒ Object

THE PRIMARY PORT, BARE AT THE ROOT — belongs to the CHAPTER as a whole, not one aggregate. BindingProxy#port is the aggregate- scoped sibling (Payments::Payment.port("Gateway") do ... end); this is what's left when a port isn't about any one record. The bluebook must already be built and registered, since a hecksagon loads after its bluebook, and this attaches to that real, final object directly rather than building a second copy MetaValidator would have to know how to reconstruct. RENAMED FROM port — item #13's full metaprogrammed dispatch (slice 5). Not bootstrap-reachable (checked directly — no core/attached chapter declares a Hecksagon of its own). Reached through WordGate#method_missing's new word_gate_dispatch, called explicitly below since HecksagonBuilder's own class-level method_missing (the open-verb catch-all beneath this) always wins over the module's — see word_gate.rb's own header for the full mechanism.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 77

def port_impl(name, &block)
  bluebook_ir = Hecks.current_registry.bluebook(@domain) or
    raise Malformed, "#{@domain} declares no such bluebook — a port needs one to belong to"

  # See BindingProxy#port's own comment on why this resolver swap is
  # needed — ConstShim's active resolver is one global for the whole
  # dynamic extent, currently this file's own BindingProxy-minting
  # one, which would turn a bare constant inside an operation's
  # `reference_to`/`attribute` into another BindingProxy instead of
  # a name.
  built = ConstShim.with(->(const) { const }) { DomainPortBuilder.build(name, &block) }

  # See BindingProxy#port's own comment on the same branch — a
  # `verb`-shaped port is a plain `Port`, registered the same
  # way `Hecks.port`'s top-level method already does, not attached
  # to this bluebook's own IR the way an operations-shaped
  # `DomainPort` is.
  return Hecks.current_registry.add_port(built) if built.is_a?(Port)

  bluebook_ir.add_port(built)
end

#respond_to_missing?(_name, _include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


144
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 144

def respond_to_missing?(_name, _include_private = false) = true

#subscribe(event) ⇒ Object

An event this hecksagon takes from OUTSIDE the domain's own bluebook.



26
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 26

def subscribe(event) = @subscriptions << event.to_s

#uses_embryonaut_bluebook(name) ⇒ Object

A VENDORED, EXTERNAL bluebook this domain wants attached — same wiring-decision shape uses_framework already is, one level further out: not a member shipped inside hecks's own lib/, but a separate package (embryonaut_bluebooks) vendored into THIS project's own checkout. See EmbryonautBluebook's own header for the full reasoning on why its ROOT can't be a fixed constant the way Framework::ROOT is, and for the recovery provenance.

RECORDED ONTO @vendored_bluebooks, same shape uses_framework already gives @framework_members — a SEPARATE list on purpose: framework_members is load-bearing for refuse_ungoverned_roles! (below) and for Governance's own attachment check; conflating the two would make a vendored bluebook attachment satisfy a Governance check it has nothing to do with.



56
57
58
59
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 56

def uses_embryonaut_bluebook(name)
  @vendored_bluebooks << name.to_s
  Hecks::EmbryonautBluebook.load!(name)
end

#uses_framework(name) ⇒ Object

A framework/ member this domain wants attached — Governance, Identity, whatever else lands beside them. Attaching one is a WIRING decision, the same kind persisted_by/ projected_by already are, so it lives here rather than as a fact stated in the domain's own bluebook. Recorded onto THIS hecksagon, the same way subscribe records onto its own subscriptions — and loads the member's bluebook then its own hecksagon into whatever registry this one is loading into, see Framework.load!.



37
38
39
40
# File 'lib/hecks/bluebook/dsl/hecksagon_builder.rb', line 37

def uses_framework(name)
  @framework_members << name.to_s
  Hecks::Framework.load!(name)
end