namo 0.12.0 → 0.13.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 +9 -0
- data/README.md +36 -0
- data/lib/Namo/VERSION.rb +1 -1
- data/lib/namo.rb +9 -2
- data/test/namo_test.rb +87 -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: ed83092f20799b8589b970ffa06ba8d9c976dc25e4545670e72e600895273548
|
|
4
|
+
data.tar.gz: ad2b6d939418fe8c0197fdc415a0f884179b374009a3397480b9e25e1814b993
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 791f6bd3afa3589546df37e9c6fb7b016de75a6c368202554b53cbc55b92e23c7e9821837ce2057ac8ea40b7f13ef42ec493bfcd061667fa3d293a217608996b
|
|
7
|
+
data.tar.gz: ec205a0df5900931bdf2df644522cffdbd7ef53d6d665fc8fa0ca3b18e79fc5e97971e0c089ea7254e446124866db91d43eed91dda9b0230b5a6f8d6abb9ada3
|
data/CHANGELOG
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
CHANGELOG
|
|
2
2
|
_________
|
|
3
3
|
|
|
4
|
+
20260601
|
|
5
|
+
0.13.0: Polymorphic []= — proc registers a formula, scalar broadcasts to every row.
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
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.
|
|
9
|
+
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.
|
|
10
|
+
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+.
|
|
11
|
+
5. ~ Namo::VERSION: /0.12.0/0.13.0/
|
|
12
|
+
|
|
4
13
|
20260601
|
|
5
14
|
0.12.0: Constructor widening — keyword data: and name:.
|
|
6
15
|
|
data/README.md
CHANGED
|
@@ -585,6 +585,42 @@ sales[product: 'Widget'][:revenue, :quarter]
|
|
|
585
585
|
|
|
586
586
|
Formulae carry through selection — a filtered Namo instance remembers its formulae.
|
|
587
587
|
|
|
588
|
+
### Polymorphic `[]=`
|
|
589
|
+
|
|
590
|
+
`[]=` dispatches on the type of the value assigned. A proc registers a formula, as above. Anything else broadcasts the value to every row:
|
|
591
|
+
|
|
592
|
+
```ruby
|
|
593
|
+
sales[:status] = 'active'
|
|
594
|
+
sales.values(:status)
|
|
595
|
+
# => ['active', 'active', 'active', 'active']
|
|
596
|
+
|
|
597
|
+
sales[:revenue] = proc{|row| row[:price] * row[:quantity]}
|
|
598
|
+
sales.values(:revenue)
|
|
599
|
+
# => [1000.0, 1500.0, 1000.0, 1500.0]
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
The two branches mirror the polymorphism `[]` already has on the selection side, where a single bracket call dispatches over exact values, arrays, ranges, procs, and regexes. Rather than introduce a separate `broadcast` or `set_all` method for the scalar case, `[]=` reads the same way for both: `sales[:status] = 'active'` says "set status to active across this Namo," and `sales[:revenue] = proc{…}` says "derive revenue from each row."
|
|
603
|
+
|
|
604
|
+
The two branches enforce **exclusive storage**: a name is either a data dimension or a derived dimension, never both. Assigning a proc clears any data column of that name; assigning anything else clears any formula of that name. The last write wins, and there is no shadowing:
|
|
605
|
+
|
|
606
|
+
```ruby
|
|
607
|
+
sales[:x] = 5 # :x is a broadcast data value
|
|
608
|
+
sales[:x] = proc{|row| row[:price]} # :x is now a formula — the broadcast value is gone
|
|
609
|
+
|
|
610
|
+
sales[:x] = proc{|row| row[:price]} # :x is a formula
|
|
611
|
+
sales[:x] = 5 # :x is now a broadcast data value — the formula is gone
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
Exclusivity ties directly to the inspection vocabulary: a name assigned a scalar shows up in `data_dimensions`; a name assigned a proc shows up in `derived_dimensions`; never in both, so it appears in `dimensions` exactly once.
|
|
615
|
+
|
|
616
|
+
Only a `Proc` takes the formula branch. An array is a value like any other, so it broadcasts as the per-row value rather than registering as a formula:
|
|
617
|
+
|
|
618
|
+
```ruby
|
|
619
|
+
sales[:weights] = [1, 2, 3]
|
|
620
|
+
sales.values(:weights)
|
|
621
|
+
# => [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
|
|
622
|
+
```
|
|
623
|
+
|
|
588
624
|
### Coordinates and values
|
|
589
625
|
|
|
590
626
|
`dimensions` covers the *queryable namespace* — every name you can ask for, whether it lives in the row data or is computed by a formula. Once formulae are defined, they appear alongside data dimensions:
|
data/lib/Namo/VERSION.rb
CHANGED
data/lib/namo.rb
CHANGED
|
@@ -74,8 +74,15 @@ class Namo
|
|
|
74
74
|
self.class.new(projected, formulae: @formulae.dup)
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
def []=(name,
|
|
78
|
-
|
|
77
|
+
def []=(name, value)
|
|
78
|
+
case value
|
|
79
|
+
when Proc
|
|
80
|
+
@data.each{|row| row.delete(name)} if @data.first&.key?(name)
|
|
81
|
+
@formulae[name] = value
|
|
82
|
+
else
|
|
83
|
+
@formulae.delete(name)
|
|
84
|
+
@data.each{|row| row[name] = value}
|
|
85
|
+
end
|
|
79
86
|
end
|
|
80
87
|
|
|
81
88
|
def +(other)
|
data/test/namo_test.rb
CHANGED
|
@@ -560,6 +560,93 @@ describe Namo do
|
|
|
560
560
|
end
|
|
561
561
|
end
|
|
562
562
|
|
|
563
|
+
describe "#[]= polymorphic dispatch" do
|
|
564
|
+
it "registers a formula when assigned a proc (existing behaviour preserved)" do
|
|
565
|
+
namo = Namo.new([{price: 10.0, quantity: 100}])
|
|
566
|
+
namo[:revenue] = proc{|r| r[:price] * r[:quantity]}
|
|
567
|
+
_(namo.derived_dimensions).must_include :revenue
|
|
568
|
+
_(namo.values(:revenue)).must_equal [1000.0]
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
it "clears any data column of the same name when assigned a proc" do
|
|
572
|
+
namo = Namo.new([{x: 1}, {x: 2}])
|
|
573
|
+
_(namo.data_dimensions).must_include :x
|
|
574
|
+
namo[:x] = proc{|r| 99}
|
|
575
|
+
_(namo.data_dimensions).wont_include :x
|
|
576
|
+
_(namo.derived_dimensions).must_include :x
|
|
577
|
+
_(namo.values(:x)).must_equal [99, 99]
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
it "broadcasts a scalar to every row" do
|
|
581
|
+
namo = Namo.new([{a: 1}, {a: 2}, {a: 3}])
|
|
582
|
+
namo[:status] = 'active'
|
|
583
|
+
_(namo.values(:status)).must_equal ['active', 'active', 'active']
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
it "clears any formula of the same name when assigned a scalar" do
|
|
587
|
+
namo = Namo.new([{price: 10.0, quantity: 100}])
|
|
588
|
+
namo[:revenue] = proc{|r| r[:price] * r[:quantity]}
|
|
589
|
+
namo[:revenue] = 0
|
|
590
|
+
_(namo.derived_dimensions).wont_include :revenue
|
|
591
|
+
_(namo.data_dimensions).must_include :revenue
|
|
592
|
+
_(namo.values(:revenue)).must_equal [0]
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
it "broadcasts an array as the value (array is not a proc)" do
|
|
596
|
+
namo = Namo.new([{a: 1}, {a: 2}])
|
|
597
|
+
namo[:weights] = [1, 2, 3]
|
|
598
|
+
_(namo.values(:weights)).must_equal [[1, 2, 3], [1, 2, 3]]
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
it "is last-write-wins: scalar then proc leaves a formula only" do
|
|
602
|
+
namo = Namo.new([{y: 7}])
|
|
603
|
+
namo[:x] = 5
|
|
604
|
+
namo[:x] = proc{|r| r[:y]}
|
|
605
|
+
_(namo.derived_dimensions).must_include :x
|
|
606
|
+
_(namo.data_dimensions).wont_include :x
|
|
607
|
+
_(namo.values(:x)).must_equal [7]
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
it "is last-write-wins: proc then scalar leaves a broadcast value only" do
|
|
611
|
+
namo = Namo.new([{y: 7}])
|
|
612
|
+
namo[:x] = proc{|r| r[:y]}
|
|
613
|
+
namo[:x] = 5
|
|
614
|
+
_(namo.data_dimensions).must_include :x
|
|
615
|
+
_(namo.derived_dimensions).wont_include :x
|
|
616
|
+
_(namo.values(:x)).must_equal [5]
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
it "surfaces a proc-assigned name as derived, not data, exactly once in dimensions" do
|
|
620
|
+
namo = Namo.new([{x: 1}, {x: 2}])
|
|
621
|
+
namo[:x] = proc{|r| 99}
|
|
622
|
+
_(namo.data_dimensions).wont_include :x
|
|
623
|
+
_(namo.derived_dimensions).must_include :x
|
|
624
|
+
_(namo.dimensions.count(:x)).must_equal 1
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
it "surfaces a scalar-assigned name as data, not derived, exactly once in dimensions" do
|
|
628
|
+
namo = Namo.new([{x: 1}])
|
|
629
|
+
namo[:rev] = proc{|r| r[:x]}
|
|
630
|
+
namo[:rev] = 5
|
|
631
|
+
_(namo.data_dimensions).must_include :rev
|
|
632
|
+
_(namo.derived_dimensions).wont_include :rev
|
|
633
|
+
_(namo.dimensions.count(:rev)).must_equal 1
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
it "does not walk rows to clear when no data column of that name exists" do
|
|
637
|
+
namo = Namo.new([{a: 1}])
|
|
638
|
+
namo[:b] = proc{|r| r[:a]}
|
|
639
|
+
_(namo.derived_dimensions).must_include :b
|
|
640
|
+
_(namo.values(:b)).must_equal [1]
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
it "handles an empty Namo without error" do
|
|
644
|
+
namo = Namo.new([])
|
|
645
|
+
namo[:x] = proc{|r| 1}
|
|
646
|
+
_(namo.derived_dimensions).must_include :x
|
|
647
|
+
end
|
|
648
|
+
end
|
|
649
|
+
|
|
563
650
|
describe "#each" do
|
|
564
651
|
it "yields Row objects" do
|
|
565
652
|
rows = []
|