namo.rb 0.31.7
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.
- checksums.yaml +7 -0
- data/CHANGELOG +566 -0
- data/Gemfile +5 -0
- data/LICENSE +21 -0
- data/README.md +1268 -0
- data/Rakefile +42 -0
- data/lib/Namo/Collection.rb +130 -0
- data/lib/Namo/Enumerable.rb +83 -0
- data/lib/Namo/Formulae.rb +137 -0
- data/lib/Namo/Formulary.rb +6 -0
- data/lib/Namo/NegatedDimension.rb +14 -0
- data/lib/Namo/Row.rb +93 -0
- data/lib/Namo/VERSION.rb +6 -0
- data/lib/Symbol.rb +8 -0
- data/lib/namo.rb +433 -0
- data/namo.rb.gemspec +42 -0
- data/test/Namo/Collection_test.rb +548 -0
- data/test/Namo/Formulae_test.rb +458 -0
- data/test/Namo/NegatedDimension_test.rb +13 -0
- data/test/Namo/Row_test.rb +487 -0
- data/test/Symbol_test.rb +16 -0
- data/test/console_test.rb +44 -0
- data/test/demo_test.rb +86 -0
- data/test/namo_test.rb +3328 -0
- data/test/sizing_test.rb +40 -0
- metadata +107 -0
data/CHANGELOG
ADDED
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
## 20260827
|
|
4
|
+
|
|
5
|
+
0.31.7: + namo.rb.gemspec, so that the gem resolves under both names.
|
|
6
|
+
|
|
7
|
+
1. + namo.rb.gemspec
|
|
8
|
+
2. ~ Gemfile: /gemspec/gemspec name: 'namo'/, bundler refusing to parse a Gemfile whose bare gemspec directive finds more than one gemspec to choose from.
|
|
9
|
+
3. ~ namo.gemspec: + the file's own name as a header comment, which namo.rb.gemspec carries and which every other file here does.
|
|
10
|
+
4. ~ Namo::VERSION: /0.31.6/0.31.7/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## 20260826
|
|
14
|
+
|
|
15
|
+
0.31.6: ~ Namo::Collection: a row operation yields a Namo rather than a memberless Collection.
|
|
16
|
+
|
|
17
|
+
1. + Namo#return_class: the class an operation returns, protected, returning self.class. The twenty-two sites which built what they return — projection and contraction, the five set operators, the two composition operators, and the subset Enumerable methods in Namo::Enumerable — now ask for it rather than naming self.class themselves.
|
|
18
|
+
2. ~ Namo::Collection#return_class: returns Namo. A Collection's kind is its members and a row operation carries none across, so what came back was a Collection with none: it answered summary with an empty Namo rather than raising, and rendered as [] beside a row count. 0.18.0 has these operations read the detail view and behave as the detail, and the detail is a Namo; this is the code meeting that sentence.
|
|
19
|
+
3. ~ README.md: the four views, on what a row operation on a Collection returns.
|
|
20
|
+
4. ~ ROADMAP.md: + 0.31.6.
|
|
21
|
+
5. ~ Namo::VERSION: /0.31.5/0.31.6/
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
0.31.5: ~ Namo::Collection#detail, #as_detail: the detail view carries the members' formulae.
|
|
25
|
+
|
|
26
|
+
1. ~ Namo::Collection#detail: the returned Namo carries the members' formulae, folded in member order so that a later member wins a name collision, as a later member wins a collision of names in <<. It had been built with none, so the view documented at 0.18.0 as the lossless union of the members' rows dropped every derived dimension: group_by hands each member the source's formulae, and detail handed them back stripped.
|
|
27
|
+
2. ~ Namo::Collection#as_detail: merges the detail view's formulae into the Collection's own, the Collection having become that view. Without it the round trip still lost them, as_detail having taken only the data.
|
|
28
|
+
3. ~ script/demo: the collections section checks the round trip with === rather than ==, == comparing row multisets alone and so unable to see the formulae go missing, which is why this stood.
|
|
29
|
+
4. ~ README.md: the four views, on which of them carries formulae and why.
|
|
30
|
+
5. ~ ROADMAP.md: + 0.31.5.
|
|
31
|
+
6. ~ Namo::VERSION: /0.31.4/0.31.5/
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
0.31.4: ~ Namo::Collection#inspect: the members are rendered, nested under their names.
|
|
35
|
+
|
|
36
|
+
1. ~ Namo::Collection#inspect: the members are rendered nested under their names rather than listed as bare labels beside a row count, the members being the substance and the data view derived from them. The Namo::INSPECTED_ROWS budget is spread across the members shown — one member renders ten of its rows, five render two apiece, ten render one each and are put on one line — and members beyond it are elided with a count, as rows are. The member list had no cap at all: 2,641 members emitted 22,704 characters on a single line, which is the failure INSPECTED_ROWS was introduced at 0.31.0 to end. A member renders nested as it renders alone, its derived values among its stored ones and its own derived: suffix naming whatever none of its rendered rows could materialise. The detail view's row count closes the block, and a Collection's own formulae are named after it, being a namespace its members cannot resolve.
|
|
37
|
+
2. + Namo#inspected_data: the rendered rows, the count of those not shown, and the derived dimensions none of them carries — protected, so Namo::Collection can render its members' rows without going through Namo#inspect and repeating each member's name, and still report what a member reports of itself.
|
|
38
|
+
3. ~ README.md: the Inspection output section, and the two group_by examples whose rendering changes.
|
|
39
|
+
4. ~ ROADMAP.md: + 0.31.4.
|
|
40
|
+
5. ~ Namo::VERSION: /0.31.3/0.31.4/
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## 20260824
|
|
44
|
+
|
|
45
|
+
0.31.3: ~ Namo#to_a: derived dimensions are carried alongside the stored ones.
|
|
46
|
+
|
|
47
|
+
1. ~ Namo#to_a: each row carries the derived dimensions which can be materialised without arguments, in among its stored keys, so to_a agrees with to_h and with dimensions about what the Namo holds. A formula wanting arguments is omitted, the rule values, coordinates and to_h already follow. Namo#data is unchanged and remains the stored rows; Namo::Row#to_h is unchanged, Row#==, Row#eql? and Namo#<< all reading through it.
|
|
48
|
+
2. ~ README.md: the Extracting data section, and the parameterised-formula rule which now names to_a.
|
|
49
|
+
3. ~ ROADMAP.md: + 0.31.3; the open question on which conversions materialise closes for to_a and stays open for Namo::Row.
|
|
50
|
+
4. ~ Namo::VERSION: /0.31.2/0.31.3/
|
|
51
|
+
|
|
52
|
+
Rebuilding a Namo from to_a now carries the derived dimensions in as stored data. Namo.new(other.to_a) followed by attaching the formulary which produced them raises, the 0.24.1 collision guard refusing the name twice over; []= accepts it, evicting the data. Where the stored rows are what is meant, data is the accessor for them. A formula whose inputs a projection has cut raises through to_a as it has always raised through to_h and values, which 0.16.0 settled as the caller's own choice.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
0.31.2: ~ Namo#inspect: the derived: suffix names only what the rows do not carry.
|
|
56
|
+
|
|
57
|
+
1. ~ Namo#inspect: the derived: suffix names only the derived dimensions the rendered rows do not already carry, and is omitted where they all do. A formula which raises, a parameterised one which cannot be materialised without its arguments, and any formula on a Namo with no rows to render, are named as before, being nowhere else in the output. Namo::Collection renders no rows and so names every one of its own. Namo::Row is unchanged, naming its formulae in every case.
|
|
58
|
+
2. ~ README.md: the Inspection output section, and the block whose suffix goes.
|
|
59
|
+
3. ~ ROADMAP.md: + 0.31.2.
|
|
60
|
+
4. ~ Namo::VERSION: /0.31.1/0.31.2/
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
0.31.1: ~ Namo#inspect, Namo::Row#inspect: derived dimensions render their values.
|
|
64
|
+
|
|
65
|
+
1. ~ Namo#inspect: a derived dimension renders with its value, in among the stored ones, rather than being named only. Where there is no value to show it is omitted and the derived: suffix names it alone: a formula which raises, and a parameterised one which cannot be materialised without its arguments.
|
|
66
|
+
2. ~ Namo::Row#inspect: the same.
|
|
67
|
+
3. ~ README.md: the Inspection output section rewritten, and the two blocks whose rendering changes.
|
|
68
|
+
4. ~ ROADMAP.md: + 0.31.1.
|
|
69
|
+
5. ~ Namo::VERSION: /0.31.0/0.31.1/
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## 20260822
|
|
73
|
+
|
|
74
|
+
0.31.0: + Namo#inspect, Namo::Collection#inspect, Namo::Row#inspect, and Namo::Formulae#inspect.
|
|
75
|
+
|
|
76
|
+
1. + Namo#inspect: the class, the name where there is one, the stored rows, and the derived dimension names — the form the README has shown since 0.0.0 and the code has never produced. Rows beyond Namo::INSPECTED_ROWS are elided with a count of what is left, so the output no longer grows with the data.
|
|
77
|
+
2. + Namo::Row#inspect: the row and its formula names. It had been rendering @namo, so asking a 1,000-row Namo for one row emitted more output than inspecting the whole Namo.
|
|
78
|
+
3. + Namo::Collection#inspect: the member names and the row count, the members being the substance and the data view derived from them.
|
|
79
|
+
4. + Namo::Formulae#inspect: the formula names, never the callables.
|
|
80
|
+
5. + README.md: an Inspection output section, covering the rendered form, the naming of derived dimensions, and the row cap.
|
|
81
|
+
6. ~ README.md: the inspect-output blocks requoted from single to double quotes, as Hash#inspect emits, and the two projections that carry formulae given their derived: suffix. The blocks had been hand-written before there was an inspect to copy from.
|
|
82
|
+
7. ~ Namo::VERSION: /0.30.1/0.31.0/
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
## 20260819
|
|
86
|
+
|
|
87
|
+
0.30.1: ~ namo.gemspec: spec.files reordered, the globs first.
|
|
88
|
+
|
|
89
|
+
1. ~ namo.gemspec: spec.files reordered, the two Dir globs first and the loose files alphabetically after them, as moby and measurand have it. The globs are what the gem is and the rest is paperwork, so adding a file to the paperwork now never disturbs them. This gemspec had been pinned first with the globs last, which http shared and impuri left at its 0.12.1. The same 21 files ship, in the same gem; only the order in the source changes.
|
|
90
|
+
2. ~ Namo::VERSION: /0.30.0/0.30.1/
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
## 20260715
|
|
94
|
+
|
|
95
|
+
0.30.0: Assemble Collection objects in a single pass when doing Namo::Enumerable#group_by.
|
|
96
|
+
|
|
97
|
+
Appending one member per group re-materialised Collection#<<'s @data = detail.data over all members-so-far g times; group_by now appends all the members in one << operation and materialises once, causing it be performed in O(rows) instead of O(rows·groups).
|
|
98
|
+
|
|
99
|
+
1. ~ Namo::Enumerable#group_by: map the groups to members, then append them all in one collection << members, rather than << per group — each of which rebuilt @data over every member-so-far.
|
|
100
|
+
2. ~ test/namo_test.rb: + a single-pass assembly pin under #group_by — the assembly materialises the detail once whatever the number of groups (not once per group).
|
|
101
|
+
3. ~ ROADMAP.md: + the 0.30.0 entry; current-state bumped to 0.30.0.
|
|
102
|
+
4. ~ docs/: regenerate the print-ready PDFs.
|
|
103
|
+
5. ~ Namo::VERSION: /0.29.0/0.30.0/
|
|
104
|
+
|
|
105
|
+
## 20260715
|
|
106
|
+
|
|
107
|
+
0.29.0: Per access indexed materialisation.
|
|
108
|
+
|
|
109
|
+
Materialise each column once per top-level access, collapsing an O(n²) parent-scanning formula to O(n), with no value retained across accesses.
|
|
110
|
+
|
|
111
|
+
1. ~ Namo#values: wrap the body in a private materialising scope.
|
|
112
|
+
2. ~ Namo#values_for: memoise its column build into @materialised[dim] ||= (the data/derived branch that was its body).
|
|
113
|
+
3. + Namo#materialising (a per-access scope that sets @materialised, yields, and clears it in an ensure; a nested values call reuses the open scope rather than starting another). A formula that scans its parent asks for the same column once per row it visits; the scope materialises that column once and reuses it for the rest of the access, then discards it — so every access still recomputes from current state and nothing can go stale. Enumerable is untouched: iteration paths (each/select/map/group_by) do not yet open the scope.
|
|
114
|
+
4. ~ test/namo_test.rb: + an "access-scoped materialisation" describe — a referenced column materialises once per access, not once per referencing row; nothing is retained across accesses (a later access recomputes from current state); the scope clears on a raising materialisation, leaving later accesses working.
|
|
115
|
+
5. ~ README.md: ~ the cross-row liveness note — "with no caching" becomes "with no caching across accesses", the within-access materialisation being a loop-invariant hoist, not a cache.
|
|
116
|
+
6. ~ ROADMAP.md: + the 0.29.0 entry; current-state bumped to 0.29.0; the 1.x pure-live passages refined from "no memoisation anywhere" to the invariant "no derived value outlives the access that produced it"; the Live-data-and-cache-invalidation open question gains the access-scoped resolution; + forward notes on streaming performance vs incremental maintenance (a commercial-tier candidate) and on formula opacity as the shared wall behind ===, dependency-aware projection, serialisation, and incremental streaming.
|
|
117
|
+
7. ~ docs/: regenerate the print-ready PDFs.
|
|
118
|
+
8. ~ Namo::VERSION: /0.28.0/0.29.0/
|
|
119
|
+
|
|
120
|
+
## 20260713
|
|
121
|
+
|
|
122
|
+
0.28.0: ~ Namo::Collection#detail/#as_detail: Accept the by label positionally or by keyword.
|
|
123
|
+
|
|
124
|
+
Accept the by label positionally or by keyword (positional winning), per the constructor precedent — resolving the detail/as_detail calling-convention asymmetry.
|
|
125
|
+
|
|
126
|
+
1. ~ Namo::Collection#detail(positional_by = nil, by: :member), #as_detail(positional_by = nil, by: :member): resolve by = positional_by || by, mirroring the constructor's @data = positional_data || data; positional wins. summary/as_summary unchanged — dimension stays their positional, by: the keyword modifier.
|
|
127
|
+
2. ~ test/Namo/Collection_test.rb: + detail and as_detail each take the by label positionally or by keyword, positional winning over a conflicting keyword.
|
|
128
|
+
3. ~ README.md: ~ the view-methods note — detail/as_detail take the label positionally or by keyword (the constructor's shape); replaces the "carries no dimension, so positional" rationale.
|
|
129
|
+
4. ~ ROADMAP.md: + the 0.28.0 entry (twin asymmetry, constructor precedent, primary-dual/modifier-keyword scope); current-state bumped to 0.28.0; a forward-reference on the 0.18.0 note superseding its positional rationale.
|
|
130
|
+
5. ~ docs/: regenerate the print-ready PDFs.
|
|
131
|
+
6. ~ Namo::VERSION: /0.27.0/0.28.0/
|
|
132
|
+
|
|
133
|
+
## 20260710
|
|
134
|
+
|
|
135
|
+
0.27.0: ~ Namo::Collection#summary/#as_summary: + an optional block for a per-member reduction beyond a single named reducer.
|
|
136
|
+
|
|
137
|
+
1. ~ Namo::Collection#summary(dimension = nil, by:, reducer:, &block): dimension goes optional; with a block, maps each member through block.call(member).merge(by => member.name), the member label merged last so it wins; given neither a dimension nor a block, raises ArgumentError; the no-block path is byte-identical. as_summary forwards &block to summary and persists the result, so the block form lives in one place.
|
|
138
|
+
2. ~ test/Namo/Collection_test.rb: + block-form summary returns one row per member merged with the member label; the member label wins over a by key the block returns; the neither-arg ArgumentError; the map/flat_map cardinality pin (summary one row per member, detail all rows per member); as_summary with a block sets the data and delegates. ~ the non-block custom-by test also pins the reduced value carried alongside the relabelled column.
|
|
139
|
+
3. ~ README.md: + an "A block for per-member reductions" note under the Collection view methods (the one-row-per-member contract, the member-label-wins merge, an argmax example, the neither-arg raise); + a bare-collection call-site by: example beside the class-default prose; the summary/as_summary signatures gain (dimension = nil, ..., &block).
|
|
140
|
+
4. ~ ROADMAP.md: + the 0.27.0 release entry (the block warrant from 0.14.0, summarise considered-and-refused, the pinned decisions, the transient-block serialisation-independence, the deferred group-scoped formula); current-state bumped to 0.27.0; the block form folded into the Summary vocabulary and the 1.0.0 feature list.
|
|
141
|
+
5. ~ docs/: regenerate the print-ready PDFs.
|
|
142
|
+
6. ~ Namo::VERSION: /0.26.1/0.27.0/
|
|
143
|
+
|
|
144
|
+
0.26.1: Refactor Namo::Formulae#attach through a private #bind, and document mutability and the corrected collision-resolution idioms.
|
|
145
|
+
|
|
146
|
+
1. ~ lib/Namo/Formulae.rb — attach binds each of a formulary's public method names through a new private bind(name, modul): marker guard, host.extend(modul), then self[name] = modul.instance_method(name).bind(host). attach and detach share one private raise_unless_formulary marker guard. bind stays private — attach is its only caller.
|
|
147
|
+
2. ~ README.md: + a "Mutability" section (Namos are mutable; an unfrozen Namo is not a reliable Hash key or Set member since hash tracks data and formula names; the deliberate attr_accessor :data back door and the sanctioned namo << row append idiom); + a private-helper support note under Formularies (a public formulary method resolves its private helpers through the shared host; a same-named helper in a later-attached formulary shadows the earlier one); ~ the collision-resolution passages — correct a prescribed rename operation Namo does not have to the shipped idioms (detach, formula-plus-projection, re-key before ingestion, attach!).
|
|
148
|
+
3. ~ ROADMAP.md: + a note in the deep-freeze open question — the attr_accessor :data back door means a generation counter cannot observe all mutation, deciding the 2.x caching mechanism in deep freeze's favour.
|
|
149
|
+
4. ~ test/Namo/Formulae_test.rb, test/namo_test.rb: identity assertions on stored callables migrate to behavioural ones; + a "private support methods" describe (a public formulary method resolving through its private helper; shared-host same-named-helper shadowing).
|
|
150
|
+
5. ~ docs/: regenerate the print-ready PDFs.
|
|
151
|
+
6. ~ Namo::VERSION: /0.26.0/0.26.1/
|
|
152
|
+
|
|
153
|
+
## 20260704
|
|
154
|
+
|
|
155
|
+
0.26.0: + Namo#detach, Namo::Formulae#detach — remove a formula by name or a formulary's methods by module, completing the attach/attach!/detach family.
|
|
156
|
+
|
|
157
|
+
detach becomes the sanctioned formula-removal idiom, displacing the namo.formulae.delete(:name) reach-through. It is by-name with no provenance (the store is a snapshot): the Module arm removes whatever currently holds each of the module's method names. There is no detach! (removing a formula can't collide with data, so no guard to force through) and no removal operator (<< appends; per the 0.14.0 principle, no >>).
|
|
158
|
+
|
|
159
|
+
1. + Namo::Formulae#detach(constituent) — dispatches a Symbol to @store.delete(constituent); a Module to deleting each of its public_instance_methods(false) names, after the same Namo::Formulary marker guard attach uses (an untagged module raises ArgumentError); anything else raises TypeError naming the class, in the << else-arm's style. Returns self. Placed directly after attach/<<. Formulae#delete is unchanged — it stays the store-level primitive detach sits on.
|
|
160
|
+
2. + Namo#detach(constituent) — forwards to @formulae.detach and returns self, placed with the attach family (after attach!, before <<). No guard at the Namo level: there is no data-side interaction, so the Formulae worker owns the dispatch and the raises. Collection inherits detach harmlessly.
|
|
161
|
+
3. ~ test/Namo/Formulae_test.rb: + a "#detach" describe in the formulary tier — both arms return self; the Symbol arm removes the named formula and no-ops on an absent name; the Module arm removes exactly the formulary's method names, leaves a []= name it doesn't contribute, and deletes a []= overwrite occupying a formulary-method name (the by-name/no-provenance pin); ArgumentError on an untagged module; TypeError on a String and a Hash naming the class; chaining removes both. ~ test/namo_test.rb: + a "#detach" describe under formularies — returns the Namo; removes a []= formula from the queryable namespace (derived_dimensions drops it, values returns one nil per row); leaves other formulae resolving; the Module arm empties derived_dimensions; a data dimension is a no-op (the column survives); an absent name no-ops returning the Namo; TypeError on a non-Symbol non-Module and ArgumentError on an untagged module; the #dimensions "reflects mutation" test migrates sales.formulae.delete(:revenue) to sales.detach(:revenue). ~ test/Namo/Collection_test.rb: + a "#detach" describe pinning that a Collection inherits detach — a formulary attached to the collection detaches, so the detail rows no longer resolve its name.
|
|
162
|
+
4. ~ README.md: + a "Removing a formula" note under Formulae (detach removes a formula, returns the Namo, chains, no-ops on an absent name; complements contraction across the data/derived and algebra/mutation splits; never touches @data); + a detach(Module) note in the Formularies section (the reverse of attach, by-name with no provenance, raises on an untagged module, the attach/attach!/detach family, no << removal counterpart, no detach!).
|
|
163
|
+
5. ~ ROADMAP.md: + the 0.26.0 release entry recording the settled decisions (by-name/no-provenance, marker required, loose-on-absent, derived-side-only, no-bang, no-operator, single-argument/chaining, untouched-host-ancestry); current-state bumped to 0.26.0; detach folded into the Summary's formulary vocabulary and the 1.0.0 feature list.
|
|
164
|
+
6. ~ Namo::VERSION: /0.25.1/0.26.0/
|
|
165
|
+
|
|
166
|
+
## 20260703
|
|
167
|
+
|
|
168
|
+
0.25.1: Fix documentation and reformat CHANGELOG to use Markdown headers.
|
|
169
|
+
|
|
170
|
+
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.
|
|
171
|
+
2. ~ CHANGELOG: reformat to Markdown headers (# CHANGELOG, ## <date>), keeping the extensionless name; entry bodies unchanged.
|
|
172
|
+
3. ~ docs/: regenerate the print-ready PDFs via rake docs:gen (stale for several releases).
|
|
173
|
+
4. ~ Namo::VERSION: /0.25.0/0.25.1/
|
|
174
|
+
|
|
175
|
+
## 20260703
|
|
176
|
+
|
|
177
|
+
0.25.0: + Namo#attach! — attach a formulary, evicting any data column whose name a formulary method collides with, rather than raising.
|
|
178
|
+
|
|
179
|
+
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.
|
|
180
|
+
|
|
181
|
+
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.
|
|
182
|
+
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.
|
|
183
|
+
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.
|
|
184
|
+
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.
|
|
185
|
+
5. ~ ROADMAP.md: + the 0.25.0 release entry; current-state bumped to 0.25.0.
|
|
186
|
+
6. ~ Namo::VERSION: /0.24.1/0.25.0/
|
|
187
|
+
|
|
188
|
+
## 20260629
|
|
189
|
+
|
|
190
|
+
0.24.1: ~ Namo#add_row — guard row-append against a data/formula name collision.
|
|
191
|
+
|
|
192
|
+
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.
|
|
193
|
+
|
|
194
|
+
1. ~ lib/namo.rb: ~ private add_row(row) — + a guard: collisions = row.keys & derived_dimensions; raise ArgumentError naming them unless empty, before appending. A non-colliding row appends and returns self as before. << is otherwise unchanged: Module→attach, Row→add_row(row.to_h), Hash→add_row(row), else→TypeError.
|
|
195
|
+
2. ~ test/namo_test.rb: + the "#<<" describe gains two guard cases — an appended Hash whose keys collide with a formula raises ArgumentError naming the collision, and an appended Row likewise. The existing non-colliding row/chain tests are unchanged.
|
|
196
|
+
3. ~ README.md: ~ the Formularies << note — the row arm now states the guard (a row colliding with a formula name raises, symmetric with attach), replacing the 0.24.0 "surfaces live per existing precedence" wording, which held only when the colliding key was not @data.first.
|
|
197
|
+
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.
|
|
198
|
+
5. ~ Namo::VERSION: /0.24.0/0.24.1/
|
|
199
|
+
|
|
200
|
+
## 20260629
|
|
201
|
+
|
|
202
|
+
0.24.0: ~ Namo#<<, Namo::Collection#<< — widen from a formulary-only alias into a polymorphic "append a constituent" operator.
|
|
203
|
+
|
|
204
|
+
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.
|
|
205
|
+
|
|
206
|
+
1. ~ lib/namo.rb: ~ Namo#<< — replace the alias_method :<<, :attach with a dispatching << that routes Module→attach, Row→add_row(row.to_h), Hash→add_row(row), else→TypeError. attach stays Module-only and unchanged, so the 0.23.0 collision-guard and formulary semantics carry over verbatim and a Module's << still reaches it. + private add_row(row), appending to @data and returning self (so << chains). A whole Namo falls to the else and raises (it is not a Module/Row/Hash) — combining is +. Row-append is a data mutation that does not pass through []=, so it does not trigger formula-eviction; a row carrying a formula-named key surfaces live through the formula on access, per existing precedence (not guarded — the exclusivity invariant is []='s).
|
|
207
|
+
2. ~ lib/Namo/Collection.rb: ~ Collection#<< — keep the splat + flatten (a member or an array of them), but dispatch each constituent: Namo→add_member, Module→attach (inherited from Namo — a Collection carries formulae over its detail view), Hash/Row→ArgumentError redirecting to member-add, else→TypeError. + private add_member(member), the prior reject-by-name + append body extracted verbatim. The @data = detail.data rebuild after the loop is preserved, so member-adds re-materialise detail as before and a Module-only << rebuilds harmlessly. Module routes to the inherited Namo#attach; member-add behaviour is byte-identical to 0.23.0 for the Namo/array cases.
|
|
208
|
+
3. ~ test/namo_test.rb: ~ the formularies "#<<" describe gains the row arms — a Hash appends as a data row (and returns the Namo), a Row appends its underlying hash, a whole Namo raises TypeError (+'s job), a bare callable raises TypeError ([]='s job), and a mixed chain interleaves rows and a formulary. The two 0.23.0 Module-arm tests are unchanged.
|
|
209
|
+
4. ~ test/Namo/Collection_test.rb: ~ the "#<<" describe gains a formulary attach whose row-scoped method resolves over the detail rows, a collection-scoped method (the namo_collection convention) reaching members, a loose Hash and a loose Row each raising ArgumentError with the member-redirect message, and a chain mixing member-adds with a formulary attach. The 0.23.0 member-add tests are unchanged.
|
|
210
|
+
5. ~ README.md: ~ the Formularies << note — << now appends a constituent (Module, Hash, or Row) to a base Namo, chains, and raises on a bare callable or a whole Namo; the bare-Hash-is-always-a-row and row-append-does-not-evict notes added. ~ the Collection "<< and unnamed members" section — Collection#<< dispatches member/Module/raise, with the deliberate Hash/Row divergence spelled out. + a "Collection formulae" subsection — the namo_collection convention (an ordinary two-arity formulary method reaching a collection view in its body), no marker and no second store.
|
|
211
|
+
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.
|
|
212
|
+
7. ~ Namo::VERSION: /0.23.0/0.24.0/
|
|
213
|
+
|
|
214
|
+
## 20260627
|
|
215
|
+
|
|
216
|
+
0.23.0: + Namo::Formulary — A formulary is a reusable body of derived dimensions a Namo instance may draw upon.
|
|
217
|
+
|
|
218
|
+
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.
|
|
219
|
+
2. ~ lib/Namo/Formulae.rb: + attach(modul), aliased as << — after the marker guard, extends a single memoised inert host (@host ||= Object.new) with the module and stores each of its public_instance_methods(false) as a Method bound to that host, then returns self. One host per Formulae across all attaches, so the bound Methods share a self-free receiver and a formulary method can't shadow Namo's API. No other change: derive, key?, required_parameter_count, keys, merge/dup/reject, and ==/eql?/hash are the 0.22.0 store-only methods, and they handle the formulary methods because the formulary methods are now store entries.
|
|
220
|
+
3. ~ lib/namo.rb: + require of Namo::Formulary; + Namo#attach(modul), aliased as << — raises ArgumentError naming the collision if a formulary method's name collides with a data column (intent/blast-radius: you didn't type the module's names, and it could clear several columns), else forwards to @formulae.attach and returns self. + the class-include channel: initialize calls a private attach_included_formularies that scans self.class.ancestors.reverse and routes each tagged module through attach (same guard, same host; reverse so most-recent-include wins, matching MRO). Collection#initialize calls super, so it participates harmlessly. Collection defines its own <<, so its member-add is unaffected and a plain Namo's << attaches a formulary. Separately and additively, []='s formula branch widens from when Proc to value.respond_to?(:call), so a directly-assigned Method or lambda registers as a formula rather than broadcasting per-row; arrays are unaffected. derived_dimensions, ===/eql?/hash, and requires_arguments? are unchanged from 0.22.0 — formulary methods are store keys, so the existing keys-based forms already count them.
|
|
221
|
+
4. + test/Namo/Formulae_test.rb: + a "formulary tier" describe — attach returns self, rejects an untagged module, and copies the public methods into the store (excluding a private helper); a single shared host across attaches (bound Methods share a receiver); << attaches as attach does; key?, required_parameter_count, and derive resolve the copied methods; most-recent-wins on re-attachment; carry-through through dup/reject/merge.
|
|
222
|
+
5. ~ test/namo_test.rb: + a "formularies" describe (runtime attach) — attach (returns the Namo, rejects untagged); derived_dimensions (copied names, private helper absent, vanilla Namo empty); resolution through values/Row/selection; two-arity windowing and the missing-namo raise; a parameterised method omitted from bulk and raising bare, materialising through a row-scoped wrapper; re-attachment; an instance []= formula shadowing a formulary method; attach raising on a formulary/data-name collision; carry-through through selection, projection, and concatenation; projection materialising-and-dropping a formulary name; === against a vanilla Namo and against the same names via []=; << attaching as attach does. + a "formularies via class include" describe — included methods as derived dimensions (private absent), resolution, most-recent-include wins (MRO), raise at construction on a data collision, and the build-time-vs-after contract (an instance built before a later include sees it only via attach). ~ the #[]= polymorphic dispatch describe gains a Method and a lambda registering as formulae (the array-broadcast regression already present).
|
|
223
|
+
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.
|
|
224
|
+
7. ~ Namo::VERSION: /0.22.0/0.23.0/
|
|
225
|
+
|
|
226
|
+
## 20260626
|
|
227
|
+
|
|
228
|
+
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.
|
|
229
|
+
|
|
230
|
+
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.
|
|
231
|
+
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.
|
|
232
|
+
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.
|
|
233
|
+
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.
|
|
234
|
+
5. ~ Namo::VERSION: /0.21.0/0.22.0/
|
|
235
|
+
|
|
236
|
+
## 20260625
|
|
237
|
+
|
|
238
|
+
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.
|
|
239
|
+
|
|
240
|
+
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.
|
|
241
|
+
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.
|
|
242
|
+
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).
|
|
243
|
+
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).
|
|
244
|
+
5. ~ Namo::VERSION: /0.20.0/0.21.0/
|
|
245
|
+
|
|
246
|
+
## 20260615
|
|
247
|
+
|
|
248
|
+
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.
|
|
249
|
+
|
|
250
|
+
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.
|
|
251
|
+
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).
|
|
252
|
+
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.
|
|
253
|
+
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.
|
|
254
|
+
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.
|
|
255
|
+
6. ~ Namo::VERSION: /0.19.0/0.20.0/
|
|
256
|
+
|
|
257
|
+
## 20260614
|
|
258
|
+
|
|
259
|
+
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.
|
|
260
|
+
|
|
261
|
+
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).
|
|
262
|
+
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 <=.
|
|
263
|
+
3. ~ Namo::VERSION: /0.18.0/0.19.0/
|
|
264
|
+
|
|
265
|
+
## 20260613
|
|
266
|
+
|
|
267
|
+
0.18.0: + Namo::Collection — hierarchical aggregate of named Namos with summary/detail views.
|
|
268
|
+
|
|
269
|
+
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.
|
|
270
|
+
2. ~ lib/namo.rb: + require_relative './Namo/Collection'.
|
|
271
|
+
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.
|
|
272
|
+
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).
|
|
273
|
+
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.
|
|
274
|
+
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.
|
|
275
|
+
7. ~ Namo::VERSION: /0.17.0/0.18.0/
|
|
276
|
+
|
|
277
|
+
## 20260613
|
|
278
|
+
|
|
279
|
+
0.17.0: + parameterised formulae — formulae with required parameters beyond (row, namo) receive arguments at access time through Row#[].
|
|
280
|
+
|
|
281
|
+
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.
|
|
282
|
+
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".
|
|
283
|
+
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.
|
|
284
|
+
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.
|
|
285
|
+
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.
|
|
286
|
+
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.
|
|
287
|
+
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.
|
|
288
|
+
8. ~ COMPARISON.md: Parameterised formulae -> shipped (0.17.0), the entry noting access-time arguments and argument-count enforcement. Date bumped.
|
|
289
|
+
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.
|
|
290
|
+
10. ~ Namo::VERSION: /0.16.0/0.17.0/
|
|
291
|
+
|
|
292
|
+
## 20260612
|
|
293
|
+
|
|
294
|
+
0.16.0: ~ data/formula exclusivity — projection drops the formulae it materialises; * and ** raise on a data/formula name collision.
|
|
295
|
+
|
|
296
|
+
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.
|
|
297
|
+
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.
|
|
298
|
+
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.
|
|
299
|
+
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).
|
|
300
|
+
5. ~ README.md: + Projection of derived dimensions subsection under Formulae (naming materialises, omitting carries live); data/formula collision sentences under Composition and Cartesian product.
|
|
301
|
+
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.
|
|
302
|
+
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.
|
|
303
|
+
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.
|
|
304
|
+
9. ~ Namo::VERSION: /0.15.0/0.16.0/
|
|
305
|
+
|
|
306
|
+
## 20260612
|
|
307
|
+
|
|
308
|
+
0.15.0: + two-arity formulae — procs with arity 2 receive (row, namo) for cross-row computation.
|
|
309
|
+
|
|
310
|
+
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.
|
|
311
|
+
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.
|
|
312
|
+
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).
|
|
313
|
+
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.
|
|
314
|
+
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.
|
|
315
|
+
6. ~ README.md: + Cross-row formulae subsection under Formulae, with the SMA example, the yielding-Namo semantic, and the no-context error.
|
|
316
|
+
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.
|
|
317
|
+
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.
|
|
318
|
+
9. ~ Namo::VERSION: /0.14.0/0.15.0/
|
|
319
|
+
|
|
320
|
+
## 20260608
|
|
321
|
+
|
|
322
|
+
0.14.0: + blocks on the composition operators (*, **) for custom match refinement.
|
|
323
|
+
|
|
324
|
+
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.
|
|
325
|
+
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.
|
|
326
|
+
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.
|
|
327
|
+
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.
|
|
328
|
+
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.
|
|
329
|
+
6. ~ COMPARISON.md: "Conditional join with block" -> shipped (0.14.0).
|
|
330
|
+
7. ~ Namo::VERSION: /0.13.2/0.14.0/
|
|
331
|
+
|
|
332
|
+
## 20260608
|
|
333
|
+
|
|
334
|
+
0.13.2: Narrow the planned 0.14.0 block-form scope to the composition operators and document the rationale.
|
|
335
|
+
|
|
336
|
+
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.
|
|
337
|
+
2. ~ ROADMAP.md: Rescope the Summary's "next phase" pointer to composition-only block forms.
|
|
338
|
+
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.
|
|
339
|
+
4. ~ Namo::VERSION: /0.13.1/0.13.2/
|
|
340
|
+
|
|
341
|
+
## 20260604
|
|
342
|
+
|
|
343
|
+
0.13.1: Correct the row-order claim across the docs and document the same-dimension-order precondition.
|
|
344
|
+
|
|
345
|
+
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 +.
|
|
346
|
+
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.
|
|
347
|
+
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.
|
|
348
|
+
4. ~ Namo::VERSION: /0.13.0/0.13.1/
|
|
349
|
+
|
|
350
|
+
## 20260601
|
|
351
|
+
|
|
352
|
+
0.13.0: Polymorphic []= — proc registers a formula, scalar broadcasts to every row.
|
|
353
|
+
|
|
354
|
+
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.
|
|
355
|
+
2. ~ test/namo_test.rb: + "#[]= polymorphic dispatch" describe — proc registration (regression), data-column clear on proc assign, scalar broadcast, formula clear on scalar assign, array-broadcasts-as-value, both last-write-wins orderings, derived/data surfacing exactly once in `dimensions`, the no-data-column clear-guard short-circuit, and the empty-Namo case. Existing "#[]= formulae" describe unchanged.
|
|
356
|
+
3. ~ README.md: + Polymorphic []= section (two branches, exclusivity rule, last-write-wins, symmetry with []), with the scalar-broadcast-vs-formula contrast and a note tying exclusivity to data_dimensions/derived_dimensions.
|
|
357
|
+
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+.
|
|
358
|
+
5. ~ Namo::VERSION: /0.12.0/0.13.0/
|
|
359
|
+
|
|
360
|
+
## 20260601
|
|
361
|
+
|
|
362
|
+
0.12.0: Constructor widening — keyword data: and name:.
|
|
363
|
+
|
|
364
|
+
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.
|
|
365
|
+
2. + lib/namo.rb: `attr_accessor :name`, alongside the existing `:data` and `:formulae` accessors. Gives `name` reading and `name=` post-construction mutation. Operator results carry `name == nil` (the operators construct without `name:`), establishing the subclass guard convention: subclasses guard `initialize` side effects with `return unless name`, so operator-derived instances skip them and only explicitly-named constructions fire them.
|
|
366
|
+
3. ~ test/namo_test.rb: + "construction" describe (positional data, positional + keyword formulae, no-arg empty, formulae-only, explicit `[]` honoured over the nil sentinel, keyword `data:`, positional-wins-over-keyword, and round-trips through a set operator and an Enumerable method). + "#name" describe (stored from keyword, defaults to nil, settable post-construction, nil on set-operator and Enumerable-derived results). + "subclass side-effect guard" describe — an anonymous Class.new(Namo) with a `return unless name`-guarded counter, asserting it fires for a named construction and stays untouched for an unnamed construction and an operator result.
|
|
367
|
+
4. ~ README.md: + note in the Usage section that data may be passed positionally or by `data:`, with positional winning when both are given. + "Named Namos" section: the `name:` keyword, the `name`/`name=` accessor, nil-on-derivation for operator results, and the subclass guard convention with the TradingAnalysis / argument-less `super` example.
|
|
368
|
+
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+.
|
|
369
|
+
6. ~ Namo::VERSION: /0.11.1/0.12.0/
|
|
370
|
+
|
|
371
|
+
## 20260601
|
|
372
|
+
|
|
373
|
+
0.11.1: Extract the subset Enumerable methods into a Namo::Enumerable module.
|
|
374
|
+
|
|
375
|
+
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.
|
|
376
|
+
2. ~ lib/namo.rb: Remove the inline each and the eleven subset methods; remove `include Enumerable`; + `require_relative './Namo/Enumerable'` and `include Namo::Enumerable` (which transitively brings stdlib Enumerable into the ancestor chain below the module, so the overrides win and map/reduce/etc. still fall through). Pure reorganisation — no behaviour change.
|
|
377
|
+
3. ~ test/namo_test.rb: + Namo::Enumerable module tests — it is a Module, Namo includes it and stdlib Enumerable, and it precedes ::Enumerable in Namo.ancestors so the overrides resolve first. The existing Enumerable subset tests are unchanged and remain green, evidencing the move is behaviour-preserving.
|
|
378
|
+
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.
|
|
379
|
+
5. ~ Namo::VERSION: /0.11.0/0.11.1/
|
|
380
|
+
|
|
381
|
+
## 20260531
|
|
382
|
+
|
|
383
|
+
0.11.0: ~ Subset Enumerable methods (select, reject, sort_by, first, last, take, drop, take_while, drop_while, uniq, partition) return Namos
|
|
384
|
+
|
|
385
|
+
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.
|
|
386
|
+
2. + Namo#first, Namo#last: With an argument, return a Namo of the first/last n rows. Without an argument, return a single Row (or nil on an empty Namo), per Ruby's Enumerable#first / Array#last convention. first(0) returns an empty Namo (n is truthy). last(n) reads @data.last(n) directly — the efficient path, no Enumerable materialise-then-slice fall-through.
|
|
387
|
+
3. + Namo#take, Namo#drop: Leading subset and its complement, wrapping @data.take(n) / @data.drop(n).
|
|
388
|
+
4. + Namo#uniq: Full-row dedupe. The no-block path dedupes raw @data hashes via Array#uniq, which uses eql?/hash — matching Row#eql?/Row#hash from 0.10.0, so numeric types stay distinct ({n: 1} and {n: 1.0} are both kept) and Row allocations are avoided. With a block, dedupes on the block's return value, per Enumerable#uniq.
|
|
389
|
+
5. + Namo#partition: Returns [Namo, Namo] — matches and non-matches — each wrapped via self.class.new with duped formulae. map, flat_map, reduce, sum, min_by, max_by, count, and each are deliberately left unchanged (transformed values, scalars, or already-correct). group_by is structurally blocked — it needs Namo::Collection (0.17.0) and lands at 0.18.0.
|
|
390
|
+
6. ~ test/namo_test.rb: + describe blocks for each new method — return-as-Namo, formula references in blocks, formula carry-through, empty-Namo edges, n=0 and n>length boundaries, uniq full-row dedupe plus numeric strictness plus block form, partition summing to the original, and subclass type preservation via Class.new(Namo). + Tests that select's filter and find_all aliases return Namos. + Guard tests that map/flat_map/reduce keep their original return types.
|
|
391
|
+
7. ~ README.md: + Note in the Enumerable section that the subset-returning methods return Namos, with a chaining example and a first/last/uniq semantics paragraph. ~ Overview design-stance sentence to fold the Enumerable subset methods into "Namos in, Namos out".
|
|
392
|
+
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.
|
|
393
|
+
9. ~ Namo::VERSION: /0.10.0/0.11.0/
|
|
394
|
+
|
|
395
|
+
## 20260528
|
|
396
|
+
|
|
397
|
+
0.10.0: + Row value semantics: ==, eql?, hash
|
|
398
|
+
|
|
399
|
+
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.
|
|
400
|
+
2. + Namo::Row#eql?: Strict data equality. Same shape as `==` but uses `Hash#eql?` on `@row`, matching Ruby's numeric-type-strict convention (`{n: 1}` Row is `eql?` to another `{n: 1}` Row, but not to a `{n: 1.0}` Row, just as `1.eql?(1.0)` is false).
|
|
401
|
+
3. + Namo::Row#hash: Returns `@row.hash`. Consistent with `eql?` — Rows that are `eql?` produce equal hashes, so Rows participate correctly in Hash keys, Set members, and Array#uniq.
|
|
402
|
+
4. ~ test/Namo/Row_test.rb: + `#==` tests (equal/different `@row`, non-Row operand, formulae ignored). + `#eql?` tests (equal `@row`, non-Row operand, `Hash#eql?` numeric-type strictness vs `==`, formulae ignored). + `#hash` tests (hash equality for `eql?` Rows, Rows as Hash keys, Rows as Set members deduplicating equal Rows).
|
|
403
|
+
5. ~ README.md: + Note in the Equality section that Row participates in value semantics (`==`, `eql?`, `hash`) on the same data-only basis as `Namo#==`, ignoring formulae.
|
|
404
|
+
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.
|
|
405
|
+
7. ~ Namo::VERSION: /0.9.2/0.10.0/
|
|
406
|
+
|
|
407
|
+
## 20260525
|
|
408
|
+
|
|
409
|
+
0.9.2: ~ Auto-load Namo::VERSION
|
|
410
|
+
|
|
411
|
+
1. ~ lib/namo.rb: + `require_relative './Namo/VERSION'`. Namo::VERSION is now defined when `namo` is required, without needing a separate require.
|
|
412
|
+
2. ~ Namo::VERSION: /0.9.1/0.9.2/
|
|
413
|
+
|
|
414
|
+
## 20260525
|
|
415
|
+
|
|
416
|
+
0.9.1: ~ Namo#initialize: default data to [], ~ Namo#data_dimensions to handle empty data
|
|
417
|
+
|
|
418
|
+
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`.
|
|
419
|
+
2. ~ Namo#data_dimensions: Handle empty `@data` with `@data.first&.keys || []`. Returns `[]` for a Namo with no rows instead of raising `NoMethodError` on `nil.keys`.
|
|
420
|
+
3. ~ Namo#dimensions: Refactor to delegate as `data_dimensions + derived_dimensions`. The empty-case guard now lives in one place; `dimensions` becomes a trivial composition.
|
|
421
|
+
4. ~ test/namo_test.rb: + Empty-Namo tests (dimensions, data_dimensions, formula surfacing on a Namo with no data).
|
|
422
|
+
5. ~ Namo::VERSION: /0.9.0/0.9.1/
|
|
423
|
+
|
|
424
|
+
## 20260521
|
|
425
|
+
|
|
426
|
+
0.9.0: + composition operators: equi-join (*), Cartesian product (**), decomposition (/)
|
|
427
|
+
|
|
428
|
+
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.
|
|
429
|
+
2. + Namo#**: Cartesian product of two Namos with disjoint data dimensions. Raises ArgumentError ("dimensions in common, need no common dimensions") when any dimension is shared. Output has left.length * right.length rows. Formulae merge with self winning on conflict.
|
|
430
|
+
3. + Namo#/: Decomposition. Removes from self the dimensions that are also in other (the intersection), then dedupes the projected rows. No precondition — total on Namo × Namo. When self and other share no dimensions, the operator is a no-op. Formulae carry through from self. (a ** b) / b == a exactly; (a * b) / b loses dimensions shared between a and b.
|
|
431
|
+
4. + Namo#raise_unless_shared_data_dimensions, Namo#raise_unless_disjoint_data_dimensions: Private precondition helpers for #* and #** respectively.
|
|
432
|
+
5. ~ test/namo_test.rb: + #* tests (single/multi-dimension join, inner-join symmetry, multiplicative duplicates, formulae merging, error cases). + #** tests (Cartesian product, output sizing, dimension overlap error). + #/ tests (intersection removal, dedupe of collided rows, no-op on disjoint operands, idempotence). + Composition round-trip tests for the ** case (exact identity) and the * case (lossy on shared dimensions).
|
|
433
|
+
6. ~ README.md: + Composition section (*), + Cartesian product section (**), + Decomposition section (/) including the combining-vs-projecting rationale for /'s no-precondition design. Placed after Symmetric Difference and before Equality.
|
|
434
|
+
7. ~ ROADMAP.md: Promote 0.9.0 from upcoming to shipped under "Current state: 0.9.0"; revise Summary to include composition in the operator vocabulary and point "next phase" at 0.10.0+.
|
|
435
|
+
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.
|
|
436
|
+
9. ~ Namo::VERSION: /0.8.0/0.9.0/
|
|
437
|
+
|
|
438
|
+
## 20260521
|
|
439
|
+
|
|
440
|
+
0.8.0: + proc and regex-based selection
|
|
441
|
+
|
|
442
|
+
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.
|
|
443
|
+
2. ~ Namo::Row#match?: + `when Regexp` branch — matches against row[dimension].to_s. nil becomes "" (matches //, not /./); Integer/Float/Date/Symbol coerce via to_s.
|
|
444
|
+
3. ~ test/Namo/Row_test.rb: + Proc-predicate tests (true/false/nil/truthy non-boolean returns, nil dimension values, composition with exact/array/range/regex, multiple proc predicates across dimensions, carry-through to formula-defined dimensions). + Regexp-predicate tests (match/no-match/case-insensitive on Strings, to_s coercion of Integer/Float/Date/Symbol/nil, composition with exact/array/range/proc, multiple regex predicates across dimensions, carry-through to formula-defined dimensions).
|
|
445
|
+
4. ~ test/namo_test.rb: + End-to-end tests for proc selection, regex selection, and mixed proc/regex selection, including composition with projection and contraction in a single call and selection on formula-defined dimensions.
|
|
446
|
+
5. ~ README.md: + Proc and regex examples in the Selection section; + paragraphs on proc semantics (truthy/nil-aware, composes with everything) and regex semantics (.to_s coercion across nil/String/Symbol/Integer/Float/Date).
|
|
447
|
+
6. ~ ROADMAP.md: Promote 0.8.0 from upcoming to shipped under "Current state: 0.8.0"; revise Summary to include proc/regex in the selection vocabulary and point "next phase" at 0.9.0+.
|
|
448
|
+
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).
|
|
449
|
+
8. ~ Namo::VERSION: /0.7.0/0.8.0/
|
|
450
|
+
|
|
451
|
+
## 20260520
|
|
452
|
+
|
|
453
|
+
0.7.0: + derived-dimension surfacing, lazy single-column access, live views
|
|
454
|
+
|
|
455
|
+
1. + Namo#data_dimensions: Returns the storage dimensions as a plain Array (keys of the first row).
|
|
456
|
+
2. + Namo#derived_dimensions: Returns the formula names as a plain Array.
|
|
457
|
+
3. + Namo#values(*dims): Per-dimension full sequences (duplicates preserved, in row order). With no args, returns a Hash {dim => sequence} across the queryable namespace. With one arg, lazily computes and returns just that column as an Array. With multiple args, returns a subset Hash containing each requested dimension. Unknown dimensions propagate as nil per row, matching Row#[] and Namo#[] selection conventions — values(:unknown) returns an Array of nils; values(:known, :unknown) returns {known: [...], unknown: [nil, ...]}.
|
|
458
|
+
4. + Namo#coordinates(*dims): Per-dimension unique-value sets. Same argument shape as #values; coordinates(dim) == values(dim).uniq. Unknown dimensions therefore appear as [nil].
|
|
459
|
+
5. + Namo#to_h: Alias for the full values Hash.
|
|
460
|
+
6. ~ Namo#dimensions: Now covers the queryable namespace (storage + derived) instead of storage-only. Return type stays a plain Array. Memoisation removed: every call recomputes from current state (live view).
|
|
461
|
+
7. ~ Namo#coordinates: Memoisation removed (was @coordinates ||= ...). Now covers the queryable namespace; coordinates(:derived_dim) and coordinates[:derived_dim] work, evaluating the formula across all rows. New positional-args API supports lazy single-column access.
|
|
462
|
+
8. ~ Namo#canonical_data: Sorts by data_dimensions to preserve 0.6.0 row-equality semantics under the broader dimensions definition.
|
|
463
|
+
9. /raise_unless_matching_dimensions/raise_unless_matching_data_dimensions/: Private helper renamed to reflect what it actually compares.
|
|
464
|
+
10. ~ test/namo_test.rb: + Tests for #data_dimensions, #derived_dimensions, the no-arg/single-arg/multi-arg forms of #values and #coordinates, derived-dimension surfacing in #dimensions, #to_h, the coordinates(dim) == values(dim).uniq consistency property, and live-view semantics (added rows / formulae reflected on next call).
|
|
465
|
+
11. ~ README.md: + Coordinates and values section covering #values, #coordinates(*dims), #data_dimensions, #derived_dimensions, and #to_h.
|
|
466
|
+
12. ~ COMPARISON.md: + "Worked example: comparing yesterday's screen to today's" under the set-operators section; + "Schema dispatch on incoming data feeds" under the equality/=== section. Date bumped to 20260520.
|
|
467
|
+
13. ~ EXAMPLES.md: - "Schema dispatch on incoming data feeds", - "Comparing yesterday's screen to today's" — both relocated to COMPARISON.md as worked examples under the matching feature sections. Date bumped to 20260520.
|
|
468
|
+
14. ~ ROADMAP.md: Promote 0.7.0 from upcoming to shipped under "Current state: 0.7.0"; point "next phase" at 0.8.0+.
|
|
469
|
+
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.
|
|
470
|
+
16. ~ Namo::VERSION: /0.6.0/0.7.0/
|
|
471
|
+
|
|
472
|
+
## 20260511
|
|
473
|
+
|
|
474
|
+
0.6.0: + equality, pattern-match, and subset/superset operators
|
|
475
|
+
|
|
476
|
+
1. + Namo#==: Multiset equality on row data, ignoring class and formulae.
|
|
477
|
+
2. + Namo#===: Analytical identity match — true iff other has the same dimensions and same formula names as self, ignoring rows and proc bodies. Returns false for non-Namo operands.
|
|
478
|
+
3. + Namo#eql?: Strict equality requiring class match, multiset-equal data, and formula name match.
|
|
479
|
+
4. + Namo#hash: Content-based hash, consistent with eql?.
|
|
480
|
+
5. + Namo#<, #<=, #>, #>=: Multiset subset/superset relations on rows. Raise ArgumentError on mismatched dimensions, TypeError on non-Namo operand.
|
|
481
|
+
6. ~ Namo#+, #-, #&, #|, #^: Error message on dimension mismatch updated to "dimensions don't match: X vs Y". Non-Namo operand now raises TypeError ("can't compare Namo with X") instead of NoMethodError.
|
|
482
|
+
7. ~ lib/namo.rb, namo.gemspec: Minor cleanup (./-prefixed requires; gemspec whitespace).
|
|
483
|
+
8. ~ test/namo_test.rb: Add tests for ==, ===, eql?, hash, <, <=, >, >=, equal?, and the new error message.
|
|
484
|
+
9. ~ README.md: + Equality section, + Subset and superset section, + design-philosophy paragraph in the opening and one-line principle callouts in the dimensions, formulae, set-operator, and equality sections.
|
|
485
|
+
10. + script/md4print, ~ Rakefile: + rake docs:print, docs:pdf, docs:all for regenerating docs/*.print.md and docs/*.print.pdf.
|
|
486
|
+
11. ~ Namo::VERSION: /0.5.0/0.6.0/
|
|
487
|
+
|
|
488
|
+
## 20260416
|
|
489
|
+
|
|
490
|
+
0.5.0: + row-axis set operations: intersection (&), union (|), symmetric difference (^)
|
|
491
|
+
|
|
492
|
+
1. + Namo#&: Intersection. Returns rows present in both Namo objects. Requires matching dimensions.
|
|
493
|
+
2. + Namo#|: Union. Returns all rows from both sides, deduplicated. Requires matching dimensions.
|
|
494
|
+
3. + Namo#^: Symmetric difference. Returns rows in one side but not both. Requires matching dimensions.
|
|
495
|
+
4. ~ Namo_test.rb: Add tests for #&, #|, and #^.
|
|
496
|
+
5. ~ README.md: + Intersection section, + Union section, + Symmetric difference section.
|
|
497
|
+
6. ~ Namo::VERSION: /0.4.0/0.5.0/
|
|
498
|
+
|
|
499
|
+
## 20260415
|
|
500
|
+
|
|
501
|
+
0.4.0: + concatenation (+) and row removal (-)
|
|
502
|
+
|
|
503
|
+
1. + Namo#+: Concatenate two Namo instances with matching dimensions. Appends rows from the second to the first. Raises ArgumentError when dimensions differ.
|
|
504
|
+
2. + Namo#-: Remove rows from the first Namo that appear exactly in the second. Whole-row set difference. Raises ArgumentError when dimensions differ.
|
|
505
|
+
3. ~ Namo_test.rb: Add tests for #+ and #-.
|
|
506
|
+
4. ~ README.md: + Concatenation section, + Row removal section.
|
|
507
|
+
5. ~ Namo::VERSION: /0.3.0/0.4.0/
|
|
508
|
+
|
|
509
|
+
## 20260415
|
|
510
|
+
|
|
511
|
+
0.3.0: + contraction
|
|
512
|
+
|
|
513
|
+
1. + Namo::NegatedDimension: Wrapper for negated dimension names used in contraction.
|
|
514
|
+
2. + Symbol#-@: Produce a NegatedDimension, enabling -:date syntax.
|
|
515
|
+
3. ~ Namo#[]: Extend to recognise NegatedDimension for contraction (remove named dimensions, keep everything else). Raise ArgumentError when mixing projection and contraction.
|
|
516
|
+
4. > Namo::Row: Extract to Namo/Row.rb.
|
|
517
|
+
5. ~ namo_test.rb: Add tests for contraction, mixed-mode error, and contraction with selection and formulae. Split tests into one file per class: Row_test.rb, NegatedDimension_test.rb, Symbol_test.rb.
|
|
518
|
+
6. ~ README.md: + Contraction section
|
|
519
|
+
7. ~ Namo::VERSION: /0.2.0/0.3.0/
|
|
520
|
+
|
|
521
|
+
## 20260414
|
|
522
|
+
|
|
523
|
+
0.2.0: Include Enumerable.
|
|
524
|
+
|
|
525
|
+
1. ~ Namo: include Enumerable
|
|
526
|
+
2. + Namo#each: Yield Row objects wrapping each data row with formulae. Returns an Enumerator when no block is given.
|
|
527
|
+
3. ~ Namo#[]: Refactor to use Enumerable#select and Row#match? instead of select_rows.
|
|
528
|
+
4. - Namo#select_rows: Replaced by Row#match?.
|
|
529
|
+
5. + Namo::Row#match?: Selection logic moved from Namo to Row.
|
|
530
|
+
6. + Namo::Row#to_h: Return the underlying row hash.
|
|
531
|
+
7. ~ namo_test.rb: Add tests for #each and Enumerable methods (map, reduce, min_by, flat_map).
|
|
532
|
+
8. ~ README.md: + Enumerable section
|
|
533
|
+
9. ~ Namo::VERSION: /0.1.0/0.2.0/; /module/class/ to match Namo's definition.
|
|
534
|
+
|
|
535
|
+
## 20260328
|
|
536
|
+
|
|
537
|
+
0.1.0: Add formulae via []=. Formulae are procs which receive a Row object, compose via named references, and carry through selection.
|
|
538
|
+
|
|
539
|
+
1. ~ Namo#[]: Refactor to accept *names for projection alongside **selections. Always returns a Namo instance.
|
|
540
|
+
2. + Namo#[]=: Define named formulae as procs.
|
|
541
|
+
3. + Namo#to_a: Extract data as an array of hashes.
|
|
542
|
+
4. + Namo::Row: Resolve names against row data or formulae.
|
|
543
|
+
5. ~ Namo#initialize: Accept optional formulae: keyword argument.
|
|
544
|
+
6. + attr_accessor :data, :formulae
|
|
545
|
+
7. ~ README.md: + Projection section, + Formulae section, + Extracting data section
|
|
546
|
+
8. ~ namo_test.rb: Add tests for projection, formulae, composition, chained and single-call selection with projection.
|
|
547
|
+
9. ~ Namo::VERSION: /0.0.1/0.1.0/
|
|
548
|
+
|
|
549
|
+
## 20260315
|
|
550
|
+
|
|
551
|
+
0.0.1: Fix README.md and CHANGELOG.
|
|
552
|
+
|
|
553
|
+
1. ~ README.md: Fix Contributing secton.
|
|
554
|
+
2. ~ CHANGELOG: Use the same description in the summary as in the commit message.
|
|
555
|
+
3. ~ Namo::VERSION: /0.0.0/0.0.1/
|
|
556
|
+
|
|
557
|
+
## 20260315
|
|
558
|
+
|
|
559
|
+
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.
|
|
560
|
+
|
|
561
|
+
1. + Namo
|
|
562
|
+
2. + Namo#initialize: Initialise from an array of hashes.
|
|
563
|
+
3. + Namo#dimensions: Dimension names are inferred from hash keys.
|
|
564
|
+
4. + Namo#coordinates: Extract the co-ordinates by finding unique values per dimension.
|
|
565
|
+
5. + Namo#[]: Select by named dimension using keyword arguments, using any one of a single value, an array, or a range.
|
|
566
|
+
6. + Namo::VERSION
|