namo 0.20.0 → 0.22.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 +51 -0
- data/lib/Namo/Formulae.rb +96 -0
- data/lib/Namo/Row.rb +6 -25
- data/lib/Namo/VERSION.rb +1 -1
- data/lib/namo.rb +2 -1
- data/test/Namo/Formulae_test.rb +211 -0
- data/test/Namo/Row_test.rb +1 -1
- data/test/namo_test.rb +13 -2
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e6342f1cab3b5811a97d92be8ef7ba34a81191bd97a6ee2c606f8adc7d9a0d47
|
|
4
|
+
data.tar.gz: ce7db23d2907e33a80d1e263b4b9987645a6c1c47979b7f3311bf519d8f9e738
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5c6c26eda0a637af215eef9cab0fe369769af3e945b3a1cf980788bd34be97abeefb3cf29fb208012556c5df9e46c212c9476dc222b0f3917404c7db3003f21a
|
|
7
|
+
data.tar.gz: f71fd3ed7b8e8068b55fbb8379cd361c91c84ca1cb4fe823ff72af9fe1c9b8e1247468fb9fd818aa865cac709aa2d22e63f2c866030cacee22bf25c5930e7758
|
data/CHANGELOG
CHANGED
|
@@ -1,6 +1,57 @@
|
|
|
1
1
|
CHANGELOG
|
|
2
2
|
_________
|
|
3
3
|
|
|
4
|
+
20260626
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
1. ~ lib/Namo/Formulae.rb: + derive(name, row, namo, *arguments), the formula resolver —
|
|
8
|
+
invocation mechanics lifted whole from Row's formula branch (collection-scoped formulae
|
|
9
|
+
are called (row, namo, *arguments), row-only formulae (row)). + required_parameter_count(name),
|
|
10
|
+
public, a property of the stored callable. collection_scoped? and raise_unless_namo_context
|
|
11
|
+
move in as private; the latter takes namo as a parameter rather than closing over Row's
|
|
12
|
+
@namo. [] and []= are untouched — [] still returns the stored callable.
|
|
13
|
+
2. ~ lib/Namo/Row.rb: #[] delegates formula resolution to
|
|
14
|
+
@formulae.derive(name, self, @namo, *arguments); Row keeps only the fail-fast argument
|
|
15
|
+
guard, the formula-vs-data dispatch (@formulae.key?), and the data fallback (@row[name]).
|
|
16
|
+
collection_scoped?, required_parameter_count, and raise_unless_namo_context are gone
|
|
17
|
+
(now Formulae's). expected_argument_counts sources the count from
|
|
18
|
+
@formulae.required_parameter_count and the fixed-vs-variadic check from
|
|
19
|
+
@formulae[name].arity.
|
|
20
|
+
3. + test/Namo/Formulae_test.rb: + #derive tests (row-only to value, collection-scoped with
|
|
21
|
+
namo context, missing-namo-context raise, parameterised with arguments) and +
|
|
22
|
+
#required_parameter_count tests (row-only, collection-scoped, parameterised, variadic).
|
|
23
|
+
The #[] and #[]= blocks are unchanged — [] still returns the callable.
|
|
24
|
+
4. ~ test/Namo/Row_test.rb: the formulae fixture is a Namo::Formulae, not a bare {} — Row
|
|
25
|
+
now delegates formula resolution to its collaborator (call, required_parameter_count),
|
|
26
|
+
which production always wraps (lib/namo.rb initialize). The data-only equality/match
|
|
27
|
+
tests keep their {} literals: they never resolve a formula, so Hash#key? alone answers.
|
|
28
|
+
5. ~ Namo::VERSION: /0.21.0/0.22.0/
|
|
29
|
+
|
|
30
|
+
20260625
|
|
31
|
+
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.
|
|
32
|
+
|
|
33
|
+
1. + lib/Namo/Formulae.rb: + Namo::Formulae, a thin class wrapping a Hash @store.
|
|
34
|
+
Responds to exactly the subset of Hash that lib/namo.rb and Row send the formula
|
|
35
|
+
collection: [], []=, keys, key?, empty?, each, delete, merge, reject, dup; includes
|
|
36
|
+
Enumerable (each yields [name, callable] pairs, giving map/select/etc. for free).
|
|
37
|
+
Three deliberate choices: to_h returns a copy (@store.dup) so callers cannot mutate
|
|
38
|
+
internals; ==/eql?/hash are names-only (keys.sort), matching Namo's own formula
|
|
39
|
+
equality where two sets with the same names but different proc objects are equivalent;
|
|
40
|
+
merge/reject/dup return a Formulae, not a Hash.
|
|
41
|
+
2. ~ lib/namo.rb: + the require; initialize now normalises the incoming formulae to a
|
|
42
|
+
Formulae (Formulae.new(formulae) unless already one). This is the single behavioural
|
|
43
|
+
seam — every existing internal construction passes a Hash and is wrapped, while
|
|
44
|
+
operator results that now pass a Formulae (merge/dup return Formulae) pass through.
|
|
45
|
+
No other call site changed: each sends a message Formulae answers.
|
|
46
|
+
3. ~ test/namo_test.rb: the two assertions comparing the public #formulae accessor to a
|
|
47
|
+
literal {} now assert emptiness (must_be_empty). The accessor returns a Formulae, not a
|
|
48
|
+
Hash; the formula set being empty is the unchanged behaviour these tests pin. + two
|
|
49
|
+
construction tests pinning the normalisation seam: a Hash given by keyword is wrapped in
|
|
50
|
+
a Formulae, and a Formulae given by keyword passes through unchanged (same object).
|
|
51
|
+
4. + test/Namo/Formulae_test.rb: + unit test pinning the three deliberate choices (to_h
|
|
52
|
+
copy, names-only ==/eql?/hash, the Hash-compatible surface plus Enumerable map/select).
|
|
53
|
+
5. ~ Namo::VERSION: /0.20.0/0.21.0/
|
|
54
|
+
|
|
4
55
|
20260615
|
|
5
56
|
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.
|
|
6
57
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Namo/Formulae.rb
|
|
2
|
+
# Namo::Formulae
|
|
3
|
+
|
|
4
|
+
class Namo
|
|
5
|
+
class Formulae
|
|
6
|
+
include Enumerable
|
|
7
|
+
|
|
8
|
+
def [](name)
|
|
9
|
+
@store[name]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def []=(name, callable)
|
|
13
|
+
@store[name] = callable
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def derive(name, row, namo, *arguments)
|
|
17
|
+
formula = self[name]
|
|
18
|
+
if collection_scoped?(name)
|
|
19
|
+
raise_unless_namo_context(name, namo)
|
|
20
|
+
formula.call(row, namo, *arguments)
|
|
21
|
+
else
|
|
22
|
+
formula.call(row)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def required_parameter_count(name)
|
|
27
|
+
formula = self[name]
|
|
28
|
+
formula.arity >= 0 ? formula.arity : -formula.arity - 1
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def keys
|
|
32
|
+
@store.keys
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def key?(name)
|
|
36
|
+
@store.key?(name)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def empty?
|
|
40
|
+
@store.empty?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def each(&block)
|
|
44
|
+
return enum_for(:each) unless block_given?
|
|
45
|
+
@store.each(&block)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def delete(name)
|
|
49
|
+
@store.delete(name)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def merge(other)
|
|
53
|
+
self.class.new(@store.merge(other.to_h))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def reject(&block)
|
|
57
|
+
self.class.new(@store.reject(&block))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def dup
|
|
61
|
+
self.class.new(@store.dup)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def to_h
|
|
65
|
+
@store.dup
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def ==(other)
|
|
69
|
+
other.is_a?(Formulae) && keys.sort == other.keys.sort
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def eql?(other)
|
|
73
|
+
self == other
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def hash
|
|
77
|
+
keys.sort.hash
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def initialize(store = {})
|
|
83
|
+
@store = store
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def collection_scoped?(name)
|
|
87
|
+
required_parameter_count(name) >= 2
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def raise_unless_namo_context(name, namo)
|
|
91
|
+
unless namo
|
|
92
|
+
raise ArgumentError, "collection-scoped formula #{name.inspect} requires a Namo context, but this Row has none"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
data/lib/Namo/Row.rb
CHANGED
|
@@ -6,13 +6,7 @@ class Namo
|
|
|
6
6
|
def [](name, *arguments)
|
|
7
7
|
raise_unless_expected_arguments(name, arguments)
|
|
8
8
|
if @formulae.key?(name)
|
|
9
|
-
|
|
10
|
-
if collection_scoped?(formula)
|
|
11
|
-
raise_unless_namo_context(name)
|
|
12
|
-
formula.call(self, @namo, *arguments)
|
|
13
|
-
else
|
|
14
|
-
formula.call(self)
|
|
15
|
-
end
|
|
9
|
+
@formulae.derive(name, self, @namo, *arguments)
|
|
16
10
|
else
|
|
17
11
|
@row[name]
|
|
18
12
|
end
|
|
@@ -57,19 +51,12 @@ class Namo
|
|
|
57
51
|
@namo = namo
|
|
58
52
|
end
|
|
59
53
|
|
|
60
|
-
def collection_scoped?(formula)
|
|
61
|
-
required_parameter_count(formula) >= 2
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def required_parameter_count(formula)
|
|
65
|
-
formula.arity >= 0 ? formula.arity : -formula.arity - 1
|
|
66
|
-
end
|
|
67
|
-
|
|
68
54
|
def expected_argument_counts(name)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
55
|
+
return [0, 0] unless @formulae.key?(name)
|
|
56
|
+
count = @formulae.required_parameter_count(name)
|
|
57
|
+
return [0, 0] unless count >= 2
|
|
58
|
+
minimum = count - 2
|
|
59
|
+
maximum = @formulae[name].arity >= 0 ? minimum : nil
|
|
73
60
|
[minimum, maximum]
|
|
74
61
|
end
|
|
75
62
|
|
|
@@ -79,11 +66,5 @@ class Namo
|
|
|
79
66
|
expected = maximum.nil? ? "#{minimum}+" : minimum.to_s
|
|
80
67
|
raise ArgumentError, "wrong number of arguments for #{name.inspect} (given #{arguments.length}, expected #{expected})"
|
|
81
68
|
end
|
|
82
|
-
|
|
83
|
-
def raise_unless_namo_context(name)
|
|
84
|
-
unless @namo
|
|
85
|
-
raise ArgumentError, "collection-scoped formula #{name.inspect} requires a Namo context, but this Row has none"
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
69
|
end
|
|
89
70
|
end
|
data/lib/Namo/VERSION.rb
CHANGED
data/lib/namo.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# Namo
|
|
3
3
|
|
|
4
4
|
require_relative './Namo/NegatedDimension'
|
|
5
|
+
require_relative './Namo/Formulae'
|
|
5
6
|
require_relative './Namo/Row'
|
|
6
7
|
require_relative './Namo/Collection'
|
|
7
8
|
require_relative './Namo/Enumerable'
|
|
@@ -235,7 +236,7 @@ class Namo
|
|
|
235
236
|
|
|
236
237
|
def initialize(positional_data = nil, data: [], formulae: {}, name: nil)
|
|
237
238
|
@data = positional_data || data
|
|
238
|
-
@formulae = formulae
|
|
239
|
+
@formulae = formulae.is_a?(Formulae) ? formulae : Formulae.new(formulae)
|
|
239
240
|
@name = name
|
|
240
241
|
end
|
|
241
242
|
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
require 'minitest-spec-context'
|
|
3
|
+
|
|
4
|
+
require_relative '../../lib/namo'
|
|
5
|
+
|
|
6
|
+
describe Namo::Formulae do
|
|
7
|
+
let(:revenue) do
|
|
8
|
+
proc{|r| r[:price] * r[:quantity]}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
let(:cost) do
|
|
12
|
+
proc{|r| r[:quantity] * 4.0}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
let(:formulae) do
|
|
16
|
+
Namo::Formulae.new({revenue: revenue})
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe "#[]" do
|
|
20
|
+
it "returns the callable stored under a name" do
|
|
21
|
+
_(formulae[:revenue]).must_be_same_as revenue
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "returns nil for an absent name" do
|
|
25
|
+
_(formulae[:missing]).must_be_nil
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "#derive" do
|
|
30
|
+
let(:row) do
|
|
31
|
+
{price: 10.0, quantity: 100}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "resolves a row-only formula to its value" do
|
|
35
|
+
_(formulae.derive(:revenue, row, nil)).must_equal 1000.0
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "resolves a collection-scoped formula with the namo context" do
|
|
39
|
+
namo = Object.new
|
|
40
|
+
formulae[:context] = ->(r, n){n}
|
|
41
|
+
_(formulae.derive(:context, row, namo)).must_be_same_as namo
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "raises ArgumentError naming the formula when a collection-scoped formula has no namo context" do
|
|
45
|
+
formulae[:context] = ->(r, n){n}
|
|
46
|
+
error = _(proc{formulae.derive(:context, row, nil)}).must_raise ArgumentError
|
|
47
|
+
_(error.message).must_match(/context/)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "resolves a parameterised formula with arguments" do
|
|
51
|
+
formulae[:scaled] = ->(r, n, factor){r[:price] * factor}
|
|
52
|
+
_(formulae.derive(:scaled, row, Object.new, 3)).must_equal 30.0
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe "#required_parameter_count" do
|
|
57
|
+
it "counts a row-only formula" do
|
|
58
|
+
_(formulae.required_parameter_count(:revenue)).must_equal 1
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "counts a collection-scoped formula" do
|
|
62
|
+
formulae[:context] = ->(r, n){n}
|
|
63
|
+
_(formulae.required_parameter_count(:context)).must_equal 2
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "counts a parameterised formula" do
|
|
67
|
+
formulae[:scaled] = ->(r, n, factor){r[:price] * factor}
|
|
68
|
+
_(formulae.required_parameter_count(:scaled)).must_equal 3
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "excludes the splat when counting a variadic formula" do
|
|
72
|
+
formulae[:variadic] = ->(r, n, *rest){rest}
|
|
73
|
+
_(formulae.required_parameter_count(:variadic)).must_equal 2
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
describe "#[]=" do
|
|
78
|
+
it "stores a callable under a name" do
|
|
79
|
+
formulae[:cost] = cost
|
|
80
|
+
_(formulae[:cost]).must_be_same_as cost
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe "#keys" do
|
|
85
|
+
it "returns the stored names" do
|
|
86
|
+
_(formulae.keys).must_equal [:revenue]
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
describe "#key?" do
|
|
91
|
+
it "is true for a stored name" do
|
|
92
|
+
_(formulae.key?(:revenue)).must_equal true
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "is false for an absent name" do
|
|
96
|
+
_(formulae.key?(:missing)).must_equal false
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe "#empty?" do
|
|
101
|
+
it "is true with no formulae" do
|
|
102
|
+
_(Namo::Formulae.new).must_be_empty
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "is false with formulae" do
|
|
106
|
+
_(formulae.empty?).must_equal false
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
describe "#each" do
|
|
111
|
+
it "yields name/callable pairs" do
|
|
112
|
+
yielded = {}
|
|
113
|
+
formulae.each{|name, callable| yielded[name] = callable}
|
|
114
|
+
_(yielded).must_equal({revenue: revenue})
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "returns an enumerator with no block" do
|
|
118
|
+
_(formulae.each).must_be_kind_of Enumerator
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
describe "#delete" do
|
|
123
|
+
it "removes a name" do
|
|
124
|
+
formulae.delete(:revenue)
|
|
125
|
+
_(formulae.key?(:revenue)).must_equal false
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
describe "#merge" do
|
|
130
|
+
it "returns a Formulae combining both sets" do
|
|
131
|
+
merged = formulae.merge(Namo::Formulae.new({cost: cost}))
|
|
132
|
+
_(merged).must_be_kind_of Namo::Formulae
|
|
133
|
+
_(merged.keys.sort).must_equal [:cost, :revenue]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "lets the argument win on a name conflict" do
|
|
137
|
+
other = proc{|r| 0}
|
|
138
|
+
merged = formulae.merge(Namo::Formulae.new({revenue: other}))
|
|
139
|
+
_(merged[:revenue]).must_be_same_as other
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "does not mutate the receiver" do
|
|
143
|
+
formulae.merge(Namo::Formulae.new({cost: cost}))
|
|
144
|
+
_(formulae.keys).must_equal [:revenue]
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
describe "#reject" do
|
|
149
|
+
it "returns a Formulae without the rejected names" do
|
|
150
|
+
formulae[:cost] = cost
|
|
151
|
+
rejected = formulae.reject{|name, _| name == :cost}
|
|
152
|
+
_(rejected).must_be_kind_of Namo::Formulae
|
|
153
|
+
_(rejected.keys).must_equal [:revenue]
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
describe "#dup" do
|
|
158
|
+
it "returns an independent Formulae" do
|
|
159
|
+
copy = formulae.dup
|
|
160
|
+
copy[:cost] = cost
|
|
161
|
+
_(formulae.key?(:cost)).must_equal false
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
describe "#to_h" do
|
|
166
|
+
it "returns a copy, not the live store" do
|
|
167
|
+
hash = formulae.to_h
|
|
168
|
+
hash[:cost] = cost
|
|
169
|
+
_(formulae.key?(:cost)).must_equal false
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
describe "Enumerable" do
|
|
174
|
+
it "supports map over name/callable pairs" do
|
|
175
|
+
_(formulae.map{|name, _| name}).must_equal [:revenue]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it "supports select returning name/callable pairs" do
|
|
179
|
+
formulae[:cost] = cost
|
|
180
|
+
_(formulae.select{|name, _| name == :cost}).must_equal [[:cost, cost]]
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
describe "value semantics" do
|
|
185
|
+
it "is == when names match, regardless of proc objects" do
|
|
186
|
+
_(Namo::Formulae.new({revenue: revenue})).must_equal Namo::Formulae.new({revenue: proc{|r| 0}})
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "is not == when names differ" do
|
|
190
|
+
_(formulae).wont_equal Namo::Formulae.new({cost: cost})
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "is not == to a bare Hash" do
|
|
194
|
+
_(formulae).wont_equal({revenue: revenue})
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it "is eql? on matching names with differing procs" do
|
|
198
|
+
_(Namo::Formulae.new({revenue: revenue}).eql?(Namo::Formulae.new({revenue: proc{|r| 0}}))).must_equal true
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it "hashes equal on matching names with differing procs" do
|
|
202
|
+
_(Namo::Formulae.new({revenue: revenue}).hash).must_equal Namo::Formulae.new({revenue: proc{|r| 0}}).hash
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
describe "constructor" do
|
|
207
|
+
it "defaults to an empty store" do
|
|
208
|
+
_(Namo::Formulae.new.keys).must_equal []
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
data/test/Namo/Row_test.rb
CHANGED
data/test/namo_test.rb
CHANGED
|
@@ -47,7 +47,7 @@ describe Namo do
|
|
|
47
47
|
it "produces an empty Namo with no arguments" do
|
|
48
48
|
namo = Namo.new
|
|
49
49
|
_(namo.data).must_equal []
|
|
50
|
-
_(namo.formulae).
|
|
50
|
+
_(namo.formulae).must_be_empty
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
it "accepts keyword formulae with no data" do
|
|
@@ -56,6 +56,17 @@ describe Namo do
|
|
|
56
56
|
_(namo.derived_dimensions).must_equal [:y]
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
it "wraps keyword formulae given as a Hash in a Formulae" do
|
|
60
|
+
namo = Namo.new(formulae: {y: proc{|r| r[:x] * 2}})
|
|
61
|
+
_(namo.formulae).must_be_kind_of Namo::Formulae
|
|
62
|
+
_(namo.formulae.keys).must_equal [:y]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "passes keyword formulae already given as a Formulae through unchanged" do
|
|
66
|
+
formulae = Namo::Formulae.new({y: proc{|r| r[:x] * 2}})
|
|
67
|
+
_(Namo.new(formulae: formulae).formulae).must_be_same_as formulae
|
|
68
|
+
end
|
|
69
|
+
|
|
59
70
|
it "honours an explicit empty positional array over the nil sentinel" do
|
|
60
71
|
_(Namo.new([]).data).must_equal []
|
|
61
72
|
end
|
|
@@ -1010,7 +1021,7 @@ describe Namo do
|
|
|
1010
1021
|
prices[:sma] = sma
|
|
1011
1022
|
projected = prices[:sma]
|
|
1012
1023
|
_(projected.to_a).must_equal [{sma: 10.0}, {sma: 15.0}, {sma: 20.0}]
|
|
1013
|
-
_(projected.formulae).
|
|
1024
|
+
_(projected.formulae).must_be_empty
|
|
1014
1025
|
end
|
|
1015
1026
|
|
|
1016
1027
|
it "returns an instance of self's class" do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: namo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.22.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- thoran
|
|
@@ -66,6 +66,7 @@ files:
|
|
|
66
66
|
- Rakefile
|
|
67
67
|
- lib/Namo/Collection.rb
|
|
68
68
|
- lib/Namo/Enumerable.rb
|
|
69
|
+
- lib/Namo/Formulae.rb
|
|
69
70
|
- lib/Namo/NegatedDimension.rb
|
|
70
71
|
- lib/Namo/Row.rb
|
|
71
72
|
- lib/Namo/VERSION.rb
|
|
@@ -73,6 +74,7 @@ files:
|
|
|
73
74
|
- lib/namo.rb
|
|
74
75
|
- namo.gemspec
|
|
75
76
|
- test/Namo/Collection_test.rb
|
|
77
|
+
- test/Namo/Formulae_test.rb
|
|
76
78
|
- test/Namo/NegatedDimension_test.rb
|
|
77
79
|
- test/Namo/Row_test.rb
|
|
78
80
|
- test/Symbol_test.rb
|
|
@@ -95,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
95
97
|
- !ruby/object:Gem::Version
|
|
96
98
|
version: '0'
|
|
97
99
|
requirements: []
|
|
98
|
-
rubygems_version: 4.0.
|
|
100
|
+
rubygems_version: 4.0.10
|
|
99
101
|
specification_version: 4
|
|
100
102
|
summary: Named dimensional data for Ruby.
|
|
101
103
|
test_files: []
|