Module: Hecks::Bluebook::DSL::AttributeCollector

Included in:
AggregateBuilder, CommandBuilder, EntityBuilder, PortOperationBuilder, QueryBuilder, ValueObjectBuilder
Defined in:
lib/hecks/bluebook/dsl/attribute_collector.rb

Defined Under Namespace

Classes: ListOf, OneOf

Instance Method Summary collapse

Instance Method Details

#attribute_impl(name, type = UNSET, default: nil, optional: false, pattern: nil, admits: nil, one_of: nil) ⇒ Object

admits: names a closed set that is ALREADY DECLARED elsewhere —

attribute :op, String, admits: "Vocabulary::QueryComparator"

which is the difference between it and one_of: one_of SYNTHESISES a fresh value object named for the attribute, so it can only ever name something new. admits points at a set the language already holds, so the same set can be named from many places without being written twice.

QUALIFIED, because a closed set is a value object INSIDE an aggregate and reference_to reaches heads only — so this is text, checked where it is read rather than by reference resolution.

AND WRITTEN AS TEXT, not as the constant path Vocabulary::QueryComparator it reads like. The constant spelling was tried — ConstShim returning a Module so Ruby's :: reaches a second const_missing — and it cannot hold: Facade::Surface installs every aggregate name as a TOP-LEVEL constant, so the moment any facade exists, Vocabulary resolves to that module and the shim is never asked. A spelling that works only until a facade is built is worse than a quoted one.

THE TYPE POSITION TAKES A BARE CONSTANT, ALWAYS REQUIRED (ADR 0025, "Attributes") — omitting it (a mint default of String) and quoting it as text both refuse now. Neither had a real reason left: no corpus attribute ever omitted the type, and ConstShim#const_missing (S0b) already resolves a bare, not-yet-declared constant to the SAME forward reference the quoted form existed for — a bareword Name reaches a value object named "Name" declared later in the same block exactly as "Name" used to, spell's own to_s renders either one identically. Neither form appears in any frozen era text (checked directly), so both are refused unconditionally — nothing for MetaValidator.shadow_parsing? to answer for. RENAMED FROM attribute — item #13's full metaprogrammed dispatch (slice 3, whole-project table-unification survey). The word attribute itself is no longer a real method any builder answers directly: every (context, word) Keyword row for it carries calls: "attribute_impl", and GenericDispatch forwards the whole call here untouched — this method's own body is exactly what attribute always was, unchanged, just reached generically now rather than by Ruby's own direct method lookup. attribute_collector_spec.rb (AttributeCollector has no method without a test — dsl_coverage_spec.rb) and the bootstrap fallback (GenericDispatch::BOOTSTRAP_CALLS_FALLBACK) both name this same string; they must never drift apart.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/hecks/bluebook/dsl/attribute_collector.rb', line 61

def attribute_impl(name, type = UNSET, default: nil, optional: false, pattern: nil,
                   admits: nil, one_of: nil)
  # moved to the language: FieldName invariant, on Root.Attribute

  if type.equal?(UNSET)
    raise Malformed, "#{name} declares no type — attribute :#{name}, SomeType is required, " \
                     "there is no default"
  end

  # `list_of("X")` carries the same quoted text one level down — a
  # bare constant is required there too, checked before it unwraps
  # below, not after (once unwrapped, a plain String in the `type:`
  # slot looks identical to one that legitimately belongs there).
  quoted = type.is_a?(ListOf) ? type.type : type
  if quoted.is_a?(::String)
    raise Malformed, "#{name}'s type #{quoted.inspect} is quoted text — give the bare constant " \
                     "(#{quoted}) instead; a forward reference to a value object declared later " \
                     "in the same block already resolves without quoting"
  end

  refuse_unshared_pattern(name, pattern) if pattern

  type = synthesise_closed_set(name, type) if type.is_a?(OneOf)
  list = type.is_a?(ListOf)
  attributes << Attribute.new(
    name:     name,
    type:     list ? type.type : type,
    list:     list,
    default:  default,
    optional: optional,
    pattern:  pattern,
    admits:   admits
  )

  install_inline_closed_set(name, one_of) if one_of
end

#attributesObject



11
# File 'lib/hecks/bluebook/dsl/attribute_collector.rb', line 11

def attributes = @attributes ||= []

#closed_setsObject

Value objects synthesised from inline closed sets, collected here and installed by whoever owns value objects (the aggregate).



15
# File 'lib/hecks/bluebook/dsl/attribute_collector.rb', line 15

def closed_sets = @closed_sets ||= []

#list_of_impl(type) ⇒ Object

RENAMED FROM list_of — item #13's full metaprogrammed dispatch (slice 5). Called in an attribute's own TYPE position (attribute :x, list_of(Y)), never through a def list_of any ONE builder answers as its own word — reached via WordGate#word_gate_dispatch's new "Type"-context fallback, the same one one_of_impl below uses. Bootstrap- reachable (every core chapter's own list-typed attributes use it), so GenericDispatch::BOOTSTRAP_CALLS_FALLBACK carries a SINGLE ["Type", "list_of"] entry rather than one per calling context — the bootstrap branch checks that key too now, same reasoning as the ordinary fallback.



109
# File 'lib/hecks/bluebook/dsl/attribute_collector.rb', line 109

def list_of_impl(type) = ListOf.new(type)

#one_of_impl(*values) ⇒ Object

A closed set declared INLINE on the attribute:

attribute :status, one_of("open", "shut")

Desugars to a value object named for the attribute, so it goes through exactly the machinery a hand-written one_of does — and so the attribute's type is still a DECLARED value object, which is now a structural rule rather than a predicate.

An earlier reading of this spelling parsed it and threw the values away: the attribute became a plain String and the closed set meant nothing, in a construct that looked supported. The desugaring is pinned now — the same bluebook must always yield the same IR. RENAMED FROM one_of — item #13's full metaprogrammed dispatch (slice 5), same reasoning as list_of_impl above. SAME NAME as ValueObjectBuilder#one_of_impl's own override on purpose — that method's own super(*values) call (the no-block, bare type-position case) resolves by METHOD NAME up the ancestor chain, and renaming only one side would silently break it.



154
# File 'lib/hecks/bluebook/dsl/attribute_collector.rb', line 154

def one_of_impl(*values) = OneOf.new(values)