namo 0.28.0 → 0.29.0
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 +4 -4
- data/CHANGELOG +16 -1
- data/README.md +1 -1
- data/lib/Namo/VERSION.rb +1 -1
- data/lib/namo.rb +25 -11
- data/test/namo_test.rb +38 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5e56f3159063720b0e933e9c346ddad824831a51dac7a576da6d0cf841fd28ea
|
|
4
|
+
data.tar.gz: 8aee98bb89f8c7ab978c0ce459cc90c8259123123c13f5158a3e16dbb93922c7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 16e3d16921ee88ae7d5d76c79227efb035128ac46f17d93fa61c7445434bb1c475c597ff7e2478fe19bbaf8f53b73ed366fee5ed1e514ad99a423a030db0d46c
|
|
7
|
+
data.tar.gz: 7e0045e6e6a842be576e171c9c31bde901f7ade581e5dce694e853a868f832c358e5eb7329ce9fc14efdc13a20663f4877135895f99328d646bb8717d177a2af
|
data/CHANGELOG
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 20260715
|
|
4
|
+
|
|
5
|
+
0.29.0: Per access indexed materialisation.
|
|
6
|
+
|
|
7
|
+
Materialise each column once per top-level access, collapsing an O(n²) parent-scanning formula to O(n), with no value retained across accesses.
|
|
8
|
+
|
|
9
|
+
1. ~ Namo#values: wrap the body in a private materialising scope.
|
|
10
|
+
2. ~ Namo#values_for: memoise its column build into @materialised[dim] ||= (the data/derived branch that was its body).
|
|
11
|
+
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.
|
|
12
|
+
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.
|
|
13
|
+
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.
|
|
14
|
+
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.
|
|
15
|
+
7. ~ docs/: regenerate the print-ready PDFs.
|
|
16
|
+
8. ~ Namo::VERSION: /0.28.0/0.29.0/
|
|
17
|
+
|
|
18
|
+
## 20260713
|
|
4
19
|
|
|
5
20
|
0.28.0: ~ Namo::Collection#detail/#as_detail: Accept the by label positionally or by keyword.
|
|
6
21
|
|
data/README.md
CHANGED
|
@@ -688,7 +688,7 @@ prices.values(:sma)
|
|
|
688
688
|
# => [10.0, 15.0, 20.0]
|
|
689
689
|
```
|
|
690
690
|
|
|
691
|
-
`namo` is the Namo that yielded the row, live — so the window always reflects the current state of the object you ask through. A filtered Namo's rows window over the filtered rows; an operator result's rows window over the result. Appending a row changes every cross-row value on the next access, with no caching.
|
|
691
|
+
`namo` is the Namo that yielded the row, live — so the window always reflects the current state of the object you ask through. A filtered Namo's rows window over the filtered rows; an operator result's rows window over the result. Appending a row changes every cross-row value on the next access, with no caching across accesses.
|
|
692
692
|
|
|
693
693
|
One-arity formulae are unchanged, and the two forms mix freely — a one-arity formula can reference a two-arity one, and a two-arity formula can reference a one-arity one, by name.
|
|
694
694
|
|
data/lib/Namo/VERSION.rb
CHANGED
data/lib/namo.rb
CHANGED
|
@@ -30,12 +30,14 @@ class Namo
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def values(*dims)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
materialising do
|
|
34
|
+
if dims.empty?
|
|
35
|
+
materialisable_dimensions.each_with_object({}){|dim, hash| hash[dim] = values_for(dim)}
|
|
36
|
+
elsif dims.length == 1
|
|
37
|
+
values_for(dims.first)
|
|
38
|
+
else
|
|
39
|
+
dims.each_with_object({}){|dim, hash| hash[dim] = values_for(dim)}
|
|
40
|
+
end
|
|
39
41
|
end
|
|
40
42
|
end
|
|
41
43
|
|
|
@@ -290,14 +292,26 @@ class Namo
|
|
|
290
292
|
end
|
|
291
293
|
end
|
|
292
294
|
|
|
293
|
-
def
|
|
294
|
-
if
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
295
|
+
def materialising
|
|
296
|
+
return yield if @materialised
|
|
297
|
+
@materialised = {}
|
|
298
|
+
begin
|
|
299
|
+
yield
|
|
300
|
+
ensure
|
|
301
|
+
@materialised = nil
|
|
298
302
|
end
|
|
299
303
|
end
|
|
300
304
|
|
|
305
|
+
def values_for(dim)
|
|
306
|
+
@materialised[dim] ||= (
|
|
307
|
+
if data_dimensions.include?(dim)
|
|
308
|
+
@data.map{|row_data| row_data[dim]}
|
|
309
|
+
else
|
|
310
|
+
@data.map{|row_data| Row.new(row_data, @formulae, self)[dim]}
|
|
311
|
+
end
|
|
312
|
+
)
|
|
313
|
+
end
|
|
314
|
+
|
|
301
315
|
def materialisable_dimensions
|
|
302
316
|
dimensions.reject{|dim| requires_arguments?(dim)}
|
|
303
317
|
end
|
data/test/namo_test.rb
CHANGED
|
@@ -287,6 +287,44 @@ describe Namo do
|
|
|
287
287
|
end
|
|
288
288
|
end
|
|
289
289
|
|
|
290
|
+
describe "access-scoped materialisation" do
|
|
291
|
+
it "materialises a referenced column once per access, not once per referencing row" do
|
|
292
|
+
calls = Hash.new(0)
|
|
293
|
+
namo = Namo.new([{n: 1}, {n: 2}, {n: 3}])
|
|
294
|
+
namo[:base] = proc{|row| calls[:base] += 1; row[:n] * 10}
|
|
295
|
+
namo[:sum_base] = proc{|row, namo| namo.values(:base).sum}
|
|
296
|
+
_(namo.values(:sum_base)).must_equal [60, 60, 60]
|
|
297
|
+
_(calls[:base]).must_equal 3
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
it "retains nothing across accesses — a later access recomputes from current state" do
|
|
301
|
+
namo = Namo.new([{n: 1}, {n: 2}])
|
|
302
|
+
namo[:total] = proc{|row, namo| namo.values(:n).sum}
|
|
303
|
+
_(namo.values(:total)).must_equal [3, 3]
|
|
304
|
+
namo.data << {n: 4}
|
|
305
|
+
_(namo.values(:total)).must_equal [7, 7, 7]
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
it "clears the access scope when materialisation raises, leaving later accesses working" do
|
|
309
|
+
namo = Namo.new([{n: 1}])
|
|
310
|
+
namo[:boom] = proc{|row| raise 'materialisation blew up'}
|
|
311
|
+
_(->{namo.values(:boom)}).must_raise RuntimeError
|
|
312
|
+
namo[:ok] = proc{|row| row[:n]}
|
|
313
|
+
_(namo.values(:ok)).must_equal [1]
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
it "keeps one scope when a formula's nested values call runs during a multi-dimension access" do
|
|
317
|
+
calls = 0
|
|
318
|
+
namo = Namo.new([{x: 1}, {x: 2}, {x: 3}])
|
|
319
|
+
namo[:base] = proc{|row| calls += 1; row[:x] * 2}
|
|
320
|
+
namo[:agg] = proc{|row, namo| namo.values(:base).first}
|
|
321
|
+
# :agg's formula calls values(:base) mid-materialisation; that nested call must
|
|
322
|
+
# reuse the open scope, not reset it, so the :x column that follows still builds.
|
|
323
|
+
_(namo.values(:agg, :x)).must_equal(agg: [2, 2, 2], x: [1, 2, 3])
|
|
324
|
+
_(calls).must_equal 3
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
290
328
|
describe "#to_h" do
|
|
291
329
|
it "returns the full values Hash" do
|
|
292
330
|
_(sales.to_h).must_equal sales.values
|