mon 0.0.2
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/lib/contracts/contract_helpers.rb +12 -0
- data/lib/contracts/future.rb +28 -0
- data/lib/contracts/lazy.rb +27 -0
- data/lib/contracts/list.rb +22 -0
- data/lib/contracts/maybe.rb +15 -0
- data/lib/contracts/monad_contract.rb +57 -0
- data/lib/contracts/reactron.rb +23 -0
- data/lib/contracts/try.rb +23 -0
- data/lib/mon.rb +21 -0
- data/lib/monads/chainable_monad.rb +23 -0
- data/lib/monads/future.rb +154 -0
- data/lib/monads/lazy.rb +200 -0
- data/lib/monads/list.rb +89 -0
- data/lib/monads/maybe.rb +211 -0
- data/lib/monads/monad.rb +11 -0
- data/lib/monads/reactron.rb +175 -0
- data/lib/monads/try.rb +190 -0
- data/spec/contract_spec.rb +310 -0
- data/spec/monad_spec.rb +757 -0
- metadata +77 -0
data/spec/monad_spec.rb
ADDED
@@ -0,0 +1,757 @@
|
|
1
|
+
require_relative '../lib/mon'
|
2
|
+
|
3
|
+
module Mon
|
4
|
+
|
5
|
+
module Monad
|
6
|
+
|
7
|
+
describe "List" do
|
8
|
+
|
9
|
+
describe "[]" do
|
10
|
+
it "takes a sequence and turns it into a list monad" do
|
11
|
+
List[1, 2, 3].to_a.should eql [1, 2, 3]
|
12
|
+
List[].to_a.should eql []
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should preserve arrays" do
|
16
|
+
List[[1, 2, 3]].to_a.should eql [[1, 2, 3]]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should preserve lists" do
|
20
|
+
List[List[1, 2, 3]].to_a.should eql [List[1, 2, 3]]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "==" do
|
25
|
+
it "should compare two List monads" do
|
26
|
+
List[1, 2, 3].should == List[1, 2, 3]
|
27
|
+
List["one", "two"].should == List["one", "two"]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should compare contents if not given a List monad" do
|
31
|
+
List[1, 2, 3].should == [1, 2, 3]
|
32
|
+
List["one", "two"].should == ["one", "two"]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "shouldn't match non-matching List monads" do
|
36
|
+
List[1, 2, 3].should_not == List[3, 2, 1]
|
37
|
+
List["one", "two"].should_not == List["one"]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "shouldn't match contents if not given a List monad" do
|
41
|
+
List[1, 2, 3].should_not == [3, 2, 1]
|
42
|
+
List["one", "two"].should_not == ["two", "one"]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "eql" do
|
47
|
+
it "should compare two List monads" do
|
48
|
+
List[1, 2, 3].should eql List[1, 2, 3]
|
49
|
+
List["one", "two"].should eql List["one", "two"]
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should compare contents if not given a List monad" do
|
53
|
+
List[1, 2, 3].should eql [1, 2, 3]
|
54
|
+
List["one", "two"].should eql ["one", "two"]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "shouldn't match non-matching List monads" do
|
58
|
+
List[1, 2, 3].should_not eql List[3, 2, 1]
|
59
|
+
List["one", "two"].should_not eql List["one"]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "shouldn't match contents if not given a List monad" do
|
63
|
+
List[1, 2, 3].should_not eql [3, 2, 1]
|
64
|
+
List["one", "two"].should_not eql ["two", "one"]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "new" do
|
69
|
+
it "should be protected" do
|
70
|
+
expect { List.new([1, 2, 3]) }.to raise_error(NoMethodError)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "get" do
|
75
|
+
it "should be protected" do
|
76
|
+
expect { List[1, 2, 3].get }.to raise_error(NoMethodError)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "_canBind?" do
|
81
|
+
it "should be protected" do
|
82
|
+
expect { List[1]._canBind? :test }.to raise_error(NoMethodError)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "to_s" do
|
87
|
+
it "should properly handle 0, 1, 3, 4+ items" do
|
88
|
+
List[].to_s.should eql "List[]"
|
89
|
+
List[1].to_s.should eql "List[1]"
|
90
|
+
List[1, 2, 3].to_s.should eql "List[1, 2, 3]"
|
91
|
+
List[1, 2, 3, 4].to_s.should eql "List[1, 2, 3...]"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "list functionality" do
|
96
|
+
it "should pass methods through to the contained object" do
|
97
|
+
(List[1, 2, 3] * 6).to_a.should eql [6, 12, 18]
|
98
|
+
(List["Test", "One", "Two"].downcase).to_a.should eql ["test", "one", "two"]
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should treat missing_methods the same as bind" do
|
102
|
+
(List[1, 2, 3] * 5).to_a.should eql (List[1, 2, 3].bind { |i| i * 5 }.to_a)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should flatten once" do
|
106
|
+
List[1, 2, 3].bind { |i| [i, i * 2] }.to_a.should eql [1, 2, 2, 4, 3, 6]
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should allow for internal transformation" do
|
110
|
+
List[1].bind { |i| Try[i] }.to_a.should eql [Success[1]]
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should allow chaining methods" do
|
114
|
+
(((List[1, 2, 3] + 2) * 3) - 5).to_a.should eql [4, 7, 10]
|
115
|
+
List[1, 6, 9].to_f.div(2).floor.round.should eql [0, 3, 4]
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should work with empty lists" do
|
119
|
+
(List[] * 3 + 5 - 2).should eql List[]
|
120
|
+
(List[].upcase.downcase.reverse).should eql List[]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "monad laws" do
|
125
|
+
it "should obey left identity (but it won't)" do
|
126
|
+
(List[1] * 3).should eql [(1 * 3)]
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should obey right identity" do
|
130
|
+
List[1, 2].bind { |i| i }.should eql List[1, 2]
|
131
|
+
List[1, 2].bind { |i| List[i] }.should eql List[1, 2]
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should obey associativity" do
|
135
|
+
((List[3] * 5) * 9).should eql (List[3] * (5 * 9))
|
136
|
+
((3 * List[5]) * 9).should eql (3 * (List[5] * 9))
|
137
|
+
(List[3] * 5).should eql (3 * List[5])
|
138
|
+
((3 * List[]) * 9).should eql (3 * (List[] * 9))
|
139
|
+
((List[] * 5) * 9).should eql (List[] * (5 * 9))
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe Maybe do
|
145
|
+
describe "[]" do
|
146
|
+
it "should take an arbitrary object" do
|
147
|
+
Maybe[1].or(0).should == 1
|
148
|
+
Maybe["test"].or("").should == "test"
|
149
|
+
Maybe[[1, 2, 3]].or([]).should == [1, 2, 3]
|
150
|
+
Maybe[List[1, 2]].or(List[]).should == List[1, 2]
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should unwrap to one level" do
|
154
|
+
Maybe[Maybe[1]].or(Maybe[false]).should == 1
|
155
|
+
Maybe[Maybe[Maybe[1]]].or(false).should == Maybe[1]
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should return Some/None appropriately" do
|
159
|
+
Maybe[true].should == Some[true]
|
160
|
+
Maybe[false].should == None[]
|
161
|
+
Maybe[nil].should == None[]
|
162
|
+
Maybe[1].should == Some[1]
|
163
|
+
Maybe["test"].should == Some["test"]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "==" do
|
168
|
+
it "should compare internals" do
|
169
|
+
Maybe[1].should == Maybe[1]
|
170
|
+
Maybe["test"].should == Maybe["test"]
|
171
|
+
Maybe[[1, 2, 3]].should == Maybe[[1, 2, 3]]
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should not match when objects don't" do
|
175
|
+
Maybe[1].should_not == Maybe[2]
|
176
|
+
Maybe["test"].should_not == Maybe["other"]
|
177
|
+
Maybe[[1, 2, 3]].should_not == Maybe[[3, 2, 1]]
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should compare provided non-options to internals" do
|
181
|
+
Maybe[1].should == 1
|
182
|
+
Maybe["test"].should == "test"
|
183
|
+
Maybe[[1, 2, 3]].should == [1, 2, 3]
|
184
|
+
Maybe[1].should_not == 2
|
185
|
+
Maybe["test"].should_not == "other"
|
186
|
+
Maybe[[1, 2, 3]].should_not == [3, 2, 1]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "eql" do
|
191
|
+
it "should compare internals" do
|
192
|
+
Maybe[1].should eql Maybe[1]
|
193
|
+
Maybe["test"].should eql Maybe["test"]
|
194
|
+
Maybe[[1, 2, 3]].should eql Maybe[[1, 2, 3]]
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should not match when objects don't" do
|
198
|
+
Maybe[1].should_not eql Maybe[2]
|
199
|
+
Maybe["test"].should_not eql Maybe["other"]
|
200
|
+
Maybe[[1, 2, 3]].should_not eql Maybe[[3, 2, 1]]
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should compare provided non-options to internals" do
|
204
|
+
Maybe[1].should eql 1
|
205
|
+
Maybe["test"].should eql "test"
|
206
|
+
Maybe[[1, 2, 3]].should eql [1, 2, 3]
|
207
|
+
Maybe[1].should_not eql 2
|
208
|
+
Maybe["test"].should_not eql "other"
|
209
|
+
Maybe[[1, 2, 3]].should_not eql [3, 2, 1]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "bind" do
|
214
|
+
it "should apply function to the internals" do
|
215
|
+
Maybe[1].bind { |i| i * 3 }.should == Maybe[3]
|
216
|
+
Maybe["test"].bind { |s| s.upcase }.should == Maybe["TEST"]
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should modify contents of monad" do
|
220
|
+
Maybe[1].bind { |i| List[1] }.or(false).should == List[1]
|
221
|
+
Maybe["test"].bind { |i| [i] }.or([]).should == ["test"]
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe "maybe functionality" do
|
226
|
+
it "should pass methods through to the contained object" do
|
227
|
+
(Maybe[1] * 3).should == Maybe[3]
|
228
|
+
(Maybe["test"].upcase).should == Maybe["TEST"]
|
229
|
+
((Maybe[5] * 10) - 12).should == Maybe[38]
|
230
|
+
Maybe["test"].upcase.reverse.downcase.should == Maybe["tset"]
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should treat missing_methods the same as bind" do
|
234
|
+
(Maybe[5] * 5).should == Maybe[5].bind { |i| i * 5 }
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should flatten once" do
|
238
|
+
Maybe[5].bind { |i| Some[i * 100] }.should == Maybe[500]
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should allow for chained and multibound failures" do
|
242
|
+
(Maybe[nil] * 5 + 2 - 3).should == None[]
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
describe "monad laws" do
|
247
|
+
it "should obey left identity (but it won't)" do
|
248
|
+
(Maybe[1] * 3).should == (1 * 3)
|
249
|
+
end
|
250
|
+
|
251
|
+
it "should obey right identity" do
|
252
|
+
Maybe[1].bind { |i| i }.should == Maybe[1]
|
253
|
+
Maybe[5].bind { |i| Maybe[i] }.should == Maybe[5]
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should obey associativity" do
|
257
|
+
((Maybe[5] * Maybe[3]) * Maybe[5]).should == (Maybe[5] * (Maybe[3] * Maybe[5]))
|
258
|
+
((Maybe[5] * Maybe[3]) * None[]).should == (Maybe[5] * (Maybe[3] * None[]))
|
259
|
+
((Maybe[5] * 3) * Maybe[5]).should == (5 * (Maybe[3] * Maybe[5]))
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe "Try" do
|
265
|
+
describe "[]" do
|
266
|
+
it "should take an arbitrary object" do
|
267
|
+
Try[1].or(0).should == 1
|
268
|
+
Try["test"].or("").should == "test"
|
269
|
+
Try[[1, 2, 3]].or([]).should == [1, 2, 3]
|
270
|
+
Try[List[1, 2]].or(List[]).should == List[1, 2]
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should NOT unwrap" do
|
274
|
+
Try[Try[1]].or(Try[false]).should == Try[1].bind { |i| Try[i] }
|
275
|
+
end
|
276
|
+
|
277
|
+
it "should return Success/Failure appropriately" do
|
278
|
+
Try[true].should == Success[true]
|
279
|
+
Try[false].should == Failure[false]
|
280
|
+
Try[nil].should == Failure[false]
|
281
|
+
Try[1].should == Success[1]
|
282
|
+
Try["test"].should == Try["test"]
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
describe "==" do
|
287
|
+
it "should compare internals" do
|
288
|
+
Try[1].should == Try[1]
|
289
|
+
Try["test"].should == Try["test"]
|
290
|
+
Try[[1, 2, 3]].should == Try[[1, 2, 3]]
|
291
|
+
end
|
292
|
+
|
293
|
+
it "should not match when objects don't" do
|
294
|
+
Try[1].should_not == Try[2]
|
295
|
+
Try["test"].should_not == Try["other"]
|
296
|
+
Try[[1, 2, 3]].should_not == Try[[3, 2, 1]]
|
297
|
+
end
|
298
|
+
|
299
|
+
it "should compare provided non-options to internals" do
|
300
|
+
Try[1].should == 1
|
301
|
+
Try["test"].should == "test"
|
302
|
+
Try[[1, 2, 3]].should == [1, 2, 3]
|
303
|
+
Try[1].should_not == 2
|
304
|
+
Try["test"].should_not == "other"
|
305
|
+
Try[[1, 2, 3]].should_not == [3, 2, 1]
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
describe "eql" do
|
310
|
+
it "should compare internals" do
|
311
|
+
Try[1].should eql Try[1]
|
312
|
+
Try["test"].should eql Try["test"]
|
313
|
+
Try[[1, 2, 3]].should eql Try[[1, 2, 3]]
|
314
|
+
end
|
315
|
+
|
316
|
+
it "should not match when objects don't" do
|
317
|
+
Try[1].should_not eql Try[2]
|
318
|
+
Try["test"].should_not eql Try["other"]
|
319
|
+
Try[[1, 2, 3]].should_not eql Try[[3, 2, 1]]
|
320
|
+
end
|
321
|
+
|
322
|
+
it "should compare provided non-options to internals" do
|
323
|
+
Try[1].should eql 1
|
324
|
+
Try["test"].should eql "test"
|
325
|
+
Try[[1, 2, 3]].should eql [1, 2, 3]
|
326
|
+
Try[1].should_not eql 2
|
327
|
+
Try["test"].should_not eql "other"
|
328
|
+
Try[[1, 2, 3]].should_not eql [3, 2, 1]
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
describe "bind" do
|
333
|
+
it "should apply function to the internals" do
|
334
|
+
Try[1].bind { |i| i * 3 }.should == Try[3]
|
335
|
+
Try["test"].bind { |s| s.upcase }.should == Try["TEST"]
|
336
|
+
end
|
337
|
+
|
338
|
+
it "should modify contents of monad" do
|
339
|
+
Try[1].bind { |i| List[1] }.or(false).should == List[1]
|
340
|
+
Try["test"].bind { |i| [i] }.or([]).should == ["test"]
|
341
|
+
end
|
342
|
+
|
343
|
+
it "should catch exceptions" do
|
344
|
+
expect(Try[1].bind { |i| i / 0 }).to be_a Failure
|
345
|
+
expect(Try[1].bind { |i| i * 5 }).to be_a Success
|
346
|
+
end
|
347
|
+
|
348
|
+
it "should allow chains even for failures" do
|
349
|
+
expect(Try[1].bind { |i| i / 0 } * 7 + 3).to be_a Failure
|
350
|
+
expect(Try[5].bind { |i| i * 5 } + 6 - 3).to be_a Success
|
351
|
+
expect(Try[10].bind { |i| i / 0 }.bind { |i| i + 3}).to be_a Failure
|
352
|
+
expect(Try[10].bind { |i| i + 0 }.bind { |i| i + 3}).to be_a Success
|
353
|
+
end
|
354
|
+
|
355
|
+
it "should preserve exceptions" do
|
356
|
+
expect{(Try[5].bind { |i| i / 0 } * 7 + 3).orFail}.to raise_error(ZeroDivisionError)
|
357
|
+
(Try[5].bind { |i| i / 1 } * 7 + 3).orFail.should == 38
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
describe "try functionality" do
|
362
|
+
it "should pass methods through to the contained object" do
|
363
|
+
(Try[1] * 3).should == Try[3]
|
364
|
+
(Try["test"].upcase).should == Try["TEST"]
|
365
|
+
((Try[5] * 10) - 12).should == Try[38]
|
366
|
+
Try["test"].upcase.reverse.downcase.should == Try["tset"]
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should treat missing_methods the same as bind" do
|
370
|
+
(Try[5] * 5).should == Try[5].bind { |i| i * 5 }
|
371
|
+
end
|
372
|
+
|
373
|
+
it "should flatten once" do
|
374
|
+
Try[5].bind { |i| Some[i * 100] }.should == Try[500]
|
375
|
+
end
|
376
|
+
|
377
|
+
it "should allow for chained and multibound failures" do
|
378
|
+
(Try[nil] * 5 + 2 - 3).should == Failure[false]
|
379
|
+
end
|
380
|
+
|
381
|
+
it "should catch exceptions" do
|
382
|
+
expect(Try.to(5) { |i| i / 0 }).to be_a Failure
|
383
|
+
expect(Try.to(5) { |i| i / 5 }).to be_a Success
|
384
|
+
Try.to(5) { |i| i * 5 }.should == Success[25]
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
describe "monad laws" do
|
389
|
+
it "should obey left identity (but it won't)" do
|
390
|
+
(Try[1] * 3).should == (1 * 3)
|
391
|
+
end
|
392
|
+
|
393
|
+
it "should obey right identity" do
|
394
|
+
Try[1].bind { |i| i }.should == Try[1]
|
395
|
+
Try[5].bind { |i| Try[i] }.should == Try[5]
|
396
|
+
end
|
397
|
+
|
398
|
+
it "should obey associativity" do
|
399
|
+
((Try[5] * Try[3]) * Try[5]).should == (Try[5] * (Try[3] * Try[5]))
|
400
|
+
((Try[5] * Try[3]) * Failure["Nope!"]).should == (Try[5] * (Try[3] * Failure["Nope!"]))
|
401
|
+
((5 * Try[3]) * Try[5]).should == (Try[5] * (3 * Try[5]))
|
402
|
+
((5 * 3) * Try[5]).should == (5 * (3 * Try[5]))
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
describe "Lazy" do
|
408
|
+
describe "[]" do
|
409
|
+
it "should wrap an object" do
|
410
|
+
Lazy.eventually(1) { |i| 1 }.should == 1
|
411
|
+
Lazy.eventually([1, 2, 3]) { |l| l } == [1, 2, 3]
|
412
|
+
end
|
413
|
+
|
414
|
+
it "should return Pending/Final appropriately" do
|
415
|
+
expect(Lazy[1]).to be_a Final
|
416
|
+
expect(Lazy["testing"]).to be_a Final
|
417
|
+
expect(Lazy.eventually { "testing" }).to be_a Pending
|
418
|
+
expect(Lazy.eventually { [1, 2] }).to be_a Pending
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
describe "==" do
|
423
|
+
it "should compare internals" do
|
424
|
+
Lazy[1].should == Lazy[1]
|
425
|
+
Lazy[[1, 2, 3]].should == Lazy[[1, 2, 3]]
|
426
|
+
end
|
427
|
+
|
428
|
+
it "should compare pending with final" do
|
429
|
+
Lazy.eventually { 1 }.should == Lazy[1]
|
430
|
+
Lazy.eventually { [1, 2, 3] }.should == Lazy[[1, 2, 3]]
|
431
|
+
end
|
432
|
+
|
433
|
+
it "should not match when objects don't" do
|
434
|
+
Lazy[1].should_not == Lazy[2]
|
435
|
+
Lazy.eventually { 1 }.should_not == Lazy.eventually { 2 }
|
436
|
+
Lazy.eventually { 2 }.should_not == Lazy[1]
|
437
|
+
end
|
438
|
+
|
439
|
+
it "should compare provided non-Lazy to internals" do
|
440
|
+
Lazy[1].should == 1
|
441
|
+
Lazy["test"].should == "test"
|
442
|
+
Lazy[[1, 2, 3]].should == [1, 2, 3]
|
443
|
+
Lazy[].should_not == 1
|
444
|
+
Lazy[1].should_not == 2
|
445
|
+
Lazy.eventually { 1 }.should == 1
|
446
|
+
Lazy.eventually { "test" }.should == "test"
|
447
|
+
Lazy.eventually { [1, 2, 3] }.should == [1, 2, 3]
|
448
|
+
Lazy.eventually { }.should_not == 1
|
449
|
+
Lazy.eventually { 1 }.should_not == 2
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
describe "bind" do
|
454
|
+
it "should create a pending calculation" do
|
455
|
+
expect(Lazy[5].bind { |i| i * 5 }).to be_a Pending
|
456
|
+
expect(Lazy[10].bind { |i| i * 5 }.bind { |i| i * 7 }).to be_a Pending
|
457
|
+
end
|
458
|
+
|
459
|
+
it "should finalize correctly" do
|
460
|
+
Lazy[5].bind { |i| i * 5 }.should == Lazy[25]
|
461
|
+
Lazy[1].bind { |i| i * 7 }.bind { |i| i * 2 }.should == Lazy[14]
|
462
|
+
Lazy["test"].bind { |s| s.upcase }.bind { |s| s.reverse }.should == Lazy["TSET"]
|
463
|
+
end
|
464
|
+
|
465
|
+
it "should be able to modify monad contents" do
|
466
|
+
Lazy[5].bind { |i| Try[i] }.should == Lazy[Try[5]]
|
467
|
+
Lazy["test"].bind { |i| [i] }.should == Lazy[["test"]]
|
468
|
+
end
|
469
|
+
|
470
|
+
it "should flatten by one level" do
|
471
|
+
Lazy[5].bind { |i| Lazy[i] }.should == Lazy[5]
|
472
|
+
Lazy.eventually { [1, 2, 3] }.bind { |i| Lazy[i] }.should == Lazy[[1, 2, 3]]
|
473
|
+
end
|
474
|
+
|
475
|
+
end
|
476
|
+
|
477
|
+
describe "functionality" do
|
478
|
+
it "should pass methods through to the contained object" do
|
479
|
+
(Lazy[1] * 3).should == Lazy[3]
|
480
|
+
(Lazy["test"].upcase.reverse).should == Lazy["TSET"]
|
481
|
+
(Lazy["this is a test"][0,4]).should == Lazy["this"]
|
482
|
+
end
|
483
|
+
|
484
|
+
it "shouldn't execute until necessary" do
|
485
|
+
expect(Lazy[1] * 3).to be_a Pending
|
486
|
+
expect(Lazy["test"].upcase.reverse).to be_a Pending
|
487
|
+
expect(Lazy["this is a test"][0,4]).to be_a Pending
|
488
|
+
end
|
489
|
+
|
490
|
+
it "should treat missing_methods the same as bind" do
|
491
|
+
(Lazy[5] * 5).should == Lazy[5].bind { |i| i * 5 }
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
describe "monad laws" do
|
496
|
+
it "should obey left identity (but it won't)" do
|
497
|
+
(Lazy[1] * 3).should == (1 * 3)
|
498
|
+
end
|
499
|
+
|
500
|
+
it "should obey right identity" do
|
501
|
+
Lazy[1].bind { |i| i }.should == Lazy[1]
|
502
|
+
Lazy[5].bind { |i| Lazy[i] }.should == Lazy[5]
|
503
|
+
end
|
504
|
+
|
505
|
+
it "should obey associativity" do
|
506
|
+
((Lazy[5] * Lazy[3]) * Lazy[5]).should == (Lazy[5] * (Lazy[3] * Lazy[5]))
|
507
|
+
((Lazy[3] * 5) * 9).should eql (Lazy[3] * (5 * 9))
|
508
|
+
((3 * Lazy[5]) * 9).should eql (3 * (Lazy[5] * 9))
|
509
|
+
(Lazy[3] * 5).should eql (3 * Lazy[5])
|
510
|
+
((3 * Lazy[7]) * 9).should eql (3 * (Lazy[7] * 9))
|
511
|
+
((Lazy[7] * 5) * 9).should eql (Lazy[7] * (5 * 9))
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
describe "Future" do
|
517
|
+
describe "[]" do
|
518
|
+
it "should wrap an object" do
|
519
|
+
Future[1].should == 1
|
520
|
+
Future.eventually(1) { |i| 1 }.should == 1
|
521
|
+
Future[[1, 2, 3]].should == [1, 2, 3]
|
522
|
+
Future.eventually([1, 2, 3]) { |l| l } == [1, 2, 3]
|
523
|
+
end
|
524
|
+
|
525
|
+
it "should return Complete/Promise appropriately" do
|
526
|
+
expect(Future[1]).to be_a FutureComplete
|
527
|
+
expect(Future["testing"]).to be_a FutureComplete
|
528
|
+
expect(Future.eventually { "testing" }).to be_a FuturePromise
|
529
|
+
expect(Future.eventually { [1, 2] }).to be_a FuturePromise
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
describe "==" do
|
534
|
+
it "should compare internals" do
|
535
|
+
Future[1].should == Future[1]
|
536
|
+
Future[[1, 2, 3]].should == Future[[1, 2, 3]]
|
537
|
+
end
|
538
|
+
|
539
|
+
it "should compare pending with final" do
|
540
|
+
Future.eventually { 1 }.should == Future[1]
|
541
|
+
Future.eventually { [1, 2, 3] }.should == Future[[1, 2, 3]]
|
542
|
+
end
|
543
|
+
|
544
|
+
it "should not match when objects don't" do
|
545
|
+
Future[1].should_not == Future[2]
|
546
|
+
Future.eventually { 1 }.should_not == Future.eventually { 2 }
|
547
|
+
Future.eventually { 2 }.should_not == Future[1]
|
548
|
+
end
|
549
|
+
|
550
|
+
it "should compare provided non-Future to internals" do
|
551
|
+
Future[1].should == 1
|
552
|
+
Future["test"].should == "test"
|
553
|
+
Future[[1, 2, 3]].should == [1, 2, 3]
|
554
|
+
Future[1].should_not == 2
|
555
|
+
Future.eventually { 1 }.should == 1
|
556
|
+
Future.eventually { "test" }.should == "test"
|
557
|
+
Future.eventually { [1, 2, 3] }.should == [1, 2, 3]
|
558
|
+
Future.eventually { }.should_not == 1
|
559
|
+
Future.eventually { 1 }.should_not == 2
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
describe "bind" do
|
564
|
+
it "should create a pending calculation" do
|
565
|
+
expect(Future[5].bind { |i| i * 5 }).to be_a FuturePromise
|
566
|
+
expect(Future[10].bind { |i| i * 5 }.bind { |i| i * 7 }).to be_a FuturePromise
|
567
|
+
end
|
568
|
+
|
569
|
+
it "should finalize correctly" do
|
570
|
+
Future[5].bind { |i| i * 5 }.should == Future[25]
|
571
|
+
Future[1].bind { |i| i * 7 }.bind { |i| i * 2 }.should == Future[14]
|
572
|
+
Future["test"].bind { |s| s.upcase }.bind { |s| s.reverse }.should == Future["TSET"]
|
573
|
+
end
|
574
|
+
|
575
|
+
it "should be able to modify monad contents" do
|
576
|
+
Future[5].bind { |i| Try[i] }.should == Future[Try[5]]
|
577
|
+
Future["test"].bind { |i| [i] }.should == Future[["test"]]
|
578
|
+
end
|
579
|
+
|
580
|
+
it "should flatten by one level" do
|
581
|
+
Future[5].bind { |i| Future[i] }.should == Future[5]
|
582
|
+
Future.eventually { [1, 2, 3] }.bind { |i| Future[i] }.should == Future[[1, 2, 3]]
|
583
|
+
end
|
584
|
+
|
585
|
+
end
|
586
|
+
|
587
|
+
describe "functionality" do
|
588
|
+
it "should pass methods through to the contained object" do
|
589
|
+
(Future[1] * 3).should == Future[3]
|
590
|
+
(Future["test"].upcase.reverse).should == Future["TSET"]
|
591
|
+
(Future["this is a test"][0,4]).should == Future["this"]
|
592
|
+
end
|
593
|
+
|
594
|
+
it "should treat missing_methods the same as bind" do
|
595
|
+
(Future[5] * 5).should == Future[5].bind { |i| i * 5 }
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
describe "monad laws" do
|
600
|
+
it "should obey left identity (but it won't)" do
|
601
|
+
(Future[1] * 3).should == (1 * 3)
|
602
|
+
end
|
603
|
+
|
604
|
+
it "should obey right identity" do
|
605
|
+
Future[1].bind { |i| i }.should == Future[1]
|
606
|
+
Future[5].bind { |i| Future[i] }.should == Future[5]
|
607
|
+
end
|
608
|
+
|
609
|
+
it "should obey associativity" do
|
610
|
+
((Future[5] * Future[3]) * Future[5]).should == (Future[5] * (Future[3] * Future[5]))
|
611
|
+
((Future[3] * 5) * 9).should eql (Future[3] * (5 * 9))
|
612
|
+
((3 * Future[5]) * 9).should eql (3 * (Future[5] * 9))
|
613
|
+
(Future[3] * 5).should eql (3 * Future[5])
|
614
|
+
((3 * Future[7]) * 9).should eql (3 * (Future[7] * 9))
|
615
|
+
((Future[7] * 5) * 9).should eql (Future[7] * (5 * 9))
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
describe "Reactron" do
|
621
|
+
describe "[]" do
|
622
|
+
it "should wrap an object" do
|
623
|
+
React[1].should == 1
|
624
|
+
React[[1, 2, 3]].should == [1, 2, 3]
|
625
|
+
end
|
626
|
+
|
627
|
+
it "should return Pending/Final appropriately" do
|
628
|
+
expect(React[1]).to be_a Reactron
|
629
|
+
expect(React["testing"]).to be_a Reactron
|
630
|
+
expect(React[5] * 2).to be_a Reactor
|
631
|
+
expect(React["test"].upcase).to be_a Reactor
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
635
|
+
describe "==" do
|
636
|
+
it "should compare internals" do
|
637
|
+
React[1].should == React[1]
|
638
|
+
React[[1, 2, 3]].should == React[[1, 2, 3]]
|
639
|
+
end
|
640
|
+
|
641
|
+
it "should compare reactor with reactron" do
|
642
|
+
(React[5] * 10).should == React[50]
|
643
|
+
end
|
644
|
+
|
645
|
+
it "should not match when objects don't" do
|
646
|
+
React[1].should_not == React[2]
|
647
|
+
(React[2] * 4).should_not == React[9]
|
648
|
+
end
|
649
|
+
|
650
|
+
it "should compare provided non-React to internals" do
|
651
|
+
React[1].should == 1
|
652
|
+
React["test"].should == "test"
|
653
|
+
(React[1] * 5).should == 5
|
654
|
+
React["test"].upcase.should == "TEST"
|
655
|
+
React[[1, 2, 3]].should == [1, 2, 3]
|
656
|
+
React[1].should_not == 2
|
657
|
+
React[[1, 2, 3]].map { |i| i * 7 }.should == [7, 14, 21]
|
658
|
+
React["test"].reverse.should_not == "test"
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
describe "bind" do
|
663
|
+
it "should create a pending calculation" do
|
664
|
+
expect(React[5].bind { |i| i * 5 }).to be_a Reactor
|
665
|
+
expect(React[10].bind { |i| i * 5 }.bind { |i| i * 7 }).to be_a Reactor
|
666
|
+
end
|
667
|
+
|
668
|
+
it "should finalize correctly" do
|
669
|
+
React[5].bind { |i| i * 5 }.should == React[25]
|
670
|
+
React[1].bind { |i| i * 7 }.bind { |i| i * 2 }.should == React[14]
|
671
|
+
React["test"].bind { |s| s.upcase }.bind { |s| s.reverse }.should == React["TSET"]
|
672
|
+
end
|
673
|
+
|
674
|
+
it "should be able to modify monad contents" do
|
675
|
+
React[5].bind { |i| Try[i] }.should == React[Try[5]]
|
676
|
+
React["test"].bind { |i| [i] }.should == React[["test"]]
|
677
|
+
end
|
678
|
+
|
679
|
+
it "should flatten by one level" do
|
680
|
+
React[5].bind { |i| React[i] }.should == React[5]
|
681
|
+
React[[1, 2, 3]].bind { |i| React[i] }.should == React[[1, 2, 3]]
|
682
|
+
end
|
683
|
+
|
684
|
+
end
|
685
|
+
|
686
|
+
describe "functionality" do
|
687
|
+
it "should pass methods through to the contained object" do
|
688
|
+
(React[1] * 3).should == React[3]
|
689
|
+
(React["test"].upcase.reverse).should == React["TSET"]
|
690
|
+
(React["this is a test"][0,4]).should == React["this"]
|
691
|
+
end
|
692
|
+
|
693
|
+
it "shouldn't execute until necessary" do
|
694
|
+
expect(React[1] * 3).to be_a Reactor
|
695
|
+
expect(React["test"].upcase.reverse).to be_a Reactor
|
696
|
+
expect(React["this is a test"][0,4]).to be_a Reactor
|
697
|
+
end
|
698
|
+
|
699
|
+
it "should treat missing_methods the same as bind" do
|
700
|
+
(React[5] * 5).should == React[5].bind { |i| i * 5 }
|
701
|
+
end
|
702
|
+
|
703
|
+
it "should chain reactions together" do
|
704
|
+
x = React[5]
|
705
|
+
y = x * 10
|
706
|
+
z = y * 10
|
707
|
+
a = x * 5
|
708
|
+
expect(x.should == 5)
|
709
|
+
expect(y.should == 50)
|
710
|
+
expect(z.should == 500)
|
711
|
+
expect(a.should == 25)
|
712
|
+
x << 10
|
713
|
+
expect(x.should == 10)
|
714
|
+
expect(y.should == 100)
|
715
|
+
expect(z.should == 1000)
|
716
|
+
expect(a.should == 50)
|
717
|
+
end
|
718
|
+
|
719
|
+
it "should successfully combine reactrons" do
|
720
|
+
x = React[5]
|
721
|
+
y = React[7]
|
722
|
+
z = x * y
|
723
|
+
expect(z.should == 35)
|
724
|
+
x << 2
|
725
|
+
expect(z.should == 14)
|
726
|
+
y << 3
|
727
|
+
expect(z.should == 6)
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
731
|
+
describe "monad laws" do
|
732
|
+
it "should obey left identity (but it won't)" do
|
733
|
+
(React[1] * 3).should == (1 * 3)
|
734
|
+
end
|
735
|
+
|
736
|
+
it "should obey right identity" do
|
737
|
+
React[1].bind { |i| i }.should == React[1]
|
738
|
+
React[5].bind { |i| React[i] }.should == React[5]
|
739
|
+
end
|
740
|
+
|
741
|
+
it "should obey associativity" do
|
742
|
+
((React[5] * React[3]) * React[5]).should == (React[5] * (React[3] * React[5]))
|
743
|
+
((React[3] * 5) * 9).should eql (React[3] * (5 * 9))
|
744
|
+
((3 * React[5]) * 9).should eql (3 * (React[5] * 9))
|
745
|
+
(React[3] * 5).should eql (3 * React[5])
|
746
|
+
((3 * React[7]) * 9).should eql (3 * (React[7] * 9))
|
747
|
+
((React[7] * 5) * 9).should eql (React[7] * (5 * 9))
|
748
|
+
end
|
749
|
+
end
|
750
|
+
end
|
751
|
+
|
752
|
+
describe "State" do
|
753
|
+
|
754
|
+
end
|
755
|
+
|
756
|
+
end
|
757
|
+
end
|