Module: Hecks::Naming
- Defined in:
- lib/hecks/naming.rb
Constant Summary collapse
- IDENTITY_JOIN =
WHAT SEPARATES THE PARTS OF A DERIVED IDENTITY.
An identity of several parts is their JOIN, and the join has to be spelled the same everywhere or two readers name two different records off one declaration. It was spelled three ways at once here — "::" for an aggregate under a chapter, "." for everything under an aggregate, "#" for the three that keyed off position — and each was written where it happened to be needed. Once the runtime derived the same identity, the runtime made a FOURTH, and a reference that resolved by string comparison found nothing.
":".freeze
Class Method Summary collapse
-
.command_ref(value) ⇒ Object
trigger Account::Debit/dispatch Account::Debit— a bare CONSTANT reference (ConstShim's ownScopedConstant, S0b), not text (ADR 0025, "events and reactions" — command references become first-class). - .demodulise(type) ⇒ Object
-
.identity(parts) ⇒ Object
The parts of an identity, joined in declaration order.
-
.pascal(text) ⇒ Object
snake_case -> PascalCase.
-
.plural(text) ⇒ Object
The name a COLLECTION of something takes.
- .qualifier(dotted) ⇒ Object
- .reference_key(type) ⇒ Object
-
.singularize(text) ⇒ Object
has_many's undo — the plural WRITTEN, back to the singular the target aggregate is actually named. - .snake(text) ⇒ Object
- .split_dotted(dotted) ⇒ Object
- .split_verb(verb) ⇒ Object
- .unqualified(dotted) ⇒ Object
Class Method Details
.command_ref(value) ⇒ Object
trigger Account::Debit / dispatch Account::Debit — a bare
CONSTANT reference (ConstShim's own ScopedConstant, S0b), not
text (ADR 0025, "events and reactions" — command references become
first-class). Ruby's :: joins EVERY segment the same way a
constant path always does, but a command's own hecks_fqn joins
its aggregate with . (Construct#hecks_separator's default,
only an AGGREGATE overrides it to ::) — so only the LAST ::
becomes a .; everything before it (the chapter, when a domain is
spelled at all: Banking::Account::Debit) stays ::-joined.
A STRING PASSES THROUGH UNCHANGED, on purpose — legacy era text
(S0a's own shadow-parsed spelling) already mixes :: (domain) and
. (command) correctly on its own, e.g. "Banking::Account.Debit",
and re-splitting that by content rather than by TYPE would corrupt
it (its own last :: sits between the domain and the aggregate,
not the aggregate and the command). Only an actual constant object
— never seen holding a . of its own — needs the rewrite at all.
113 114 115 116 117 118 119 |
# File 'lib/hecks/naming.rb', line 113 def command_ref(value) return value.to_s if value.is_a?(::String) || value.is_a?(::Symbol) text = value.to_s path, _, command = text.rpartition("::") path.empty? ? text : "#{path}.#{command}" end |
.demodulise(type) ⇒ Object
19 20 21 |
# File 'lib/hecks/naming.rb', line 19 def demodulise(type) type.to_s.split("::").last.to_s end |
.identity(parts) ⇒ Object
The parts of an identity, joined in declaration order.
17 |
# File 'lib/hecks/naming.rb', line 17 def identity(parts) = Array(parts).join(IDENTITY_JOIN) |
.pascal(text) ⇒ Object
snake_case -> PascalCase. The name a synthesised closed-set value object takes when an attribute declares one inline. The derivation is part of the IR contract: the same bluebook must always produce the same name.
26 27 28 |
# File 'lib/hecks/naming.rb', line 26 def pascal(text) text.to_s.split("_").map { |part| part.sub(/\A(.)/) { Regexp.last_match(1).upcase } }.join end |
.plural(text) ⇒ Object
The name a COLLECTION of something takes.
There were two of these and one was wrong. A read model's gathered heads
derived their name with a bare "#{snake(target)}s", so the meta-domain's
own whole-bluebook read model handed back querys, entitys, policys
and dispatchs — and every check was green, because the checks compared
the wrong rule against itself. Agreement is not correctness; it never was.
So: one pluraliser, three rules, and every collection name flows through it.
46 47 48 49 50 51 52 |
# File 'lib/hecks/naming.rb', line 46 def plural(text) word = text.to_s return "#{word[0..-2]}ies" if word.match?(/[^aeiou]y\z/) return "#{word}es" if word.match?(/(s|x|z|ch|sh)\z/) "#{word}s" end |
.qualifier(dotted) ⇒ Object
78 79 80 81 |
# File 'lib/hecks/naming.rb', line 78 def qualifier(dotted) text = dotted.to_s text.include?(".") ? text.split(".", 2).first : nil end |
.reference_key(type) ⇒ Object
69 70 71 |
# File 'lib/hecks/naming.rb', line 69 def reference_key(type) snake(demodulise(type)).to_sym end |
.singularize(text) ⇒ Object
has_many's undo — the plural WRITTEN, back to the singular the target
aggregate is actually named. Deliberately the crude half of a pair: plural
above earns its precision (three suffix rules) because getting a COLLECTION
name wrong reads as a typo forever ; this only ever recovers a name someone
already wrote as a real aggregate, so "ies -> y, trailing s dropped" is the
whole rule — enough for has_many Invoices to resolve to the aggregate
actually named Invoice.
61 62 63 64 65 66 67 |
# File 'lib/hecks/naming.rb', line 61 def singularize(text) word = text.to_s return "#{word[0..-4]}y" if word.length > 3 && word.end_with?("ies") return word[0..-2] if word.length > 1 && word.end_with?("s") word end |
.snake(text) ⇒ Object
30 31 32 33 34 35 |
# File 'lib/hecks/naming.rb', line 30 def snake(text) text.to_s .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') .gsub(/([a-z\d])([A-Z])/, '\1_\2') .downcase end |
.split_dotted(dotted) ⇒ Object
73 74 75 76 |
# File 'lib/hecks/naming.rb', line 73 def split_dotted(dotted) first, second = dotted.to_s.split(".", 2) [first.to_s, second.to_s] end |
.split_verb(verb) ⇒ Object
88 89 90 91 92 93 94 |
# File 'lib/hecks/naming.rb', line 88 def split_verb(verb) path, command = verb.to_s.split(".", 2) domain, aggregate = path.to_s.split("::", 2) return nil unless domain && aggregate && command [domain, aggregate, command] end |
.unqualified(dotted) ⇒ Object
83 84 85 86 |
# File 'lib/hecks/naming.rb', line 83 def unqualified(dotted) text = dotted.to_s text.include?(".") ? text.split(".", 2).last : text end |