Module: Hecks::IR

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

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