totally_lazy 0.0.4 → 0.0.5

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.
data/lib/sequence.rb CHANGED
@@ -6,9 +6,30 @@ end
6
6
 
7
7
  module Sequences
8
8
 
9
+ # Creates a sequence
10
+ #
11
+ # == Parameters:
12
+ # items::
13
+ # Varargs - any valid ruby objects can be supplied
14
+ #
15
+ # == Returns:
16
+ # A sequence
17
+ #
18
+ # == Examples
19
+ #
20
+ # sequence(1,2,3,4).filter(even) # lazily returns 2,4
21
+ # sequence(1,2).map(as_string) # lazily returns "1","2"
22
+ # sequence(1, 2).map_concurrently(to_string) # lazily distributes the work to background threads
23
+ # sequence(1,2,3).take(2) # lazily returns 1,2
24
+ # sequence(1,2,3).drop(2) # lazily returns 3
25
+ # sequence(1,2,3).tail # lazily returns 2,3
26
+ # sequence(1,2,3).head # eagerly returns 1
27
+ # sequence(1,2,3).head_option # eagerly returns an option
28
+ # some(sequence(1,2,3)).get_or_else(empty) # eagerly returns value or else empty sequence
29
+ # sequence(1, 2, 3, 4, 5).filter(where(is greater_than 2).and(is odd)) # lazily returns 3,5
9
30
  def sequence(*items)
10
31
  if items.size == 1
11
- if [Range, Hash].include?(items.first.class)
32
+ if [Range, Hash, Array, Set].include?(items.first.class)
12
33
  Sequence.new(items.first)
13
34
  elsif items.first.nil?
14
35
  empty
@@ -20,10 +41,23 @@ module Sequences
20
41
  end
21
42
  end
22
43
 
44
+ # Creates an empty sequence
45
+ #
46
+ # == Returns:
47
+ # An empty sequence
48
+ #
49
+ # == Examples
50
+ #
51
+ # empty
52
+ # some(sequence(1,2,3)).get_or_else(empty) # eagerly returns value or else empty sequence
23
53
  def empty
24
54
  Empty.new
25
55
  end
26
56
 
57
+ def deserialize(data)
58
+ sequence(data).deserialize
59
+ end
60
+
27
61
  class Sequence < Enumerator
28
62
 
29
63
  include Comparable
@@ -50,7 +84,7 @@ module Sequences
50
84
  def map(predicate=nil, &block)
51
85
  if predicate
52
86
  Sequence.new(self) { |yielder, val|
53
- v = predicate.call(val)
87
+ v = predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates) : predicate.call(val)
54
88
  yielder << v unless v.nil?
55
89
  }
56
90
  else
@@ -62,10 +96,32 @@ module Sequences
62
96
 
63
97
  alias collect map
64
98
 
99
+ # def reduce(operation_or_value=nil)
100
+ # case operation_or_value
101
+ # when Symbol
102
+ # # convert things like reduce(:+) into reduce { |s,e| s + e }
103
+ # return reduce { |s,e| s.send(operation_or_value, e) }
104
+ # when nil
105
+ # acc = nil
106
+ # else
107
+ # acc = operation_or_value
108
+ # end
109
+ #
110
+ # each do |a|
111
+ # if acc.nil?
112
+ # acc = a
113
+ # else
114
+ # acc = yield(acc, a)
115
+ # end
116
+ # end
117
+ #
118
+ # return acc
119
+ # end
120
+
65
121
  def select(predicate=nil, &block)
66
122
  if predicate
67
123
  Sequence.new(self) { |yielder, val|
68
- v = predicate.call(val)
124
+ v = predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates) : predicate.call(val)
69
125
  yielder << v unless v.nil?
70
126
  }
71
127
  else
@@ -80,14 +136,23 @@ module Sequences
80
136
  alias find_all select
81
137
  alias filter select
82
138
 
83
- def reject(&block)
84
- Sequence.new(self) { |yielder, val|
85
- if not block.call(val)
86
- yielder << val
87
- end
88
- }
139
+ def reject(predicate=nil, &block)
140
+ if predicate
141
+ Sequence.new(self) { |yielder, val|
142
+ v = predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates, true) : predicate.call(val, :self, true)
143
+ yielder << v unless v.nil?
144
+ }
145
+ else
146
+ Sequence.new(self) { |yielder, val|
147
+ unless block.call(val)
148
+ yielder << val
149
+ end
150
+ }
151
+ end
89
152
  end
