namo 0.25.0 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +132 -312
  3. data/lib/Namo/VERSION.rb +1 -1
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf81d723c5723d7737dd544596abbe5b69323f9f027a1aac7caef2cb857ebf5a
4
- data.tar.gz: 6b1130eb34b16920641e594b52aaff58e9f275ac2a26b0089b21f62a6d8f6630
3
+ metadata.gz: fd20f662edb4f305d6c74b5a100c536c20ad57ce31f7044498f76eebe2fbbf68
4
+ data.tar.gz: e242043273cac5866c8dda07aec202c69954d36072f8092b50bb817b6ca24bb8
5
5
  SHA512:
6
- metadata.gz: fb7c989aeb34877ed56e83190bf947217370103420c6f5e5186a88316217f35c4c801d0ed1864bd0105129d3391c99a635ed756c1050bb57a6ce33c4fbffa0e5
7
- data.tar.gz: da0f8fd20ccfa3f33b860059f33a8e65ddfed9bc157c02c1dd73389c3b907b8f4dbe82eeaf89f6a5e28a45274b74d8d03121ea7bdee9855d772242e4c50f8683
6
+ metadata.gz: 581cbeae8e0eeca1fb35db857d2acad566ecd21dc8d967e921161620b6a994e26293b17026e806c0c754851fe8da175bf3e260afe98e8f2899853fcb1e20fda3
7
+ data.tar.gz: cdc1687745394bf2931469daf7823d0c724436aea4a0c4657505f39beae1afe8a6224406682f9d630672a12fc6554b9872044ee8d72d93acfd03d7459bb32282
data/CHANGELOG CHANGED
@@ -1,19 +1,29 @@
1
- CHANGELOG
2
- _________
1
+ # CHANGELOG
2
+
3
+ ## 20260703
4
+
5
+ 0.25.1: Fix documentation and reformat CHANGELOG to use Markdown headers.
6
+
7
+ 1. ~ ROADMAP.md: + the missing 0.22.0 entry (Formulae#derive) between 0.21.0 and 0.23.0; normalise 0.20.0–0.24.1 to ### under ## Current state (#### subsections), matching 0.0.0–0.19.0 and the already-correct 0.25.0; relocate ### Summary to close the shipped block, its vocabulary brought current through the formulary tier and attach!; the 1.0.0 "everything through" line → 0.25.0 with an attach! bullet.
8
+ 2. ~ CHANGELOG: reformat to Markdown headers (# CHANGELOG, ## <date>), keeping the extensionless name; entry bodies unchanged.
9
+ 3. ~ docs/: regenerate the print-ready PDFs via rake docs:gen (stale for several releases).
10
+ 4. ~ Namo::VERSION: /0.25.0/0.25.1/
11
+
12
+ ## 20260703
3
13
 
4
- 20260703
5
14
  0.25.0: + Namo#attach! — attach a formulary, evicting any data column whose name a formulary method collides with, rather than raising.
6
15
 
7
16
  attach! evicts the colliding data columns first (as []='s proc-over-data assignment does), then attaches — the bang is the explicit consent that attach's raise withholds by default. << stays guarded: a Module still routes to attach.
8
17
 
9
18
  1. ~ Namo#attach — extract the collision set into a new private attach_collisions(modul) (modul.public_instance_methods(false) & data_dimensions); behaviour unchanged, still raises ArgumentError naming the collisions when the set is non-empty.
10
- 2, + Namo#attach! — evicts each colliding data column (@data.each{|row| row.delete(name)} per colliding name, the column membership already confirmed by attach_collisions so no key-guard is needed), then forwards to @formulae.attach and returns self. The eviction mirrors []='s proc-branch clear. + private attach_collisions(modul). << is unchanged: a Module still routes to attach (the guarded form), so the operator stays safe and attach! is reached only by explicit call — one safe operator, one explicit forceful method, matching []= having no operator shortcut for its own eviction.
19
+ 2. + Namo#attach! — evicts each colliding data column (@data.each{|row| row.delete(name)} per colliding name, the column membership already confirmed by attach_collisions so no key-guard is needed), then forwards to @formulae.attach and returns self. The eviction mirrors []='s proc-branch clear. + private attach_collisions(modul). << is unchanged: a Module still routes to attach (the guarded form), so the operator stays safe and attach! is reached only by explicit call — one safe operator, one explicit forceful method, matching []= having no operator shortcut for its own eviction.
11
20
  3. ~ test/namo_test.rb: + an "#attach!" describe — attach! attaches as attach does for a non-colliding module (returns self, methods resolve); attach! evicts a colliding data column and registers the formulary method in its place, with all access paths (values, Row, selection) agreeing on the formula afterward and the name appearing once in derived_dimensions, not in data_dimensions; attach! over several colliding columns evicts all of them; attach! on a vanilla collision-free module leaves the data untouched; the existing attach collision-raise tests are unchanged, pinning that the guarded form still raises where attach! evicts.
12
21
  4. ~ README.md: ~ the Formularies section — + attach! as the forceful sibling of attach, with the materialised-snapshot refresh as the motivating case and the bang-is-consent rationale; + the self-eviction-recurses caveat (a formulary method that reads a column its own name evicts recurses on access, exclusive storage leaving no shadowed value behind the formula — the one way attach!'s eviction differs in feel from []='s); note that << stays the guarded form.
13
22
  5. ~ ROADMAP.md: + the 0.25.0 release entry; current-state bumped to 0.25.0.
14
23
  6. ~ Namo::VERSION: /0.24.1/0.25.0/
15
24
 
16
- 20260629
25
+ ## 20260629
26
+
17
27
  0.24.1: ~ Namo#add_row — guard row-append against a data/formula name collision.
18
28
 
19
29
  0.24.0 left row-append (the Hash/Row arms of <<) unguarded, on the reasoning that the data/formula exclusivity invariant was []='s to keep. That reasoning had a hole: when an appended row carries a key matching an existing formula name and lands as @data.first (e.g. the first row streamed into an empty formulary-bearing Namo), bulk values reads the raw datum (data_dimensions keys off @data.first.keys) while per-Row access derives the formula (Row#[] checks @formulae first) — the two access paths disagree, the precise split the invariant exists to prevent. So add_row now raises rather than admitting the collision, symmetric with attach's guard: a row is a bulk append you didn't name column-by-column, so refusing is safer than []='s call-site eviction.
@@ -24,7 +34,8 @@ attach! evicts the colliding data columns first (as []='s proc-over-data assignm
24
34
  4. ~ ROADMAP.md: + the 0.24.1 release entry; current-state bumped to 0.24.1; the 0.24.0 "not guarded" note annotated as guarded at 0.24.1.
25
35
  5. ~ Namo::VERSION: /0.24.0/0.24.1/
26
36
 
27
- 20260629
37
+ ## 20260629
38
+
28
39
  0.24.0: ~ Namo#<<, Namo::Collection#<< — widen from a formulary-only alias into a polymorphic "append a constituent" operator.
29
40
 
30
41
  A base Namo's constituents are formularies and rows: << dispatches a Module to attach (0.23.0 behaviour, unchanged), a Hash to a data-row append, and a Row to a row append of its underlying hash. It does not take a bare callable (formula registration stays with []=) or a whole Namo (combining is +'s job) — both raise. Collection#<< is reconciled to the same verb over its own constituents: a Namo is a member, a Module attaches, and a loose Hash/Row raises (a Collection's rows are derived from its members, so a loose row has no durable home). This Hash/Row divergence — a base Namo appends the row, a Collection refuses it — is deliberate, not an oversight.
@@ -37,7 +48,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
37
48
  6. ~ ROADMAP.md: + the 0.24.0 release entry; current-state bumped to 0.24.0; pivot/reshaping recorded as a decision (long-form aggregation is in-grain future work, widening-to-a-grid is presentational and belongs at an output edge, not the algebra), and collection formulae noted as needing no new mechanism.
38
49
  7. ~ Namo::VERSION: /0.23.0/0.24.0/
39
50
 
40
- 20260627
51
+ ## 20260627
52
+
41
53
  0.23.0: + Namo::Formulary — A formulary is a reusable body of derived dimensions a Namo instance may draw upon.
42
54
 
43
55
  1. + lib/Namo/Formulary.rb: + Namo::Formulary, an empty marker module. A module includes it to brand itself a formulary; attach raises ArgumentError for an untagged module, so an ordinary module of helpers can't be mistaken for a body of derivations.
@@ -48,341 +60,132 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
48
60
  6. ~ README.md: + a "Formularies" subsection under Formulae (attach and the marker, private-hides-helpers, the copy-into-the-store / snapshot model, the raise on a data collision, materialise-and-drop, carry-through, the bare-name/memoisation forward-note); the Polymorphic []= section now says "a callable" (proc, lambda, or Method) rather than "a proc". ~ COMPARISON.md: + a "Formularies" row. ~ ROADMAP.md: + the 0.23.0 release entry; current-state bumped to 0.23.0; the 2.x "module-based formula libraries", "Namo#=== revision", and marker-vs-implicit notes annotated as shipped/decided at 0.23.0, with bare-name bodies remaining 2.x.
49
61
  7. ~ Namo::VERSION: /0.22.0/0.23.0/
50
62
 
51
- 20260626
63
+ ## 20260626
64
+
52
65
  0.22.0: + Formulae#derive — move formula invocation out of Row#[] and into Formulae. [] keeps its meaning (the stored callable, as at 0.21.0); the new derive(name, row, namo, *arguments) resolves a named formula to its value. derive over call: the receiver is the collection, not the callable, so the verb says the collection derives a value via the named rule rather than borrowing Proc#call's "invoke the receiver"; and the result is whatever the proc returns, not necessarily numeric, which a neutral verb leaves open. Indexing a Formulae is storage access, not a Namo operation, so [] returning the callable carries no inconsistency with the dimension-value [] of Namo and Row — different domains, same spelling. Behaviour-preserving at the usage level: row[:revenue], namo[:x] = formula, values, and selections are unchanged.
53
66
 
54
- 1. ~ lib/Namo/Formulae.rb: + derive(name, row, namo, *arguments), the formula resolver —
55
- invocation mechanics lifted whole from Row's formula branch (collection-scoped formulae
56
- are called (row, namo, *arguments), row-only formulae (row)). + required_parameter_count(name),
57
- public, a property of the stored callable. collection_scoped? and raise_unless_namo_context
58
- move in as private; the latter takes namo as a parameter rather than closing over Row's
59
- @namo. [] and []= are untouched — [] still returns the stored callable.
60
- 2. ~ lib/Namo/Row.rb: #[] delegates formula resolution to
61
- @formulae.derive(name, self, @namo, *arguments); Row keeps only the fail-fast argument
62
- guard, the formula-vs-data dispatch (@formulae.key?), and the data fallback (@row[name]).
63
- collection_scoped?, required_parameter_count, and raise_unless_namo_context are gone
64
- (now Formulae's). expected_argument_counts sources the count from
65
- @formulae.required_parameter_count and the fixed-vs-variadic check from
66
- @formulae[name].arity.
67
- 3. + test/Namo/Formulae_test.rb: + #derive tests (row-only to value, collection-scoped with
68
- namo context, missing-namo-context raise, parameterised with arguments) and +
69
- #required_parameter_count tests (row-only, collection-scoped, parameterised, variadic).
70
- The #[] and #[]= blocks are unchanged — [] still returns the callable.
71
- 4. ~ test/Namo/Row_test.rb: the formulae fixture is a Namo::Formulae, not a bare {} — Row
72
- now delegates formula resolution to its collaborator (call, required_parameter_count),
73
- which production always wraps (lib/namo.rb initialize). The data-only equality/match
74
- tests keep their {} literals: they never resolve a formula, so Hash#key? alone answers.
67
+ 1. ~ lib/Namo/Formulae.rb: + derive(name, row, namo, *arguments), the formula resolver — invocation mechanics lifted whole from Row's formula branch (collection-scoped formulae are called (row, namo, *arguments), row-only formulae (row)). + required_parameter_count(name), public, a property of the stored callable. collection_scoped? and raise_unless_namo_context move in as private; the latter takes namo as a parameter rather than closing over Row's @namo. [] and []= are untouched — [] still returns the stored callable.
68
+ 2. ~ lib/Namo/Row.rb: #[] delegates formula resolution to @formulae.derive(name, self, @namo, *arguments); Row keeps only the fail-fast argument guard, the formula-vs-data dispatch (@formulae.key?), and the data fallback (@row[name]). collection_scoped?, required_parameter_count, and raise_unless_namo_context are gone (now Formulae's). expected_argument_counts sources the count from @formulae.required_parameter_count and the fixed-vs-variadic check from @formulae[name].arity.
69
+ 3. + test/Namo/Formulae_test.rb: + #derive tests (row-only to value, collection-scoped with namo context, missing-namo-context raise, parameterised with arguments) and + #required_parameter_count tests (row-only, collection-scoped, parameterised, variadic). The #[] and #[]= blocks are unchanged — [] still returns the callable.
70
+ 4. ~ test/Namo/Row_test.rb: the formulae fixture is a Namo::Formulae, not a bare {} Row now delegates formula resolution to its collaborator (call, required_parameter_count), which production always wraps (lib/namo.rb initialize). The data-only equality/match tests keep their {} literals: they never resolve a formula, so Hash#key? alone answers.
75
71
  5. ~ Namo::VERSION: /0.21.0/0.22.0/
76
72
 
77
- 20260625
73
+ ## 20260625
74
+
78
75
  0.21.0: + Namo::Formulae; @formulae: /Hash/Namo::Formulae/. Behaviour-preserving: no new capability, just an object for the formula collection to accrete behaviour onto later, mirroring the 0.1.0 extraction of Row.
79
76
 
80
- 1. + lib/Namo/Formulae.rb: + Namo::Formulae, a thin class wrapping a Hash @store.
81
- Responds to exactly the subset of Hash that lib/namo.rb and Row send the formula
82
- collection: [], []=, keys, key?, empty?, each, delete, merge, reject, dup; includes
83
- Enumerable (each yields [name, callable] pairs, giving map/select/etc. for free).
84
- Three deliberate choices: to_h returns a copy (@store.dup) so callers cannot mutate
85
- internals; ==/eql?/hash are names-only (keys.sort), matching Namo's own formula
86
- equality where two sets with the same names but different proc objects are equivalent;
87
- merge/reject/dup return a Formulae, not a Hash.
88
- 2. ~ lib/namo.rb: + the require; initialize now normalises the incoming formulae to a
89
- Formulae (Formulae.new(formulae) unless already one). This is the single behavioural
90
- seam — every existing internal construction passes a Hash and is wrapped, while
91
- operator results that now pass a Formulae (merge/dup return Formulae) pass through.
92
- No other call site changed: each sends a message Formulae answers.
93
- 3. ~ test/namo_test.rb: the two assertions comparing the public #formulae accessor to a
94
- literal {} now assert emptiness (must_be_empty). The accessor returns a Formulae, not a
95
- Hash; the formula set being empty is the unchanged behaviour these tests pin. + two
96
- construction tests pinning the normalisation seam: a Hash given by keyword is wrapped in
97
- a Formulae, and a Formulae given by keyword passes through unchanged (same object).
98
- 4. + test/Namo/Formulae_test.rb: + unit test pinning the three deliberate choices (to_h
99
- copy, names-only ==/eql?/hash, the Hash-compatible surface plus Enumerable map/select).
77
+ 1. + lib/Namo/Formulae.rb: + Namo::Formulae, a thin class wrapping a Hash @store. Responds to exactly the subset of Hash that lib/namo.rb and Row send the formula collection: [], []=, keys, key?, empty?, each, delete, merge, reject, dup; includes Enumerable (each yields [name, callable] pairs, giving map/select/etc. for free). Three deliberate choices: to_h returns a copy (@store.dup) so callers cannot mutate internals; ==/eql?/hash are names-only (keys.sort), matching Namo's own formula equality where two sets with the same names but different proc objects are equivalent; merge/reject/dup return a Formulae, not a Hash.
78
+ 2. ~ lib/namo.rb: + the require; initialize now normalises the incoming formulae to a Formulae (Formulae.new(formulae) unless already one). This is the single behavioural seam — every existing internal construction passes a Hash and is wrapped, while operator results that now pass a Formulae (merge/dup return Formulae) pass through. No other call site changed: each sends a message Formulae answers.
79
+ 3. ~ test/namo_test.rb: the two assertions comparing the public #formulae accessor to a literal {} now assert emptiness (must_be_empty). The accessor returns a Formulae, not a Hash; the formula set being empty is the unchanged behaviour these tests pin. + two construction tests pinning the normalisation seam: a Hash given by keyword is wrapped in a Formulae, and a Formulae given by keyword passes through unchanged (same object).
80
+ 4. + test/Namo/Formulae_test.rb: + unit test pinning the three deliberate choices (to_h copy, names-only ==/eql?/hash, the Hash-compatible surface plus Enumerable map/select).
100
81
  5. ~ Namo::VERSION: /0.20.0/0.21.0/
101
82
 
102
- 20260615
83
+ ## 20260615
84
+
103
85
  0.20.0: + group_by(dimension) — splits a Namo into a Namo::Collection, one member per distinct value, completing the Enumerable coherence pass begun at 0.11.0.
104
86
 
105
- 1. ~ lib/Namo/Enumerable.rb: + Namo#group_by(dimension). Returns a Namo::Collection
106
- whose members are the groups — one member per distinct value, each a Namo of the
107
- matching rows, named by its group value, carrying the surviving formulae, preserving
108
- subclass type via self.class.new. The grouping dimension is retained (the split is
109
- along the axis, not consuming it), so namo.group_by(d).as_detail(d) inverts it. A
110
- nil-valued group produces a nil-named member that holds its rows and round-trips, the
111
- use-site naming decision (0.18.0) paying off. A ternary makes the materialise
112
- decision: when dimension is derived, the group source is the projection
113
- (self[*data_dimensions, dimension], 0.16.0) so the grouped-by formula becomes a stored
114
- column and is dropped while every other formula carries through live; when data, the
115
- source is self untouched. A parameterised formula raises the 0.17.0 ArgumentError at
116
- the materialisation site, group_by needing no enforcement of its own.
117
- 2. ~ test/namo_test.rb: + "#group_by" describe — return type, one-member-per-value,
118
- row partitioning, grouping-dimension retention, find-by-name, formula carry-through,
119
- subclass preservation, non-mutation, empty-Namo; the uniform inversion law
120
- (data: exact; formula: against the materialised form); grouping by a formula
121
- (partition by value, materialise-and-drop, only-grouped-formula-altered with a live
122
- dependent, parameterised raise); the nil-valued grouping dimension (nil-named member,
123
- no rows dropped, round-trip); and consistency with assembly (summary/detail on a
124
- partitioned Collection).
125
- 3. ~ README.md: + group_by subsection under Collections — the partition constructor as
126
- the mirror of <<, the round-trip example, the materialise-on-formula behaviour, and
127
- the cross-reference to the assembly side.
128
- 4. ~ ROADMAP.md: Promote 0.20.0 to shipped; Current state -> 0.20.0; + the materialisation
129
- rationale (unified-treatment requires it; materialise-and-drop is the only invertible
130
- choice; the uniform inversion law); Summary folds in group_by; next phase -> 1.0.0.
131
- 5. ~ COMPARISON.md: Aggregation entry — group_by repointed from planned (0.20.0) to
132
- shipped, the partition constructor now realised; + the grouping-by-formula comparison
133
- (computed-key grouping is parity, but only Namo partitions on a named, attached formula
134
- through the data-dimension interface). Date bumped.
87
+ 1. ~ lib/Namo/Enumerable.rb: + Namo#group_by(dimension). Returns a Namo::Collection whose members are the groups — one member per distinct value, each a Namo of the matching rows, named by its group value, carrying the surviving formulae, preserving subclass type via self.class.new. The grouping dimension is retained (the split is along the axis, not consuming it), so namo.group_by(d).as_detail(d) inverts it. A nil-valued group produces a nil-named member that holds its rows and round-trips, the use-site naming decision (0.18.0) paying off. A ternary makes the materialise decision: when dimension is derived, the group source is the projection (self[*data_dimensions, dimension], 0.16.0) so the grouped-by formula becomes a stored column and is dropped while every other formula carries through live; when data, the source is self untouched. A parameterised formula raises the 0.17.0 ArgumentError at the materialisation site, group_by needing no enforcement of its own.
88
+ 2. ~ test/namo_test.rb: + "#group_by" describe return type, one-member-per-value, row partitioning, grouping-dimension retention, find-by-name, formula carry-through, subclass preservation, non-mutation, empty-Namo; the uniform inversion law (data: exact; formula: against the materialised form); grouping by a formula (partition by value, materialise-and-drop, only-grouped-formula-altered with a live dependent, parameterised raise); the nil-valued grouping dimension (nil-named member, no rows dropped, round-trip); and consistency with assembly (summary/detail on a partitioned Collection).
89
+ 3. ~ README.md: + group_by subsection under Collections the partition constructor as the mirror of <<, the round-trip example, the materialise-on-formula behaviour, and the cross-reference to the assembly side.
90
+ 4. ~ ROADMAP.md: Promote 0.20.0 to shipped; Current state -> 0.20.0; + the materialisation rationale (unified-treatment requires it; materialise-and-drop is the only invertible choice; the uniform inversion law); Summary folds in group_by; next phase -> 1.0.0.
91
+ 5. ~ COMPARISON.md: Aggregation entry — group_by repointed from planned (0.20.0) to shipped, the partition constructor now realised; + the grouping-by-formula comparison (computed-key grouping is parity, but only Namo partitions on a named, attached formula through the data-dimension interface). Date bumped.
135
92
  6. ~ Namo::VERSION: /0.19.0/0.20.0/
136
93
 
137
- 20260614
94
+ ## 20260614
95
+
138
96
  0.19.0: ~ row-multiset equality — replace the sorted canonical form with a row multiset, so ==, eql?, hash, and the subset operators compare without sorting: nil/NaN-safe, row- and dimension-order blind, and consistently type-strict.
139
97
 
140
- 1. ~ lib/namo.rb: replace canonical_data (which sorted @data by row.values_at(*data_dimensions.sort))
141
- with row_multiset (@data.tally). ==, eql?, hash, and subset_of_rows? now compare row
142
- multisets keyed by Hash#hash/#eql?. The sort was only ever a mechanism for reaching the
143
- already-decided multiset semantics (0.6.0); a multiset reaches them directly and needs no
144
- ordering, so the latent ArgumentError when a dimension mixed nil (or NaN) with present
145
- values — neither has a <=> against the other — is gone. The relation is now uniformly
146
- eql?-based, so == agrees with the subset operators, which already tallied: {a: 1} and
147
- {a: 1.0} are consistently distinct (== previously called them equal via Hash#==, while <=
148
- called them distinct).
149
- 2. ~ test/namo_test.rb: + four "#==" cases — equality across row order and across dimension
150
- (key) order when a dimension mixes nil and non-nil values; inequality when such rows
151
- differ; and type-strictness on values, consistent with <=.
98
+ 1. ~ lib/namo.rb: replace canonical_data (which sorted @data by row.values_at(*data_dimensions.sort)) with row_multiset (@data.tally). ==, eql?, hash, and subset_of_rows? now compare row multisets keyed by Hash#hash/#eql?. The sort was only ever a mechanism for reaching the already-decided multiset semantics (0.6.0); a multiset reaches them directly and needs no ordering, so the latent ArgumentError when a dimension mixed nil (or NaN) with present values — neither has a <=> against the other — is gone. The relation is now uniformly eql?-based, so == agrees with the subset operators, which already tallied: {a: 1} and {a: 1.0} are consistently distinct (== previously called them equal via Hash#==, while <= called them distinct).
99
+ 2. ~ test/namo_test.rb: + four "#==" cases — equality across row order and across dimension (key) order when a dimension mixes nil and non-nil values; inequality when such rows differ; and type-strictness on values, consistent with <=.
152
100
  3. ~ Namo::VERSION: /0.18.0/0.19.0/
153
101
 
154
- 20260613
102
+ ## 20260613
103
+
155
104
  0.18.0: + Namo::Collection — hierarchical aggregate of named Namos with summary/detail views.
156
105
 
157
- 1. + lib/Namo/Collection.rb: Namo::Collection < Namo, the first family member beyond Namo
158
- itself. Holds @members (an Array of named Namos, attr_reader :members) whose substance is
159
- the collection; the inherited @data is a derived view rebuilt from the members. << accepts a
160
- member or an array (via flatten), replaces by name (last-write-wins, unless member.name.nil?
161
- so nil-named members always append), rebuilds @data = detail.data, and returns self. find(name)
162
- returns the named member or nil (find(nil) is nil, and an unnamed member never matches),
163
- shadowing Enumerable#find on Collections. summary(dimension, by: :member, reducer: :sum)
164
- reduces each member to a {by => member.name, dimension => reduced} row; detail(by: :member)
165
- unions the members' rows under the inject-iff-absent conditional
166
- (row.key?(by) ? row : row.merge(by => member.name)); both return a fresh Namo (non-mutating).
167
- as_summary(...) and as_detail(by = :member) set @data to the chosen view and return self
168
- (mutating); the view persists until the next << re-materialises detail. Private initialize
169
- sets @members = [] before super.
106
+ 1. + lib/Namo/Collection.rb: Namo::Collection < Namo, the first family member beyond Namo itself. Holds @members (an Array of named Namos, attr_reader :members) whose substance is the collection; the inherited @data is a derived view rebuilt from the members. << accepts a member or an array (via flatten), replaces by name (last-write-wins, unless member.name.nil? so nil-named members always append), rebuilds @data = detail.data, and returns self. find(name) returns the named member or nil (find(nil) is nil, and an unnamed member never matches), shadowing Enumerable#find on Collections. summary(dimension, by: :member, reducer: :sum) reduces each member to a {by => member.name, dimension => reduced} row; detail(by: :member) unions the members' rows under the inject-iff-absent conditional (row.key?(by) ? row : row.merge(by => member.name)); both return a fresh Namo (non-mutating). as_summary(...) and as_detail(by = :member) set @data to the chosen view and return self (mutating); the view persists until the next << re-materialises detail. Private initialize sets @members = [] before super.
170
107
  2. ~ lib/namo.rb: + require_relative './Namo/Collection'.
171
- 3. + test/Namo/Collection_test.rb: empty members; lazy detail materialisation and selection;
172
- pure-live reflection of <<; <<' add/replace-by-name/array/unnamed/returns-self; find by name,
173
- absent, nil, and unnamed; summary's labelled rows, custom by:, :mean reducer (local Array#mean),
174
- and non-mutation; detail's plain-Namo return, inject-iff-absent both ways, and non-mutation;
175
- live recomputation after <<; as_summary/as_detail mutate and return self and surface the
176
- summary's columns in dimensions; as_* view persistence until the next <<; assembly round-trip
177
- (as_detail(:assembly) injects and is retained, removed only by contraction); subclass default
178
- by: via a Car < Namo::Collection overriding summary/detail.
179
- 4. ~ README.md: + Collections section after Named Namos — the GT-budget example (Car/SubAssembly,
180
- <<, summary, detail), lazy detail materialisation, the four view methods and the
181
- non-mutating/mutating split, the inject-iff-absent rule, << replace-by-name with use-site
182
- handling of unnamed members, the rebuild-on-<< view lifetime, and forward-notes to group_by
183
- (0.19.0) and freeze-gated memoisation (2.x).
184
- 5. ~ ROADMAP.md: Promote 0.18.0 to shipped, recording the resolved materialisation model
185
- (rebuild-on-<<, as_* persists until the next <<, diverging from the drafted pure-live-per-access
186
- / transient wording) and the find/Enumerable#find shadowing; Current state -> 0.18.0; Summary
187
- folds in Namo::Collection; next phase -> group_by (0.19.0). The upcoming 0.18.0 design block
188
- folded into the shipped entry.
189
- 6. ~ COMPARISON.md: Aggregation entry repointed — Namo::Collection shipped (0.18.0), group_by
190
- constructor still planned (0.19.0); persistent-named-collection retained as Namo's novelty
191
- versus the other tools' transient group-by intermediates.
108
+ 3. + test/Namo/Collection_test.rb: empty members; lazy detail materialisation and selection; pure-live reflection of <<; <<' add/replace-by-name/array/unnamed/returns-self; find by name, absent, nil, and unnamed; summary's labelled rows, custom by:, :mean reducer (local Array#mean), and non-mutation; detail's plain-Namo return, inject-iff-absent both ways, and non-mutation; live recomputation after <<; as_summary/as_detail mutate and return self and surface the summary's columns in dimensions; as_* view persistence until the next <<; assembly round-trip (as_detail(:assembly) injects and is retained, removed only by contraction); subclass default by: via a Car < Namo::Collection overriding summary/detail.
109
+ 4. ~ README.md: + Collections section after Named Namos — the GT-budget example (Car/SubAssembly, <<, summary, detail), lazy detail materialisation, the four view methods and the non-mutating/mutating split, the inject-iff-absent rule, << replace-by-name with use-site handling of unnamed members, the rebuild-on-<< view lifetime, and forward-notes to group_by (0.19.0) and freeze-gated memoisation (2.x).
110
+ 5. ~ ROADMAP.md: Promote 0.18.0 to shipped, recording the resolved materialisation model (rebuild-on-<<, as_* persists until the next <<, diverging from the drafted pure-live-per-access / transient wording) and the find/Enumerable#find shadowing; Current state -> 0.18.0; Summary folds in Namo::Collection; next phase -> group_by (0.19.0). The upcoming 0.18.0 design block folded into the shipped entry.
111
+ 6. ~ COMPARISON.md: Aggregation entry repointed — Namo::Collection shipped (0.18.0), group_by constructor still planned (0.19.0); persistent-named-collection retained as Namo's novelty versus the other tools' transient group-by intermediates.
192
112
  7. ~ Namo::VERSION: /0.17.0/0.18.0/
193
113
 
194
- 20260613
114
+ ## 20260613
115
+
195
116
  0.17.0: + parameterised formulae — formulae with required parameters beyond (row, namo) receive arguments at access time through Row#[].
196
117
 
197
- 1. ~ lib/Namo/Row.rb: Row#[] gains a trailing splat and forwards call-site arguments. Dispatch
198
- generalises from exact arity 2 to required-parameter count via the new private
199
- collection_scoped? and required_parameter_count — one required parameter or none stays
200
- row-scoped, two or more call formula.call(self, @namo, *arguments). Settles the 0.15.0
201
- negative-arity deferral: |row, *rest| and ->(row, namo = nil){} stay row-scoped;
202
- |row, namo, *fields| is collection-scoped with optional arguments.
203
- 2. ~ lib/Namo/Row.rb: + argument-count enforcement the new private expected_argument_counts
204
- and raise_unless_expected_arguments raise ArgumentError ("wrong number of arguments for
205
- :sma (given 0, expected 2)") on the wrong count for any dimension: too few or too many for
206
- a fixed-arity formula, fewer than required for a splatted one, any at all for a data
207
- dimension, a row-scoped formula, or a two-arity formula. Checked before the Namo-context
208
- guard, whose message generalises from "two-arity formula" to "collection-scoped formula".
209
- 3. ~ lib/namo.rb: the no-arg values (and so coordinates and to_h) materialise via the new
210
- private materialisable_dimensions, omitting formulae that require arguments (private
211
- requires_arguments? and required_parameter_count); explicit asks — values(:dim),
212
- coordinates(:dim), naming the dimension in a projection, selecting on it — raise through
213
- Row#[]. dimensions and derived_dimensions still list the name; the empty-Namo case returns
214
- [] without invoking the formula.
215
- 4. ~ test/Namo/Row_test.rb: + "#[] parameterised formulae" describe — arity 3 and 4 receive
216
- (row, namo, args...), trailing-splat forwarding (arity -3 and -4), one-required-parameter
217
- procs stay row-scoped, a row-scoped formula calling a parameterised one with arguments,
218
- missing-Namo-context raise; + "#[] argument-count enforcement" describe — the message
219
- matrix for too few, too many, splat minimum, and arguments handed to data dimensions,
220
- row-scoped, two-arity, and missing dimensions.
221
- 5. ~ test/namo_test.rb: + "#[]= parameterised formulae" describe — resolution with arguments
222
- through yielded Rows, one definition serving different fields and periods, Enumerable
223
- predicates, a one-arity formula referencing a parameterised one, dimension listing, bulk
224
- views omitting, explicit values/coordinates/projection/selection raises, carry through
225
- contraction, selection (windowing over the filtered rows), and set operators, the
226
- one-arity wrapper materialisation idiom, namo-plus-splat formulae materialising in the
227
- bulk views, and the empty-Namo case.
228
- 6. ~ README.md: + Parameterised formulae subsection under Formulae — access-time arguments,
229
- the required-parameter scope rule, argument-count enforcement, materialisation behaviour,
230
- and the wrapper idiom.
231
- 7. ~ ROADMAP.md: Promote 0.17.0 to shipped with the dispatch-rule, enforcement, and
232
- materialisation rationale; Current state -> 0.17.0; Summary folds in parameterised
233
- formulae; next phase -> Namo::Collection (0.18.0). Date bumped.
234
- 8. ~ COMPARISON.md: Parameterised formulae -> shipped (0.17.0), the entry noting access-time
235
- arguments and argument-count enforcement. Date bumped.
236
- 9. ~ EXAMPLES.md: The finance section's composition blocks (all three Namo stages) corrected
237
- from max_by, which returns a Row, to sort_by + last(1), which returns the Namo the 0.14.0
238
- block contract requires — the 1.x stage, parameterised sma included, now runs end to end
239
- against this release. The finance highlight notes the parameterised formula as shipped
240
- (0.17.0). Date bumped.
118
+ 1. ~ lib/Namo/Row.rb: Row#[] gains a trailing splat and forwards call-site arguments. Dispatch generalises from exact arity 2 to required-parameter count via the new private collection_scoped? and required_parameter_count — one required parameter or none stays row-scoped, two or more call formula.call(self, @namo, *arguments). Settles the 0.15.0 negative-arity deferral: |row, *rest| and ->(row, namo = nil){} stay row-scoped; |row, namo, *fields| is collection-scoped with optional arguments.
119
+ 2. ~ lib/Namo/Row.rb: + argument-count enforcement — the new private expected_argument_counts and raise_unless_expected_arguments raise ArgumentError ("wrong number of arguments for :sma (given 0, expected 2)") on the wrong count for any dimension: too few or too many for a fixed-arity formula, fewer than required for a splatted one, any at all for a data dimension, a row-scoped formula, or a two-arity formula. Checked before the Namo-context guard, whose message generalises from "two-arity formula" to "collection-scoped formula".
120
+ 3. ~ lib/namo.rb: the no-arg values (and so coordinates and to_h) materialise via the new private materialisable_dimensions, omitting formulae that require arguments (private requires_arguments? and required_parameter_count); explicit asks values(:dim), coordinates(:dim), naming the dimension in a projection, selecting on it — raise through Row#[]. dimensions and derived_dimensions still list the name; the empty-Namo case returns [] without invoking the formula.
121
+ 4. ~ test/Namo/Row_test.rb: + "#[] parameterised formulae" describe — arity 3 and 4 receive (row, namo, args...), trailing-splat forwarding (arity -3 and -4), one-required-parameter procs stay row-scoped, a row-scoped formula calling a parameterised one with arguments, missing-Namo-context raise; + "#[] argument-count enforcement" describe — the message matrix for too few, too many, splat minimum, and arguments handed to data dimensions, row-scoped, two-arity, and missing dimensions.
122
+ 5. ~ test/namo_test.rb: + "#[]= parameterised formulae" describe — resolution with arguments through yielded Rows, one definition serving different fields and periods, Enumerable predicates, a one-arity formula referencing a parameterised one, dimension listing, bulk views omitting, explicit values/coordinates/projection/selection raises, carry through contraction, selection (windowing over the filtered rows), and set operators, the one-arity wrapper materialisation idiom, namo-plus-splat formulae materialising in the bulk views, and the empty-Namo case.
123
+ 6. ~ README.md: + Parameterised formulae subsection under Formulae — access-time arguments, the required-parameter scope rule, argument-count enforcement, materialisation behaviour, and the wrapper idiom.
124
+ 7. ~ ROADMAP.md: Promote 0.17.0 to shipped with the dispatch-rule, enforcement, and materialisation rationale; Current state -> 0.17.0; Summary folds in parameterised formulae; next phase -> Namo::Collection (0.18.0). Date bumped.
125
+ 8. ~ COMPARISON.md: Parameterised formulae -> shipped (0.17.0), the entry noting access-time arguments and argument-count enforcement. Date bumped.
126
+ 9. ~ EXAMPLES.md: The finance section's composition blocks (all three Namo stages) corrected from max_by, which returns a Row, to sort_by + last(1), which returns the Namo the 0.14.0 block contract requires the 1.x stage, parameterised sma included, now runs end to end against this release. The finance highlight notes the parameterised formula as shipped (0.17.0). Date bumped.
241
127
  10. ~ Namo::VERSION: /0.16.0/0.17.0/
242
128
 
243
- 20260612
129
+ ## 20260612
130
+
244
131
  0.16.0: ~ data/formula exclusivity — projection drops the formulae it materialises; * and ** raise on a data/formula name collision.
245
132
 
246
- 1. ~ lib/namo.rb: Namo#[]'s positive-projection branch carries @formulae minus the projected
247
- derived names naming a derived dimension materialises it (stored values, computed against
248
- the yielding Namo, windowed over any same-call selection) and drops the formula; omitted
249
- formulae carry live and recompute from the result's own rows. Contraction and selection-only
250
- calls are unchanged. The projection list is the materialise/live selector.
251
- 2. ~ lib/namo.rb: Namo#* and Namo#** raise ArgumentError via the new private
252
- raise_unless_data_formula_exclusivity when one operand's data dimension is the other's
253
- derived dimension, block and no-block forms alike. Formula-vs-formula stays left-wins; the
254
- set operators need no guard (matching-data-dimensions blocks the asymmetric case); the
255
- constructor stays unguarded.
256
- 3. ~ test/namo_test.rb: + "data/formula exclusivity" describe — access-path agreement on a
257
- materialised dimension, dimension-listing, dependent-formula carry, omitted-formula
258
- liveness, live-without-inputs caveat, two-arity windowing at materialisation,
259
- contraction/selection unchanged, subclass type, composition collision raises (both
260
- directions, both operators, block forms), left-wins formula merge, contraction-first
261
- resolution.
262
- 4. ~ test/namo_test.rb: + "range selection" context under #[] — basic range, beginless and
263
- endless forms, range composed with projection, range on a formula-defined dimension
264
- (Row_test holds the predicate matrix; these pin the Namo-level wiring).
265
- 5. ~ README.md: + Projection of derived dimensions subsection under Formulae (naming
266
- materialises, omitting carries live); data/formula collision sentences under Composition
267
- and Cartesian product.
268
- 6. ~ ROADMAP.md: Promote 0.16.0 to shipped; Current state -> 0.16.0; Summary folds in
269
- exclusivity; next phase -> 0.17.0. window.length -> window.count and n.length -> n.count
270
- in the remaining future-release examples (0.17.0, 2.x, 4.x) — Namo has no #length,
271
- extending the 0.15.0 correction.
272
- 7. ~ COMPARISON.md: Repoint the pre-renumbering planned markers to what shipped — proc-based,
273
- regex-based, and mixed selection -> shipped (0.8.0); Enumerable methods return Namos ->
274
- shipped (0.11.0), with the entry summary's parity sentence and the Sorting entry's
275
- "as of 0.14.0" corrected to 0.11.0; values and to_h -> shipped (0.7.0). Aspect classes ->
276
- not planned, the entry rewritten to record 0.7.0's plain-return-types decision (Namo#===
277
- and subclassing cover case dispatch; a Matcher factory can serve a finer split later).
278
- Aggregation repointed from 2.x to group_by returning a Namo::Collection at 0.19.0 (gated
279
- on Collection at 0.18.0), with summary/members examples; bare names stay 2.x. Parameterised
280
- formulae stays planned (0.17.0), its example's window.length -> window.count.
281
- 8. ~ EXAMPLES.md: + Epidemiology / public health section — a cross-row (two-arity) rolling
282
- weekly average in the four-stage format (Polars, then Namo 1.x/2.x/3.x), with the
283
- (row, namo) window over the yielding Namo and a one-arity formula referencing the
284
- two-arity one. window.length -> window.count in the finance 1.x and 2.x stages. Date
285
- bumped to 20260612.
133
+ 1. ~ lib/namo.rb: Namo#[]'s positive-projection branch carries @formulae minus the projected derived names — naming a derived dimension materialises it (stored values, computed against the yielding Namo, windowed over any same-call selection) and drops the formula; omitted formulae carry live and recompute from the result's own rows. Contraction and selection-only calls are unchanged. The projection list is the materialise/live selector.
134
+ 2. ~ lib/namo.rb: Namo#* and Namo#** raise ArgumentError via the new private raise_unless_data_formula_exclusivity when one operand's data dimension is the other's derived dimension, block and no-block forms alike. Formula-vs-formula stays left-wins; the set operators need no guard (matching-data-dimensions blocks the asymmetric case); the constructor stays unguarded.
135
+ 3. ~ test/namo_test.rb: + "data/formula exclusivity" describe — access-path agreement on a materialised dimension, dimension-listing, dependent-formula carry, omitted-formula liveness, live-without-inputs caveat, two-arity windowing at materialisation, contraction/selection unchanged, subclass type, composition collision raises (both directions, both operators, block forms), left-wins formula merge, contraction-first resolution.
136
+ 4. ~ test/namo_test.rb: + "range selection" context under #[] — basic range, beginless and endless forms, range composed with projection, range on a formula-defined dimension (Row_test holds the predicate matrix; these pin the Namo-level wiring).
137
+ 5. ~ README.md: + Projection of derived dimensions subsection under Formulae (naming materialises, omitting carries live); data/formula collision sentences under Composition and Cartesian product.
138
+ 6. ~ ROADMAP.md: Promote 0.16.0 to shipped; Current state -> 0.16.0; Summary folds in exclusivity; next phase -> 0.17.0. window.length -> window.count and n.length -> n.count in the remaining future-release examples (0.17.0, 2.x, 4.x) — Namo has no #length, extending the 0.15.0 correction.
139
+ 7. ~ COMPARISON.md: Repoint the pre-renumbering planned markers to what shipped — proc-based, regex-based, and mixed selection -> shipped (0.8.0); Enumerable methods return Namos -> shipped (0.11.0), with the entry summary's parity sentence and the Sorting entry's "as of 0.14.0" corrected to 0.11.0; values and to_h -> shipped (0.7.0). Aspect classes -> not planned, the entry rewritten to record 0.7.0's plain-return-types decision (Namo#=== and subclassing cover case dispatch; a Matcher factory can serve a finer split later). Aggregation repointed from 2.x to group_by returning a Namo::Collection at 0.19.0 (gated on Collection at 0.18.0), with summary/members examples; bare names stay 2.x. Parameterised formulae stays planned (0.17.0), its example's window.length -> window.count.
140
+ 8. ~ EXAMPLES.md: + Epidemiology / public health section — a cross-row (two-arity) rolling weekly average in the four-stage format (Polars, then Namo 1.x/2.x/3.x), with the (row, namo) window over the yielding Namo and a one-arity formula referencing the two-arity one. window.length -> window.count in the finance 1.x and 2.x stages. Date bumped to 20260612.
286
141
  9. ~ Namo::VERSION: /0.15.0/0.16.0/
287
142
 
288
- 20260612
143
+ ## 20260612
144
+
289
145
  0.15.0: + two-arity formulae — procs with arity 2 receive (row, namo) for cross-row computation.
290
146
 
291
- 1. ~ lib/Namo/Row.rb: Row's constructor gains an optional third parameter, namo (default nil),
292
- stored in @namo the Namo that yielded the Row; the two-argument form keeps working.
293
- Row#[] dispatches on formula arity: exactly 2 calls formula.call(self, @namo), raising
294
- ArgumentError via the new private raise_unless_namo_context when @namo is nil; every
295
- other arity (0, 1, negative) keeps the existing formula.call(self) unchanged.
296
- 2. ~ lib/Namo/Enumerable.rb: every Row construction (each, select and its aliases, reject,
297
- sort_by, first, last, take_while, drop_while, uniq's block form, partition) passes self
298
- as the Row's namo, so two-arity formulae resolve during enumeration and predicate
299
- evaluation, with the yielding Namo as the window.
300
- 3. ~ lib/namo.rb: values_for's derived branch and the * and ** block paths pass self as the
301
- Row's namo — values/coordinates/to_h resolve two-arity dimensions, and composition-block
302
- rows can resolve self's two-arity formulae (extension of the 0.14.0 block contract; the
303
- (Row, Namo) -> Namo contract and result-formulae rule are unchanged).
304
- 4. ~ test/Namo/Row_test.rb: + constructor third-argument and backward-compatibility tests,
305
- arity-dispatch tests (1, 2, 0, negative), the nil-namo ArgumentError, and match? on a
306
- two-arity derived dimension.
307
- 5. ~ test/namo_test.rb: + "#[]= two-arity formulae" describe — SMA cross-row resolution via
308
- values/coordinates/to_h, selection and subset-predicate resolution, first/last Rows,
309
- yielding-Namo (filtered-window) semantics, liveness, operator carry-through, composition-
310
- block resolution, mixed-arity chains, and the empty-Namo case.
311
- 6. ~ README.md: + Cross-row formulae subsection under Formulae, with the SMA example, the
312
- yielding-Namo semantic, and the no-context error.
313
- 7. ~ ROADMAP.md: Promote 0.15.0 to shipped; Current state -> 0.15.0; Summary folds in
314
- two-arity formulae; the example's window.length corrected to window.count (Namo has
315
- no length); "Live computation objects" phrasing updated. + upcoming 0.16.0 section,
316
- Data/formula exclusivity — projection drops the formulae it materialises, * and **
317
- raise on a data/formula name collision — with parameterised formulae renumbered to
318
- 0.17.0, Namo::Collection to 0.18.0, and group_by to 0.19.0; next phase -> 0.16.0.
319
- 8. ~ COMPARISON.md: Two-arity formulae -> shipped (0.15.0) with the same window.count
320
- correction; Parameterised formulae repointed to planned (0.17.0). Date bumped.
147
+ 1. ~ lib/Namo/Row.rb: Row's constructor gains an optional third parameter, namo (default nil), stored in @namo — the Namo that yielded the Row; the two-argument form keeps working. Row#[] dispatches on formula arity: exactly 2 calls formula.call(self, @namo), raising ArgumentError via the new private raise_unless_namo_context when @namo is nil; every other arity (0, 1, negative) keeps the existing formula.call(self) unchanged.
148
+ 2. ~ lib/Namo/Enumerable.rb: every Row construction (each, select and its aliases, reject, sort_by, first, last, take_while, drop_while, uniq's block form, partition) passes self as the Row's namo, so two-arity formulae resolve during enumeration and predicate evaluation, with the yielding Namo as the window.
149
+ 3. ~ lib/namo.rb: values_for's derived branch and the * and ** block paths pass self as the Row's namo values/coordinates/to_h resolve two-arity dimensions, and composition-block rows can resolve self's two-arity formulae (extension of the 0.14.0 block contract; the (Row, Namo) -> Namo contract and result-formulae rule are unchanged).
150
+ 4. ~ test/Namo/Row_test.rb: + constructor third-argument and backward-compatibility tests, arity-dispatch tests (1, 2, 0, negative), the nil-namo ArgumentError, and match? on a two-arity derived dimension.
151
+ 5. ~ test/namo_test.rb: + "#[]= two-arity formulae" describe — SMA cross-row resolution via values/coordinates/to_h, selection and subset-predicate resolution, first/last Rows, yielding-Namo (filtered-window) semantics, liveness, operator carry-through, composition- block resolution, mixed-arity chains, and the empty-Namo case.
152
+ 6. ~ README.md: + Cross-row formulae subsection under Formulae, with the SMA example, the yielding-Namo semantic, and the no-context error.
153
+ 7. ~ ROADMAP.md: Promote 0.15.0 to shipped; Current state -> 0.15.0; Summary folds in two-arity formulae; the example's window.length corrected to window.count (Namo has no length); "Live computation objects" phrasing updated. + upcoming 0.16.0 section, Data/formula exclusivity — projection drops the formulae it materialises, * and ** raise on a data/formula name collision — with parameterised formulae renumbered to 0.17.0, Namo::Collection to 0.18.0, and group_by to 0.19.0; next phase -> 0.16.0.
154
+ 8. ~ COMPARISON.md: Two-arity formulae -> shipped (0.15.0) with the same window.count correction; Parameterised formulae repointed to planned (0.17.0). Date bumped.
321
155
  9. ~ Namo::VERSION: /0.14.0/0.15.0/
322
156
 
323
- 20260608
157
+ ## 20260608
158
+
324
159
  0.14.0: + blocks on the composition operators (*, **) for custom match refinement.
325
160
 
326
- 1. ~ lib/namo.rb: Namo#* takes an optional block. Without a block, behaviour is unchanged.
327
- With a block, the right rows matched on the shared data dimensions are passed as a Namo
328
- (candidates, carrying other's formulae) alongside the left Row (carrying self's
329
- formulae); the block returns a Namo of the rows to pair, each merged into the left row.
330
- An empty returned Namo drops the left row (inner-join semantics). Result formulae are
331
- other.formulae.merge(@formulae) as before — the block does not affect result formulae.
332
- The shared-dimension precondition still applies with a block present.
333
- 2. ~ lib/namo.rb: Namo#** takes an optional block, same contract, with candidates being all
334
- of other's rows (no shared-dimension pre-filter). The disjoint-dimensions precondition
335
- still applies with a block present.
336
- 3. ~ test/namo_test.rb: + "with a block" context under #* (single-match one-row return, empty-return
337
- drop, multi-row return, selection on other's derived dimension, reference to self's
338
- derived dimension, result-formulae parity with no-block, derived-not-stored-but-resolves,
339
- subclass type, preconditions still raise) and under #** (selector filtering one-to-many,
340
- empty-return drop, full-candidates reproduces no-block, selection on other's derived
341
- dimension, result-formulae parity, disjoint precondition still raises, subclass type).
342
- Existing no-block #* and #** tests unchanged and green.
343
- 4. ~ README.md: + block subsections under Composition and Cartesian product, with the
344
- price/quarterly matching and orders/tiers worked examples and the (row, candidates) ->
345
- Namo contract.
346
- 5. ~ ROADMAP.md: Promote 0.14.0 to shipped (composition blocks only); Current state -> 0.14.0;
347
- Summary folds in composition blocks; next phase -> 0.15.0. + the governing principle
348
- (a block form is warranted iff the operation gives consideration to a dimension in
349
- isolation), tied to the orthogonality/efficiency rationale for why set-operator/`/`
350
- blocks were not added. The now-redundant upcoming 0.14.0 section removed.
161
+ 1. ~ lib/namo.rb: Namo#* takes an optional block. Without a block, behaviour is unchanged. With a block, the right rows matched on the shared data dimensions are passed as a Namo (candidates, carrying other's formulae) alongside the left Row (carrying self's formulae); the block returns a Namo of the rows to pair, each merged into the left row. An empty returned Namo drops the left row (inner-join semantics). Result formulae are other.formulae.merge(@formulae) as before — the block does not affect result formulae. The shared-dimension precondition still applies with a block present.
162
+ 2. ~ lib/namo.rb: Namo#** takes an optional block, same contract, with candidates being all of other's rows (no shared-dimension pre-filter). The disjoint-dimensions precondition still applies with a block present.
163
+ 3. ~ test/namo_test.rb: + "with a block" context under #* (single-match one-row return, empty-return drop, multi-row return, selection on other's derived dimension, reference to self's derived dimension, result-formulae parity with no-block, derived-not-stored-but-resolves, subclass type, preconditions still raise) and under #** (selector filtering one-to-many, empty-return drop, full-candidates reproduces no-block, selection on other's derived dimension, result-formulae parity, disjoint precondition still raises, subclass type). Existing no-block #* and #** tests unchanged and green.
164
+ 4. ~ README.md: + block subsections under Composition and Cartesian product, with the price/quarterly matching and orders/tiers worked examples and the (row, candidates) -> Namo contract.
165
+ 5. ~ ROADMAP.md: Promote 0.14.0 to shipped (composition blocks only); Current state -> 0.14.0; Summary folds in composition blocks; next phase -> 0.15.0. + the governing principle (a block form is warranted iff the operation gives consideration to a dimension in isolation), tied to the orthogonality/efficiency rationale for why set-operator/`/` blocks were not added. The now-redundant upcoming 0.14.0 section removed.
351
166
  6. ~ COMPARISON.md: "Conditional join with block" -> shipped (0.14.0).
352
167
  7. ~ Namo::VERSION: /0.13.2/0.14.0/
353
168
 
354
- 20260608
169
+ ## 20260608
170
+
355
171
  0.13.2: Narrow the planned 0.14.0 block-form scope to the composition operators and document the rationale.
356
172
 
357
- 1. ~ ROADMAP.md: Rewrite the 0.14.0 section from "blocks on comparison, composition, and
358
- set operators" to "blocks on composition operators (*, **)". State the governing
359
- principle (a block belongs only where the operator's matching is underdetermined).
360
- Remove the comparison-operator and set-operator block subsections; note that their use
361
- cases are expressed by comparing projections and by projection + proc selection
362
- respectively. State explicitly that / takes no block. Replace "The unifying pattern"
363
- with the composition-only rationale.
173
+ 1. ~ ROADMAP.md: Rewrite the 0.14.0 section from "blocks on comparison, composition, and set operators" to "blocks on composition operators (*, **)". State the governing principle (a block belongs only where the operator's matching is underdetermined). Remove the comparison-operator and set-operator block subsections; note that their use cases are expressed by comparing projections and by projection + proc selection respectively. State explicitly that / takes no block. Replace "The unifying pattern" with the composition-only rationale.
364
174
  2. ~ ROADMAP.md: Rescope the Summary's "next phase" pointer to composition-only block forms.
365
- 3. ~ COMPARISON.md: Replace the "Blocks on set operators" subsection (feature no longer
366
- planned) with a shipped "Keyed anti-join" entry expressing keyed exclusion via projection
367
- + proc selection; repoint the composition "Conditional join with block" entry to planned
368
- (0.14.0). Date bumped to 20260608.
175
+ 3. ~ COMPARISON.md: Replace the "Blocks on set operators" subsection (feature no longer planned) with a shipped "Keyed anti-join" entry expressing keyed exclusion via projection + proc selection; repoint the composition "Conditional join with block" entry to planned (0.14.0). Date bumped to 20260608.
369
176
  4. ~ Namo::VERSION: /0.13.1/0.13.2/
370
177
 
371
- 20260604
178
+ ## 20260604
179
+
372
180
  0.13.1: Correct the row-order claim across the docs and document the same-dimension-order precondition.
373
181
 
374
- 1. ~ README.md: Scope the "row order is ignored" statement to the comparison operators
375
- (==, eql?, ===, <, <=, >, >=) in the Equality section; drop "accident of ingestion".
376
- + Positive note that row order is preserved through to_h/values, each, first, last,
377
- take, drop, and +.
378
- 2. ~ README.md: At Concatenation, state that the set operators require the same dimension
379
- names in the same order, with guidance to normalise inputs to a common column order
380
- before combining.
381
- 3. ~ ROADMAP.md: Scope the 0.6.0 section's "row order is an accident of ingestion" to the
382
- comparison operators; drop "accident of ingestion". Date bumped to 20260604.
182
+ 1. ~ README.md: Scope the "row order is ignored" statement to the comparison operators (==, eql?, ===, <, <=, >, >=) in the Equality section; drop "accident of ingestion". + Positive note that row order is preserved through to_h/values, each, first, last, take, drop, and +.
183
+ 2. ~ README.md: At Concatenation, state that the set operators require the same dimension names in the same order, with guidance to normalise inputs to a common column order before combining.
184
+ 3. ~ ROADMAP.md: Scope the 0.6.0 section's "row order is an accident of ingestion" to the comparison operators; drop "accident of ingestion". Date bumped to 20260604.
383
185
  4. ~ Namo::VERSION: /0.13.0/0.13.1/
384
186
 
385
- 20260601
187
+ ## 20260601
188
+
386
189
  0.13.0: Polymorphic []= — proc registers a formula, scalar broadcasts to every row.
387
190
 
388
191
  1. ~ lib/namo.rb: Namo#[]= now dispatches on value type. Parameter renamed `proc` -> `value`. A Proc registers a formula and clears any data column of the same name (`@data.each{|row| row.delete(name)} if @data.first&.key?(name)`); anything else broadcasts the value to every row and clears any formula of the same name (`@formulae.delete(name)`). Enforces the exclusive-storage invariant — a name is data or derived, never both — at the assignment site. Existing proc-registration behaviour preserved.
@@ -391,7 +194,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
391
194
  4. ~ ROADMAP.md: Promote 0.13.0 from upcoming to shipped — `## Current state` bumped to 0.13.0, a `### 0.13.0 (2026-06-01)` entry added in the shipped section, the now-redundant `## 0.13.0` upcoming section removed, polymorphic []= folded into the Summary's completed vocabulary, "next phase" pointed at 0.14.0+.
392
195
  5. ~ Namo::VERSION: /0.12.0/0.13.0/
393
196
 
394
- 20260601
197
+ ## 20260601
198
+
395
199
  0.12.0: Constructor widening — keyword data: and name:.
396
200
 
397
201
  1. ~ lib/namo.rb: Namo#initialize widened from `(data = [], formulae: {})` to `(positional_data = nil, data: [], formulae: {}, name: nil)`. Data may now be passed positionally or by the `data:` keyword; positional wins when both are given (`@data = positional_data || data`). The positional default changes from `[]` to `nil` so it acts as a "not supplied" sentinel that lets `|| data` fall through to the keyword path; an explicit `Namo.new([])` still yields `@data == []` because the truthy empty array short-circuits. + `name:` keyword, stored in `@name`. Every pre-existing call site (positional data, positional data + keyword formulae, no-arg, formulae-only, and the operators' internal `self.class.new(rows, formulae: ...)`) is unaffected.
@@ -401,7 +205,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
401
205
  5. ~ ROADMAP.md: Promote 0.12.0 from upcoming to shipped — `## Current state` bumped to 0.12.0, a `### 0.12.0 (2026-06-01)` entry added in the shipped section (written in shipped voice), and the now-redundant `## 0.12.0` upcoming section removed; fold the widened constructor into the Summary's completed vocabulary and point "next phase" at 0.13.0+.
402
206
  6. ~ Namo::VERSION: /0.11.1/0.12.0/
403
207
 
404
- 20260601
208
+ ## 20260601
209
+
405
210
  0.11.1: Extract the subset Enumerable methods into a Namo::Enumerable module.
406
211
 
407
212
  1. + lib/Namo/Enumerable.rb: New module Namo::Enumerable holding each and the subset-returning Enumerable methods (select plus its filter/find_all aliases, reject, sort_by, first, last, take, drop, take_while, drop_while, uniq, partition), moved verbatim from Namo. The module does `include ::Enumerable` — the leading :: is required, because under the nested `class Namo; module Enumerable` scope a bare `Enumerable` resolves to Namo::Enumerable itself and raises a cyclic-include error.
@@ -410,7 +215,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
410
215
  4. ~ ROADMAP.md: + note in the 0.11.0 section that, as of 0.11.1, these methods live in the Namo::Enumerable module; bump Date to 20260601.
411
216
  5. ~ Namo::VERSION: /0.11.0/0.11.1/
412
217
 
413
- 20260531
218
+ ## 20260531
219
+
414
220
  0.11.0: ~ Subset Enumerable methods (select, reject, sort_by, first, last, take, drop, take_while, drop_while, uniq, partition) return Namos
415
221
 
416
222
  1. + Namo#select, Namo#reject, Namo#sort_by, Namo#take_while, Namo#drop_while: Predicate and ordering subset methods that wrap the @data Array result in `self.class.new(..., formulae: @formulae.dup)`, shadowing Enumerable's Array-returning defaults. Blocks receive a Row, so formulae resolve inside the predicate, and formulae carry through to the returned Namo. Namo#select is aliased as filter and find_all (the only overridden method with Enumerable aliases), so the aliases return Namos too.
@@ -423,7 +229,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
423
229
  8. ~ ROADMAP.md: Promote 0.11.0 from upcoming to shipped under "Current state: 0.11.0"; fold the subset Enumerable methods into the Summary's completed vocabulary and point "next phase" at 0.12.0+; bump Date to 20260531.
424
230
  9. ~ Namo::VERSION: /0.10.0/0.11.0/
425
231
 
426
- 20260528
232
+ ## 20260528
233
+
427
234
  0.10.0: + Row value semantics: ==, eql?, hash
428
235
 
429
236
  1. + Namo::Row#==: Data equality. Two Rows are equal iff the other is a Row and their `@row` hashes are `==`. Class identity not gated (Row has no subclass hierarchy with included modules); formulae ignored (a formula is a property of the surrounding Namo, not of the row's identity). Mirrors `Namo#==` one level down.
@@ -434,13 +241,15 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
434
241
  6. ~ ROADMAP.md: Promote 0.10.0 from upcoming to shipped under "Current state: 0.10.0"; revise Summary to point "next phase" at 0.11.0+ and note Row value semantics among the algebra-completing pieces.
435
242
  7. ~ Namo::VERSION: /0.9.2/0.10.0/
436
243
 
437
- 20260525
244
+ ## 20260525
245
+
438
246
  0.9.2: ~ Auto-load Namo::VERSION
439
247
 
440
248
  1. ~ lib/namo.rb: + `require_relative './Namo/VERSION'`. Namo::VERSION is now defined when `namo` is required, without needing a separate require.
441
249
  2. ~ Namo::VERSION: /0.9.1/0.9.2/
442
250
 
443
- 20260525
251
+ ## 20260525
252
+
444
253
  0.9.1: ~ Namo#initialize: default data to [], ~ Namo#data_dimensions to handle empty data
445
254
 
446
255
  1. ~ Namo#initialize: Default `data` parameter changed from `nil` to `[]`. Construction with no arguments now produces a usable empty Namo rather than one whose introspection methods crash on `nil`.
@@ -449,7 +258,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
449
258
  4. ~ test/namo_test.rb: + Empty-Namo tests (dimensions, data_dimensions, formula surfacing on a Namo with no data).
450
259
  5. ~ Namo::VERSION: /0.9.0/0.9.1/
451
260
 
452
- 20260521
261
+ ## 20260521
262
+
453
263
  0.9.0: + composition operators: equi-join (*), Cartesian product (**), decomposition (/)
454
264
 
455
265
  1. + Namo#*: Equi-join on shared data dimensions. Inner-join semantics — unmatched rows from both sides are dropped. Raises ArgumentError ("no shared dimensions, need to have shared dimensions") when operands have no overlap. Preserves duplicates multiplicatively. Formulae merge with self winning on conflict.
@@ -462,7 +272,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
462
272
  8. ~ COMPARISON.md: /planned (0.9.0)/shipped (0.9.0)/ for Equi-join, Cartesian product, and Decomposition. + Paragraph in the Decomposition entry on the combining-vs-projecting distinction. Date bumped to 20260521.
463
273
  9. ~ Namo::VERSION: /0.8.0/0.9.0/
464
274
 
465
- 20260521
275
+ ## 20260521
276
+
466
277
  0.8.0: + proc and regex-based selection
467
278
 
468
279
  1. ~ Namo::Row#match?: + `when Proc` branch — calls the proc with the dimension value; truthy result selects the row. Predicate receives nil for missing or nil-valued dimensions and decides.
@@ -474,7 +285,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
474
285
  7. ~ CHANGELOG: Update with 0.8.0's changes and retroactively log the 0.7.0 changes to README (+ Coordinates and values section), COMPARISON.md, and EXAMPLES.md (2 sections relocated from EXAMPLES.md to COMPARISON.md).
475
286
  8. ~ Namo::VERSION: /0.7.0/0.8.0/
476
287
 
477
- 20260520
288
+ ## 20260520
289
+
478
290
  0.7.0: + derived-dimension surfacing, lazy single-column access, live views
479
291
 
480
292
  1. + Namo#data_dimensions: Returns the storage dimensions as a plain Array (keys of the first row).
@@ -494,7 +306,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
494
306
  15. ~ Rakefile: + -V mainfont=Charter -V monofont=Menlo on pandoc invocation in docs:md2pdf, for a cleaner serif body font and so code spans containing Unicode math glyphs (e.g. ∅) render correctly under xelatex.
495
307
  16. ~ Namo::VERSION: /0.6.0/0.7.0/
496
308
 
497
- 20260511
309
+ ## 20260511
310
+
498
311
  0.6.0: + equality, pattern-match, and subset/superset operators
499
312
 
500
313
  1. + Namo#==: Multiset equality on row data, ignoring class and formulae.
@@ -509,7 +322,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
509
322
  10. + script/md4print, ~ Rakefile: + rake docs:print, docs:pdf, docs:all for regenerating docs/*.print.md and docs/*.print.pdf.
510
323
  11. ~ Namo::VERSION: /0.5.0/0.6.0/
511
324
 
512
- 20260416
325
+ ## 20260416
326
+
513
327
  0.5.0: + row-axis set operations: intersection (&), union (|), symmetric difference (^)
514
328
 
515
329
  1. + Namo#&: Intersection. Returns rows present in both Namo objects. Requires matching dimensions.
@@ -519,7 +333,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
519
333
  5. ~ README.md: + Intersection section, + Union section, + Symmetric difference section.
520
334
  6. ~ Namo::VERSION: /0.4.0/0.5.0/
521
335
 
522
- 20260415
336
+ ## 20260415
337
+
523
338
  0.4.0: + concatenation (+) and row removal (-)
524
339
 
525
340
  1. + Namo#+: Concatenate two Namo instances with matching dimensions. Appends rows from the second to the first. Raises ArgumentError when dimensions differ.
@@ -528,7 +343,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
528
343
  4. ~ README.md: + Concatenation section, + Row removal section.
529
344
  5. ~ Namo::VERSION: /0.3.0/0.4.0/
530
345
 
531
- 20260415
346
+ ## 20260415
347
+
532
348
  0.3.0: + contraction
533
349
 
534
350
  1. + Namo::NegatedDimension: Wrapper for negated dimension names used in contraction.
@@ -539,7 +355,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
539
355
  6. ~ README.md: + Contraction section
540
356
  7. ~ Namo::VERSION: /0.2.0/0.3.0/
541
357
 
542
- 20260414
358
+ ## 20260414
359
+
543
360
  0.2.0: Include Enumerable.
544
361
 
545
362
  1. ~ Namo: include Enumerable
@@ -552,7 +369,8 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
552
369
  8. ~ README.md: + Enumerable section
553
370
  9. ~ Namo::VERSION: /0.1.0/0.2.0/; /module/class/ to match Namo's definition.
554
371
 
555
- 20260328
372
+ ## 20260328
373
+
556
374
  0.1.0: Add formulae via []=. Formulae are procs which receive a Row object, compose via named references, and carry through selection.
557
375
 
558
376
  1. ~ Namo#[]: Refactor to accept *names for projection alongside **selections. Always returns a Namo instance.
@@ -565,14 +383,16 @@ A base Namo's constituents are formularies and rows: << dispatches a Module to a
565
383
  8. ~ namo_test.rb: Add tests for projection, formulae, composition, chained and single-call selection with projection.
566
384
  9. ~ Namo::VERSION: /0.0.1/0.1.0/
567
385
 
568
- 20260315
386
+ ## 20260315
387
+
569
388
  0.0.1: Fix README.md and CHANGELOG.
570
389
 
571
390
  1. ~ README.md: Fix Contributing secton.
572
391
  2. ~ CHANGELOG: Use the same description in the summary as in the commit message.
573
392
  3. ~ Namo::VERSION: /0.0.0/0.0.1/
574
393
 
575
- 20260315
394
+ ## 20260315
395
+
576
396
  0.0.0: The initial release has instantiation from hashes, automatic inferral of dimension names and co-ordinates, and selection of data by a single value, an array, or a range.
577
397
 
578
398
  1. + Namo
data/lib/Namo/VERSION.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # Namo::VERSION
3
3
 
4
4
  class Namo
5
- VERSION = '0.25.0'
5
+ VERSION = '0.25.1'
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: namo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.25.0
4
+ version: 0.25.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - thoran