Module: Hecks::IR
- Included in:
- Bluebook::Aggregate, Bluebook::Attribute, Bluebook::Chapter, Bluebook::Command, Bluebook::DispatchSpec, Bluebook::DomainPort, Bluebook::Entity, Bluebook::Hecksagon, Bluebook::Lifecycle, Bluebook::Mutation, Bluebook::Policy, Bluebook::PortOperation, Bluebook::ProcessManager, Bluebook::ProcessManagerHandler, Bluebook::Query, Bluebook::ReadModel, Bluebook::ValueObject, Bluebook::World
- Defined in:
- lib/hecks/ir.rb
Overview
WHAT A CONSTRUCT EMITS, DECLARED RATHER THAN WRITTEN OUT.
IR is a thing this framework PRODUCES, not a thing its model IS.
The language self-hosts, and its own bluebook declares aggregates
named Bluebook, Aggregate, Command, Entity, ValueObject,
Policy, ReadModel — there is no IR aggregate anywhere in the
grammar. Where the grammar's own vision line does say IR it means the
emission: "the IR it STORES must equal the IR the DSL builder
PRODUCES". IR_VERSION says the same thing structurally — a version
stamped on to_h's output rather than on the object is a version of
the EMISSION, which only makes sense if the two are different things.
So emitting IR is a CAPABILITY a construct has, and this is that
capability: include Hecks::IR and declare the shape once.
The model it emits FROM is Hecks::Bluebook — a chapter class
nesting everything a chapter declares. It is deliberately not named
after this, its own output.
Before this, eighteen constructs each hand-wrote a to_h that said
the same four things in the same order — read a field, recurse into a
child, recurse into a list, or compute something — and the shape of a
construct was knowable only by reading a method body. Declared, it is
data: ir_spec can be walked by anything that wants to know what a
construct carries, which is the whole point of hanging emission off
the model rather than burying it.
include Hecks::IR # an instance-shaped construct
emits_ir(
name: :name, # send it
identified_by: :identity_paths, # ...under a different key
list: :list?, # ...predicates are fine
attributes: many(:attributes), # map(&:to_h)
lifecycle: one(:lifecycle), # &.to_h, nil-safe
canonical_form: -> { CanonicalForm.table } # anything else
)
KEY ORDER IS THE DECLARATION ORDER, and that is load-bearing rather
than cosmetic: spec/golden/ir/*.json pins the emitted form exactly,
so a reordered declaration is a changed artifact and the golden specs
will say so.
Defined Under Namespace
Modules: Declares, Emits Classes: Many, One, Undeclared
Class Method Summary collapse
- .extended(base) ⇒ Object
-
.included(base) ⇒ Object
THE TWO SHAPES A CONSTRUCT COMES IN, and why this module has two doors instead of hiding the difference.
Class Method Details
.extended(base) ⇒ Object
65 66 67 68 |
# File 'lib/hecks/ir.rb', line 65 def self.extended(base) base.extend(Declares) base.extend(Emits) end |
.included(base) ⇒ Object
THE TWO SHAPES A CONSTRUCT COMES IN, and why this module has two doors instead of hiding the difference.
Bluebook/Aggregate/Policy/ReadModel are ordinary objects —
metadata records, one instance per declaration. Command/Entity/
ValueObject are anonymous CLASSES (Class.new(self), see
Command.declare), because those three are referenced as TYPES in
a bluebook (attribute :price, Money) and a type has to be a real
Ruby constant to be named.
That split is real and not worth papering over, so:
include Hecks::IR # instance-shaped — to_h is an instance method
extend Hecks::IR # class-shaped — to_h is a class method
Both get the same emits_ir and the same emission rules.
60 61 62 63 |
# File 'lib/hecks/ir.rb', line 60 def self.included(base) base.extend(Declares) base.include(Emits) end |