Class: Hecks::Bluebook::DSL::TranslationAggregateBuilder

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

Constant Summary collapse

GRAMMAR_CONTEXT =
"TranslationAggregate"

Constants included from WordGate

WordGate::NOT_ADMITTED

Instance Method Summary collapse

Constructor Details

#initialize(name, was: nil) ⇒ TranslationAggregateBuilder

Returns a new instance of TranslationAggregateBuilder.

Raises:



10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/hecks/bluebook/dsl/translation_builder.rb', line 10

def initialize(name, was: nil)
  raise Malformed, "an aggregate translation needs a name" if name.to_s.empty?

  @name      = name
  @was       = was
  @renames   = {}
  @moves     = []
  @converts  = []
  @drops     = []
  @retypes   = []
  @computes  = []
  @rekeys    = []
  @backfills = []
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Hecks::Bluebook::DSL::WordGate

Instance Method Details

#backfill_impl(name, default:) ⇒ Object

A NEWLY ADDED, required attribute — the addition-side sibling of drop. Nothing to rename, move, or convert FROM, since old data never held this field at all; default is what an existing record reads until the next command against it writes a real value. Adapter-agnostic, unlike compute — applied the same in-process way rename/move/drop already are (Lineage#translate), because there is nothing to compute here, only a value to declare. This is what EraGuard.refuse_unsafe_addition! asks for when a non-optional attribute with no default: could leave an existing record with the field genuinely absent.

Raises:



115
116
117
118
119
120
# File 'lib/hecks/bluebook/dsl/translation_builder.rb', line 115

def backfill_impl(name, default:)
  raise Malformed, "a backfill needs a name" if name.to_s.empty?
  raise Malformed, "a backfill needs a default: value" if default.nil?

  @backfills << TranslationBackfill.new(name.to_sym, default)
end

#buildObject

method_missing/respond_to_missing? used to be hand-written here, giving a hand-typed "must be rename, move, convert, ..." list on every genuinely undefined call — WordGate (included above) now answers the same question off the self-hosted grammar table instead, the exact "hardcoded legal-word list" this whole arc's item #13 exists to close. A word admitted elsewhere in the grammar but not in this context still gets a richer, table-driven refusal than the old generic one did.



149
150
151
152
153
154
# File 'lib/hecks/bluebook/dsl/translation_builder.rb', line 149

def build
  TranslationAggregate.new(
    name: @name, was: @was, renames: @renames, moves: @moves, converts: @converts,
    drops: @drops, retypes: @retypes, computes: @computes, rekeys: @rekeys, backfills: @backfills
  )
end

#compute_impl(old_path, to:, sql:) ⇒ Object

A computed transform whose only implementation is the SQL expression itself — Postgres-only by construction. The scaffold never proposes one; a human writes it, and the audit's human-sampled review is its only verification.

Raises:



82
83
84
85
86
87
88
# File 'lib/hecks/bluebook/dsl/translation_builder.rb', line 82

def compute_impl(old_path, to:, sql:)
  raise Malformed, "a compute needs a destination path (to:)" if to.to_s.empty?
  raise Malformed, "a compute needs a source path" if old_path.to_s.empty?
  raise Malformed, "a compute needs its sql: expression" if sql.to_s.empty?

  @computes << TranslationCompute.new(old_path.to_s, to.to_s, sql.to_s)
end

#convert_impl(old_path, to:, values:) ⇒ Object

A value with nothing structural in common with its replacement — declared as an exhaustive table, not computed, so every value that can appear in old data has a named destination. Paths follow move's convention: dotted reaches a value-object member.

Raises:



51
52
53
54
55
56
57
# File 'lib/hecks/bluebook/dsl/translation_builder.rb', line 51

def convert_impl(old_path, to:, values:)
  raise Malformed, "a convert needs a destination path (to:)" if to.to_s.empty?
  raise Malformed, "a convert needs a source path" if old_path.to_s.empty?
  raise Malformed, "a convert needs a values: table" if values.nil? || values.empty?

  @converts << TranslationConvert.new(old_path.to_s, to.to_s, values)
end

#move_impl(old_path, to:) ⇒ Object

Raises:



40
41
42
43
44
45
# File 'lib/hecks/bluebook/dsl/translation_builder.rb', line 40

def move_impl(old_path, to:)
  raise Malformed, "a move needs a destination path (to:)" if to.to_s.empty?
  raise Malformed, "a move needs a source path" if old_path.to_s.empty?

  @moves << TranslationMove.new(old_path.to_s, to.to_s)
end

#rekey_impl(sql:) ⇒ Object

THE AGGREGATE'S OWN IDENTITY, changing what it's computed from — not a field crossing a boundary (move), not a value's own transform (compute): the record's key. No path arguments, unlike every rule above — nothing is consumed from or moved into state, only what identifies the record is recomputed. Same SQL-only, Postgres-only, human-reviewed-sample-is-the-only- verification shape compute already has, and for the same reason: there is nothing in-process to check this against.

Raises:



98
99
100
101
102
# File 'lib/hecks/bluebook/dsl/translation_builder.rb', line 98

def rekey_impl(sql:)
  raise Malformed, "a rekey needs its sql: expression" if sql.to_s.empty?

  @rekeys << TranslationRekey.new(sql.to_s)
end

#rename_impl(old_name, to:) ⇒ Object

RENAMED FROM rename/move/convert/retype/compute/ rekey/backfill (all seven below) — item #13's full metaprogrammed dispatch (slice 4c). Not bootstrap-reachable (translation.bluebook describes ITS OWN structure with aggregate/entity/attribute, never with these — they're words for real, user-authored .translation files only, loaded after the grammar table exists), so none need a BOOTSTRAP_CALLS_FALLBACK entry.

Raises:



33
34
35
36
37
38
# File 'lib/hecks/bluebook/dsl/translation_builder.rb', line 33

def rename_impl(old_name, to:)
  raise Malformed, "a rename needs a source name" if old_name.to_s.empty?
  raise Malformed, "a rename needs a destination name (to:)" if to.to_s.empty?

  @renames[old_name.to_sym] = to.to_sym
end

#retype_impl(old_type, to:) ⇒ Object

A value object's or entity's own type name changed, member structure unchanged. The stored data never carries the type name, so nothing moves — this declares that the pair of names means the same shape, which is what lets the era diff accept it.

Raises:



71
72
73
74
75
76
# File 'lib/hecks/bluebook/dsl/translation_builder.rb', line 71

def retype_impl(old_type, to:)
  raise Malformed, "a retype needs a source type name" if old_type.to_s.empty?
  raise Malformed, "a retype needs a destination type name (to:)" if to.to_s.empty?

  @retypes << TranslationRetype.new(old_type.to_s, to.to_s)
end

#unresolved_impl(name, candidates: []) ⇒ Object

The scaffold writes this where it cannot decide; a file carrying one can only boot into this refusal — never a guess.

RENAMED FROM unresolved — item #13's full metaprogrammed dispatch (slice 4). Builds its own message with real branching (empty vs. named candidates, a special :identity case), not a fixed string a boolean refuses: flag could express — reached through calls: instead, like attribute/role. NOT bootstrap-reachable: translation.bluebook (loaded during bootstrap, to describe the translation DSL itself) never writes unresolved — that word is only ever used by real, user-authored .translation files, loaded well after the grammar table exists — so no BOOTSTRAP_CALLS_FALLBACK entry is needed here (checked directly, not assumed).

Raises:



136
137
138
# File 'lib/hecks/bluebook/dsl/translation_builder.rb', line 136

def unresolved_impl(name, candidates: [])
  raise Malformed, unresolved_message(name, candidates)
end