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
|
@@ -0,0 +1,458 @@
|
|
|
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.keys).must_equal [:revenue]
|
|
115
|
+
_(yielded[:revenue].call({price: 10.0, quantity: 100})).must_equal 1000.0
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "returns an enumerator with no block" do
|
|
119
|
+
_(formulae.each).must_be_kind_of Enumerator
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
describe "#delete" do
|
|
124
|
+
it "removes a name" do
|
|
125
|
+
formulae.delete(:revenue)
|
|
126
|
+
_(formulae.key?(:revenue)).must_equal false
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
describe "#merge" do
|
|
131
|
+
it "returns a Formulae combining both sets" do
|
|
132
|
+
merged = formulae.merge(Namo::Formulae.new({cost: cost}))
|
|
133
|
+
_(merged).must_be_kind_of Namo::Formulae
|
|
134
|
+
_(merged.keys.sort).must_equal [:cost, :revenue]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "lets the argument win on a name conflict" do
|
|
138
|
+
other = proc{|r| 0}
|
|
139
|
+
merged = formulae.merge(Namo::Formulae.new({revenue: other}))
|
|
140
|
+
_(merged[:revenue].call({price: 10.0, quantity: 100})).must_equal 0
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "does not mutate the receiver" do
|
|
144
|
+
formulae.merge(Namo::Formulae.new({cost: cost}))
|
|
145
|
+
_(formulae.keys).must_equal [:revenue]
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
describe "#reject" do
|
|
150
|
+
it "returns a Formulae without the rejected names" do
|
|
151
|
+
formulae[:cost] = cost
|
|
152
|
+
rejected = formulae.reject{|name, _| name == :cost}
|
|
153
|
+
_(rejected).must_be_kind_of Namo::Formulae
|
|
154
|
+
_(rejected.keys).must_equal [:revenue]
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
describe "#dup" do
|
|
159
|
+
it "returns an independent Formulae" do
|
|
160
|
+
copy = formulae.dup
|
|
161
|
+
copy[:cost] = cost
|
|
162
|
+
_(formulae.key?(:cost)).must_equal false
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
describe "#to_h" do
|
|
167
|
+
it "returns a copy, not the live store" do
|
|
168
|
+
hash = formulae.to_h
|
|
169
|
+
hash[:cost] = cost
|
|
170
|
+
_(formulae.key?(:cost)).must_equal false
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
describe "Enumerable" do
|
|
175
|
+
it "supports map over name/callable pairs" do
|
|
176
|
+
_(formulae.map{|name, _| name}).must_equal [:revenue]
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it "supports select returning name/callable pairs" do
|
|
180
|
+
formulae[:cost] = cost
|
|
181
|
+
selected = formulae.select{|name, _| name == :cost}
|
|
182
|
+
_(selected.map(&:first)).must_equal [:cost]
|
|
183
|
+
_(selected.first.last.call({quantity: 100})).must_equal 400.0
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
describe "value semantics" do
|
|
188
|
+
it "is == when names match, regardless of proc objects" do
|
|
189
|
+
_(Namo::Formulae.new({revenue: revenue})).must_equal Namo::Formulae.new({revenue: proc{|r| 0}})
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it "is not == when names differ" do
|
|
193
|
+
_(formulae).wont_equal Namo::Formulae.new({cost: cost})
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it "is not == to a bare Hash" do
|
|
197
|
+
_(formulae).wont_equal({revenue: revenue})
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it "is eql? on matching names with differing procs" do
|
|
201
|
+
_(Namo::Formulae.new({revenue: revenue}).eql?(Namo::Formulae.new({revenue: proc{|r| 0}}))).must_equal true
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "hashes equal on matching names with differing procs" do
|
|
205
|
+
_(Namo::Formulae.new({revenue: revenue}).hash).must_equal Namo::Formulae.new({revenue: proc{|r| 0}}).hash
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
describe "constructor" do
|
|
210
|
+
it "defaults to an empty store" do
|
|
211
|
+
_(Namo::Formulae.new.keys).must_equal []
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it "stores a seeded callable as given, resolvable thereafter" do
|
|
215
|
+
formulae = Namo::Formulae.new({revenue: revenue})
|
|
216
|
+
_(formulae[:revenue]).must_be_same_as revenue
|
|
217
|
+
_(formulae.derive(:revenue, {price: 10.0, quantity: 100}, nil)).must_equal 1000.0
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
describe "formulary tier" do
|
|
222
|
+
let(:delta) do
|
|
223
|
+
Module.new do
|
|
224
|
+
include Namo::Formulary
|
|
225
|
+
def signed_volume(row); row[:buys] - row[:sells]; end
|
|
226
|
+
def echo_namo(row, namo); namo; end
|
|
227
|
+
def scaled(row, namo, factor); row[:buys] * factor; end
|
|
228
|
+
def windowed(row, namo, *rest); rest; end
|
|
229
|
+
private
|
|
230
|
+
def helper(row); row[:buys]; end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
let(:untagged) do
|
|
235
|
+
Module.new do
|
|
236
|
+
def signed_volume(row); 0; end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
let(:attached) do
|
|
241
|
+
Namo::Formulae.new.attach(delta)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
describe "#attach" do
|
|
245
|
+
it "returns self" do
|
|
246
|
+
formulae = Namo::Formulae.new
|
|
247
|
+
_(formulae.attach(delta)).must_be_same_as formulae
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
it "raises ArgumentError for a module not tagged Namo::Formulary" do
|
|
251
|
+
_(proc{Namo::Formulae.new.attach(untagged)}).must_raise ArgumentError
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
it "copies the formulary's public methods into the callable store" do
|
|
255
|
+
_(attached.keys.sort).must_equal [:echo_namo, :scaled, :signed_volume, :windowed]
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
it "excludes a private helper" do
|
|
259
|
+
_(attached.keys).wont_include :helper
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
it "binds every attached formulary to a single shared host" do
|
|
263
|
+
other = Module.new do
|
|
264
|
+
include Namo::Formulary
|
|
265
|
+
def momentum(row); row[:buys]; end
|
|
266
|
+
end
|
|
267
|
+
formulae = Namo::Formulae.new.attach(delta).attach(other)
|
|
268
|
+
_(formulae[:signed_volume].receiver).must_be_same_as formulae[:momentum].receiver
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
it "stores a formulary's methods as Methods owned by the module, bound to the shared host" do
|
|
272
|
+
_(attached[:signed_volume]).must_be_kind_of Method
|
|
273
|
+
_(attached[:signed_volume].owner).must_be_same_as delta
|
|
274
|
+
_(attached.derive(:signed_volume, {buys: 60, sells: 40}, nil)).must_equal 20
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
it "lets a []= overwrite take effect after an anonymous formulary attached the name" do
|
|
278
|
+
formulae = Namo::Formulae.new
|
|
279
|
+
formulae[:signed_volume] = proc{|r| 1}
|
|
280
|
+
formulae.attach(delta)
|
|
281
|
+
formulae[:signed_volume] = proc{|r| 0}
|
|
282
|
+
_(formulae.derive(:signed_volume, {buys: 60, sells: 40}, nil)).must_equal 0
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
describe "#<<" do
|
|
287
|
+
it "attaches a formulary, the same as #attach" do
|
|
288
|
+
formulae = Namo::Formulae.new
|
|
289
|
+
formulae << delta
|
|
290
|
+
_(formulae.keys).must_include :signed_volume
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
it "returns self for chaining" do
|
|
294
|
+
formulae = Namo::Formulae.new
|
|
295
|
+
_(formulae << delta).must_be_same_as formulae
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
describe "#key?" do
|
|
300
|
+
it "is true for a formulary method name" do
|
|
301
|
+
_(attached.key?(:signed_volume)).must_equal true
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
it "is false for a private helper" do
|
|
305
|
+
_(attached.key?(:helper)).must_equal false
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
it "is false for an absent name" do
|
|
309
|
+
_(attached.key?(:missing)).must_equal false
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
describe "#required_parameter_count" do
|
|
314
|
+
it "counts a row-scoped formulary method" do
|
|
315
|
+
_(attached.required_parameter_count(:signed_volume)).must_equal 1
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
it "counts a two-arity formulary method" do
|
|
319
|
+
_(attached.required_parameter_count(:echo_namo)).must_equal 2
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
it "counts a parameterised formulary method" do
|
|
323
|
+
_(attached.required_parameter_count(:scaled)).must_equal 3
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
it "excludes the splat for a variadic formulary method" do
|
|
327
|
+
_(attached.required_parameter_count(:windowed)).must_equal 2
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
describe "#derive" do
|
|
332
|
+
it "resolves a row-scoped formulary method to its value" do
|
|
333
|
+
_(attached.derive(:signed_volume, {buys: 60, sells: 40}, nil)).must_equal 20
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
it "resolves a two-arity formulary method with the namo context" do
|
|
337
|
+
namo = Object.new
|
|
338
|
+
_(attached.derive(:echo_namo, {buys: 60, sells: 40}, namo)).must_be_same_as namo
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
it "raises when a two-arity formulary method has no namo context" do
|
|
342
|
+
error = _(proc{attached.derive(:echo_namo, {buys: 60, sells: 40}, nil)}).must_raise ArgumentError
|
|
343
|
+
_(error.message).must_match(/echo_namo/)
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
it "resolves the most-recently attached formulary on a name collision" do
|
|
347
|
+
alt = Module.new do
|
|
348
|
+
include Namo::Formulary
|
|
349
|
+
def signed_volume(row); 999; end
|
|
350
|
+
end
|
|
351
|
+
formulae = Namo::Formulae.new.attach(delta).attach(alt)
|
|
352
|
+
_(formulae.derive(:signed_volume, {buys: 60, sells: 40}, nil)).must_equal 999
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
it "brings a shadowed formulary's definition back when it is re-attached (binds the module's own method, not the shared host's ancestry order)" do
|
|
356
|
+
alt = Module.new do
|
|
357
|
+
include Namo::Formulary
|
|
358
|
+
def signed_volume(row); 999; end
|
|
359
|
+
end
|
|
360
|
+
formulae = Namo::Formulae.new.attach(delta).attach(alt).attach(delta)
|
|
361
|
+
_(formulae.derive(:signed_volume, {buys: 60, sells: 40}, nil)).must_equal 20
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
describe "#detach" do
|
|
366
|
+
it "returns self for a Symbol" do
|
|
367
|
+
_(attached.detach(:signed_volume)).must_be_same_as attached
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
it "returns self for a Module" do
|
|
371
|
+
_(attached.detach(delta)).must_be_same_as attached
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
it "removes the named formula, leaving the others" do
|
|
375
|
+
attached.detach(:signed_volume)
|
|
376
|
+
_(attached.key?(:signed_volume)).must_equal false
|
|
377
|
+
_(attached.key?(:echo_namo)).must_equal true
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
it "is a no-op on an absent name, still returning self" do
|
|
381
|
+
_(attached.detach(:missing)).must_be_same_as attached
|
|
382
|
+
_(attached.key?(:signed_volume)).must_equal true
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
it "removes exactly the formulary's public method names via the Module arm" do
|
|
386
|
+
attached.detach(delta)
|
|
387
|
+
_(attached.keys).must_equal []
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
it "leaves a []= formula not among the formulary's methods when detaching the module" do
|
|
391
|
+
attached[:extra] = proc{|r| 0}
|
|
392
|
+
attached.detach(delta)
|
|
393
|
+
_(attached.key?(:extra)).must_equal true
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
it "deletes a []= overwrite occupying a formulary-method name" do
|
|
397
|
+
attached[:signed_volume] = proc{|r| 0}
|
|
398
|
+
attached.detach(delta)
|
|
399
|
+
_(attached.key?(:signed_volume)).must_equal false
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
it "raises ArgumentError for an untagged module" do
|
|
403
|
+
_(proc{attached.detach(untagged)}).must_raise ArgumentError
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
it "raises TypeError for a String, naming the class" do
|
|
407
|
+
error = _(proc{attached.detach("signed_volume")}).must_raise TypeError
|
|
408
|
+
_(error.message).must_match(/String/)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
it "raises TypeError for a Hash, naming the class" do
|
|
412
|
+
error = _(proc{attached.detach({})}).must_raise TypeError
|
|
413
|
+
_(error.message).must_match(/Hash/)
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
it "chains, removing both named formulae" do
|
|
417
|
+
formulae[:cost] = cost
|
|
418
|
+
formulae.detach(:revenue).detach(:cost)
|
|
419
|
+
_(formulae.keys).must_equal []
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
describe "carry-through" do
|
|
424
|
+
it "preserves attached formularies through #dup" do
|
|
425
|
+
_(attached.dup.keys).must_include :signed_volume
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
it "preserves attached formularies through #reject" do
|
|
429
|
+
_(attached.reject{|_name, _| false}.keys).must_include :signed_volume
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
it "carries both sides' formularies through #merge" do
|
|
433
|
+
other_delta = Module.new do
|
|
434
|
+
include Namo::Formulary
|
|
435
|
+
def momentum(row); row[:buys]; end
|
|
436
|
+
end
|
|
437
|
+
merged = attached.merge(Namo::Formulae.new.attach(other_delta))
|
|
438
|
+
_(merged.keys).must_include :momentum
|
|
439
|
+
_(merged.keys).must_include :signed_volume
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
describe "#inspect" do
|
|
445
|
+
it "renders the formula names" do
|
|
446
|
+
formulae = Namo::Formulae.new
|
|
447
|
+
formulae[:revenue] = proc{|row| row[:price]}
|
|
448
|
+
_(formulae.inspect).must_equal "#<Namo::Formulae [:revenue]>"
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
it "does not render the callables" do
|
|
452
|
+
formulae = Namo::Formulae.new
|
|
453
|
+
formulae[:revenue] = proc{|row| row[:price]}
|
|
454
|
+
_(formulae.inspect).wont_match(/Proc/)
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
require 'minitest-spec-context'
|
|
3
|
+
|
|
4
|
+
require_relative '../../lib/namo'
|
|
5
|
+
|
|
6
|
+
describe Namo::NegatedDimension do
|
|
7
|
+
describe "#name" do
|
|
8
|
+
it "returns the original symbol" do
|
|
9
|
+
nd = Namo::NegatedDimension.new(:price)
|
|
10
|
+
_(nd.name).must_equal :price
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|