90
153
 
154
+ alias unfilter reject
155
+
91
156
  def grep(pattern)
92
157
  Sequence.new(self) { |yielder, val|
93
158
  if pattern === val
@@ -146,7 +211,6 @@ module Sequences
146
211
  def flat_map(&block)
147
212
  Sequence.new(self) { |yielder, val|
148
213
  ary = block.call(val)
149
- # TODO: check ary is an Array
150
214
  ary.each { |x|
151
215
  yielder << x
152
216
  }
@@ -197,6 +261,10 @@ module Sequences
197
261
  sequence.empty? ? none : some(sequence.entries.last)
198
262
  end
199
263
 
264
+ def contains?(value)
265
+ sequence.empty? ? false : sequence.entries.include?(value)
266
+ end
267
+
200
268
  def tail
201
269
  Sequence.new(Sequence::Generator.new do |g|
202
270
  self.empty? ? raise(NoSuchElementException.new, 'The sequence was empty') : self.drop(1).each { |i| g.yield i }
@@ -218,18 +286,19 @@ module Sequences
218
286
 
219
287
  def transpose
220
288
  Sequence.new(Sequence::Generator.new do |g|
289
+ if self.empty?
290
+ raise(NoSuchElementException.new, 'The sequence was empty')
291
+ else
292
+ raise(Exception.new, 'The subject of transposition must be multidimensional') unless self.to_a.first.is_a?(Array)
293
+ end
221
294
  result = []
222
- max_size = self.to_a.max { |a, b| a.size <=> b.size }.size
295
+ max = option(self.to_a.max { |a, b| a.size <=> b.size })
296
+ max_size = max.get_or_throw(NoSuchElementException, 'The option was empty').size
223
297
  max_size.times do |i|
224
298
  result[i] = [self.to_a.first.size]
225
299
  self.to_a.each_with_index { |r, j| result[i][j] = r[i] }
226
300
  end
227
301
  result
228
- if self.empty?
229
- raise(NoSuchElementException.new, 'The sequence was empty')
230
- else
231
- raise(Exception.new, 'The subject of transposition must be multidimensional') unless self.to_a.first.is_a?(Array)
232
- end
233
302
  result.each { |i| g.yield i }
234
303
  end)
235
304
  end
@@ -237,9 +306,7 @@ module Sequences
237
306
  def join(target_sequence)
238
307
  Sequence.new(Sequence::Generator.new do |g|
239
308
  raise(Exception.new, 'The target (right side) must be a sequence') unless target_sequence.kind_of?(Sequences::Sequence)
240
- elements = self.entries
241
- elements = elements.reject { |i| i.is_a?(Empty) }
242
- (elements << target_sequence.entries).flatten.each { |i| g.yield i }
309
+ self.entries.push(target_sequence.entries).flatten.each { |i| g.yield i unless i.is_a?(Empty) }
243
310
  end)
244
311
  end
245
312
 
@@ -263,7 +330,7 @@ module Sequences
263
330
 
264
331
  def to_seq
265
332
  Sequence.new(Sequence::Generator.new do |g|
266
- self.entries.map { |e| Type.check(e, Sequences::Sequence); e.entries }.flatten.each { |i| g.yield i }
333
+ self.entries.map { |e| Type.responds(e, :entries); e.entries }.flatten.each { |i| g.yield i }
267
334
  end)
268
335
  end
269
336
 
@@ -273,6 +340,24 @@ module Sequences
273
340
  end)
274
341
  end
275
342
 
343
+ def from_arrays
344
+ Sequence.new(Sequence::Generator.new do |g|
345
+ self.entries.map { |e| Type.check(e, Array); e }.flatten.each { |i| g.yield i }
346
+ end)
347
+ end
348
+
349
+ def from_sets
350
+ Sequence.new(Sequence::Generator.new do |g|
351
+ self.entries.map { |e| Type.check(e, Set); e.to_a }.flatten.each { |i| g.yield i }
352
+ end)
353
+ end
354
+
355
+ def in_pairs
356
+ Sequence.new(Sequence::Generator.new do |g|
357
+ self.each_slice(2) { |k, v| g.yield pair(k, v) }
358
+ end)
359
+ end
360
+
276
361
  def to_a
277
362
  execution = {
278
363
  Sequences::Sequence => -> { self.entries.map { |s| s.entries } },
@@ -287,6 +372,26 @@ module Sequences
287
372
  end
288
373
  end
289
374
 
375
+ def marshal_dump
376
+ serialize
377
+ end
378
+
379
+ def marshal_load(data)
380
+ Sequence.new(data).deserialize
381
+ end
382
+
383
+ def serialize
384
+ c = []
385
+ serializer(c, self.entries)
386
+ c
387
+ end
388
+
389
+ def deserialize
390
+ c = []
391
+ deserializer(c, self.entries)
392
+ Sequence.new(c)
393
+ end
394
+
290
395
  def all
291
396
  to_a.flatten
292
397
  end
@@ -299,8 +404,42 @@ module Sequences
299
404
  blank?(sequence[index]) ? none : some(sequence[index])
300
405
  end
301
406
 
407
+ def get_or_throw(index, exception, message='')
408
+ blank?(sequence[index]) ? raise(exception, message) : sequence[index]
409
+ end
410
+
411
+ def drop_nil
412
+ Sequence.new(Sequence::Generator.new do |g|
413
+ self.reject { |e| e.nil? }.each { |i| g.yield i }
414
+ end)
415
+ end
416
+
417
+ def map_concurrently(predicate=nil, options={}, &block)
418
+ if predicate
419
+ Sequence.new(Sequence::Generator.new do |g|
420
+ Parallel.map(self.entries, options) { |val|
421
+ predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates) : predicate.call(val)
422
+ }.each { |i| g.yield i unless i.nil? }
423
+ end)
424
+ else
425
+ Sequence.new(Sequence::Generator.new do |g|
426
+ Parallel.map(self.entries, options) { |val| block.call(val) }.each { |i| g.yield i }
427
+ end)
428
+ end
429
+ end
430
+
431
+ def each_concurrently(options={}, &block)
432
+ Parallel.each(self.entries, options) { |val| block.call(val) }
433
+ end
434
+
435
+ def cycle
436
+ Sequence.new(Sequence::Generator.new do |g|
437
+ self.entries.cycle.each { |i| g.yield i }
438
+ end)
439
+ end
440
+
441
+ protected
302
442
 
303
- private
304
443
  def sequence
305
444
  Sequence.new(self)
306
445
  end
@@ -309,6 +448,63 @@ module Sequences
309
448
  item.respond_to?(:empty?) ? item.empty? : !item
310
449
  end
311
450
 
451
+ def serializer(container, entries)
452
+ entries.each do |entry|
453
+ if entry.is_a?(Sequences::Sequence)
454
+ data = []
455
+ serializer(data, entry)
456
+ container << {type: :sequence, values: data}
457
+ elsif entry.is_a?(Pair::Pair)
458
+ if entry.second.is_a?(Sequences::Sequence)
459
+ data = []
460
+ serializer(data, entry)
461
+ container << {type: :pair, values: data}
462
+ else
463
+ container << {type: :pair, values: entry.to_map}
464
+ end
465
+ elsif entry.is_a?(Option::Some)
466
+ container << {type: :some, values: entry.value}
467
+ elsif entry.is_a?(Option::None)
468
+ container << {type: :none, values: nil}
469
+ elsif entry.respond_to?(:each)
470
+ data = []
471
+ serializer(data, entry)
472
+ container << {type: entry.class, values: data}
473
+ else
474
+ container << entry
475
+ end
476
+ end
477
+ end
478
+
479
+ def deserializer(container, data)
480
+ data.each do |entry|
481
+ if entry.is_a?(Hash)
482
+ if entry[:type] == :sequence
483
+ data = []
484
+ deserializer(data, entry[:values])
485
+ container << Sequence.new(data)
486
+ elsif entry[:type] == :pair
487
+ if entry[:values].is_a?(Array)
488
+ container << pair(entry[:values].first, Sequence.new(entry[:values][1][:values]))
489
+ else
490
+ container << pair(entry[:values].keys.first, entry[:values].values.first)
491
+ end
492
+ elsif entry[:type] == :some
493
+ container << some(entry[:values])
494
+ elsif entry[:type] == :none
495
+ container << none
496
+ elsif entry[:type] == Hash
497
+ h = entry[:values].map { |e| [e[:values]].to_h }.reduce({}) { |a, b| a.merge(b) }
498
+ container << h
499
+ else
500
+ container << entry[:type].send(:new, entry[:values])
501
+ end
502
+ else
503
+ container << entry
504
+ end
505
+ end
506
+ end
507
+
312
508
  end
313
509
 
314
510
  class Empty < Sequence
data/lib/totally_lazy.rb CHANGED
@@ -1,15 +1,138 @@
1
1
  require_relative 'type_check'
2
+ require 'set'
3
+ require 'prime'
2
4
  require_relative 'sequence'
3
5
  require_relative 'pair'
4
6
  require_relative 'option'
5
7
  require_relative 'functor'
6
- require_relative 'predicates'
8
+ require_relative 'predicates/predicates'
9
+ require_relative 'predicates/compare'
10
+ require_relative 'predicates/conversions'
11
+ require_relative 'predicates/numbers'
12
+ require_relative 'predicates/where'
13
+ require_relative 'predicates/where_processor'
14
+ require_relative 'parallel/parallel'
15
+ require_relative 'generators'
16
+ require_relative 'any'
7
17
 
8
18
  include Sequences
9
19
  include Option
10
20
  include Pair
21
+ include Predicates
11
22
  include Predicates::Numbers
12
23
  include Predicates::Conversions
24
+ include Predicates::Compare
25
+ include Predicates::Where
26
+ include Generators
27
+ include Any
28
+
29
+
30
+ # p sequence(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).filter(
31
+ # where( is(greater_than 2).or(is equal_to 5) )
32
+ # ).entries
33
+ #
34
+ # sequence.filter(where.is(greater_than 2))
35
+
36
+
37
+
38
+ # p sequence(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).filter(where(is greater_than 2).or(is less_than 8 ).and(is odd)).entries
39
+
40
+ # p sequence(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).filter(where(is greater_than 2).and(is less_than 8 ).and(is even)).entries
41
+
42
+ # p sequence(pair(1, 2), pair(3, 4)).filter(where(key:odd)).entries
43
+
44
+ # p sequence(1,2,3,4,5,6).map_concurrently(as_string,in_threads:10).entries
45
+ # p sequence(1,2,3,4,5,6).map_concurrently{|i| i+5 }.entries
46
+
47
+ # sequence(1,2,3,4,5).each_concurrently{|i| sleep rand(10); p i }
48
+
49
+
50
+
51
+ # p Seq.repeat('car').take(10).entries
52
+ # p Seq.range(1,1000000000000000000000000000).take(20).entries
53
+
54
+ # p Seq.iterate(:*,2).take(10).entries
55
+ # p Seq.fibonacci.take(20).entries
56
+ # p Iter.fibonacci.take(20).entries
57
+ # p Iter.primes.take(10).entries
58
+
59
+ # p Seq.powers_of(3).take(10).entries
60
+ # p Iter.powers_of(3).take(10).entries
61
+
62
+ # p Seq.iterate(:+,1,0).take(10).entries
63
+
64
+ # p Seq.range(1,4).cycle.take(20).entries
65
+ # p Iter.range(1,4).cycle.take(20).entries
66
+
67
+ # p Seq.iterate(:+, 1).filter(even).take(2).reduce(:+)
68
+
69
+ #
70
+ # class Person
71
+ # attr_accessor :first_name,:last_name,:age
72
+ # def initialize(first_name,last_name,age)
73
+ # @first_name = first_name
74
+ # @last_name = last_name
75
+ # @age = age
76
+ # end
77
+ #
78
+ # def name
79
+ # @first_name
80
+ # end
81
+ #
82
+ # end
83
+ #
84
+ # people = Seq.repeat(-> {Person.new(Any.string,Any.string,Any.string)}).take(10)
85
+ #
86
+ # p people.filter(where(first_name:matches(/s/))).map(&:first_name).entries
87
+
88
+ # p sequence(pair('apples','pairs'),pair('banana','orange'),pair('apples','melon')).filter(where key:matches(/app/)).entries
89
+
90
+ # p Seq.repeat(-> { rand(9)}).take(10).to_a.join.to_i
91
+
92
+ # p Seq.range(1,100000000000000).shuffle.take(10).to_a
93
+
94
+ # a = sequence(pair(7,8),sequence(1,2),sequence(3,sequence(5,6)),sequence(pair(:apple,99), option(1),none),[10,11],{:apple => 8,:pear => 9}).serialize
95
+ #
96
+ # p a
97
+ # p deserialize(a)[5]
98
+
99
+ # p sequence(pair(1,2))[0]
100
+
101
+ # p empty.gan(ser).entries
102
+
103
+ # def countdown(c,n)
104
+ # return if n.zero? # base case
105
+ # c << n
106
+ # countdown(c,n-1) # getting closer to base case
107
+ # end
108
+ #
109
+ #
110
+ # a = []
111
+ # countdown(a,5)
112
+ # p a
113
+
114
+
115
+ # s1 = Seq.range(1,3000000)
116
+ # s2 = Seq.range(1,3000000)
117
+ #
118
+ # s = Time.now
119
+ # p s1.join2(s2).head
120
+ # f = Time.now
121
+ # p f - s
122
+ #
123
+ #
124
+ # s = Time.now
125
+ # p s1.join(s2).head
126
+ # f = Time.now
127
+ # p f - s
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+
13
136
 
14
137
 
15
138
 
@@ -0,0 +1,35 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'Functor' do
4
+
5
+ it 'should create a new functor' do
6
+ fc = Functor.new do |op, *a, &b|
7
+ [op, a, b]
8
+ end
9
+ expect(fc+1).to eq([:+, [1], nil])
10
+ end
11
+
12
+ it 'should support class method' do
13
+ fc = Functor.new do |op, *a, &b|
14
+ [op, a, b]
15
+ end
16
+ expect( fc.__class__).to eq(Functor)
17
+ end
18
+
19
+ it 'should support to proc' do
20
+ f = Functor.new do |op, *a|
21
+ [op, *a]
22
+ end
23
+ p = f.to_proc
24
+ expect(p.class).to eq(Proc)
25
+ expect(p.call(:+,1,2,3)).to eq([:+,1,2,3])
26
+ end
27
+
28
+ it 'should cache a new functor' do
29
+ Functor.cache(:cached) do |op, *a, &b|
30
+ [op, a, b]
31
+ end
32
+ expect(Functor.cache(:cached) + 1).to eq([:+, [1], nil])
33
+ end
34
+
35
+ end
@@ -0,0 +1,37 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'Generators' do
4
+
5
+ it 'should support repeat' do
6
+ expect(Seq.repeat('car').take(2)).to eq(sequence('car', 'car'))
7
+ expect(Iter.repeat('car').take(2).entries).to eq(%w(car car))
8
+ end
9
+
10
+ it 'should support range' do
11
+ expect(Seq.range(1, 4)).to eq(sequence(1, 2, 3, 4))
12
+ expect(Iter.range(1, 4).entries).to eq([1, 2, 3, 4])
13
+ end
14
+
15
+ it 'should support iterate' do
16
+ expect(Seq.iterate(:+, 1).take(4)).to eq(sequence(1, 2, 3, 4))
17
+ expect(Iter.iterate(:+, 1).take(4).entries).to eq([1, 2, 3, 4])
18
+ expect(Seq.iterate(:+, 1, 5).take(4)).to eq(sequence(5, 6, 7, 8))
19
+ expect(Iter.iterate(:+, 1, 5).take(4).entries).to eq([5, 6, 7, 8])
20
+ end
21
+
22
+ it 'should support primes' do
23
+ expect(Seq.primes.take(3)).to eq(sequence(2, 3, 5))
24
+ expect(Iter.primes.take(3).entries).to eq([2, 3, 5])
25
+ end
26
+
27
+ it 'should support fibonacci' do
28
+ expect(Seq.fibonacci.take(3)).to eq(sequence(1, 1, 2))
29
+ expect(Iter.fibonacci.take(3).entries).to eq([1, 1, 2])
30
+ end
31
+
32
+ it 'should support powers_of' do
33
+ expect(Seq.range(1, 4)).to eq(sequence(1, 2, 3, 4))
34
+ expect(Iter.range(1, 4).entries).to eq([1, 2, 3, 4])
35
+ end
36
+
37
+ end
data/spec/option_spec.rb CHANGED
@@ -1,5 +1,4 @@
1
- require 'rspec'
2
- require_relative '../lib/totally_lazy'
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
2
 
4
3
  describe 'Option' do
5
4
 
@@ -45,5 +44,77 @@ describe 'Option' do
45
44
  expect { option(empty).get_or_throw(Exception) }.to raise_error(Exception)
46
45
  end
47
46
 
47
+ it 'should return boolean for empty?' do
48
+ expect(option(1).empty?).to eq(false)
49
+ expect(option(empty).empty?).to eq(true)
50
+ end
51
+
52
+ it 'should return boolean for defined?' do
53
+ expect(option(1).defined?).to eq(true)
54
+ expect(option('').defined?).to eq(false)
55
+ end
56
+
57
+ it 'should convert to sequence' do
58
+ expect(option(1).to_seq).to eq(sequence(option(1)))
59
+ end
60
+
61
+ it 'should raise empty exception when calling get on none' do
62
+ expect { option(empty).get }.to raise_error(NoSuchElementException)
63
+ end
64
+
65
+ it 'should raise empty exception when calling value on none' do
66
+ expect { option(empty).value }.to raise_error(NoSuchElementException)
67
+ end
68
+
69
+ it 'should return true for empty on none' do
70
+ expect(option(empty).empty?).to eq(true)
71
+ end
72
+
73
+ it 'should return false for defined on none' do
74
+ expect(option(empty).defined?).to eq(false)
75
+ end
76
+
77
+ it 'should return the else in get_or_else for none' do
78
+ expect(option(empty).get_or_else(1)).to eq(1)
79
+ end
80
+
81
+ it 'should return nil in get_or_nil for none' do
82
+ expect(option(empty).get_or_nil).to eq(nil)
83
+ end
84
+
85
+ it 'should raise exception in get_or_throw for none' do
86
+ expect { option(empty).get_or_throw(Exception, 'oops') }.to raise_error(Exception)
87
+ end
88
+
89
+ it 'should return sequence for none' do
90
+ expect(option(empty).to_seq).to eq(sequence(none))
91
+ end
92
+
93
+ it 'should return false for contains for none' do
94
+ expect(option(empty).contains('ooo')).to eq(false)
95
+ end
96
+
97
+ it 'should return false for exists for none' do
98
+ pending('needs work')
99
+ pass
100
+ end
101
+
102
+ it 'should return false for contains for none' do
103
+ expect(option(empty).contains('ooo')).to eq(false)
104
+ end
105
+
106
+ it 'should return original sequence for join on none' do
107
+ expect(option(empty).join(sequence(1,2))).to eq(sequence(1,2))
108
+ expect(option(empty).join([1,2])).to eq(sequence([1,2]))
109
+ end
110
+
111
+ it 'should know the type' do
112
+ expect(option(empty).is_none?).to be(true)
113
+ expect(option(empty).is_some?).to be(false)
114
+ expect(option(1).is_none?).to be(false)
115
+ expect(option(1).is_some?).to be(true)
116
+ end
117
+
118
+
48
119
 
49
120
  end
data/spec/pair_spec.rb CHANGED
@@ -1,5 +1,4 @@
1
- require 'rspec'
2
- require_relative '../lib/totally_lazy'
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
2
 
4
3
  describe 'Pair' do
5
4
 
@@ -37,4 +36,8 @@ describe 'Pair' do
37
36
  expect(pair('1', '2').to_f).to eq({1.0 => 2.0})
38
37
  end
39
38
 
39
+ it 'should support each' do
40
+ expect(pair(1,2).each{|x| x }).to eq([1,2])
41
+ end
42
+
40
43
  end