lazily 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YTE1ODg4ZDgzNDFjMTc0YzlhMWE0ODU5MWRmNjcyYzQyMTNkMGNmYw==
4
+ OTA2Zjk3MzAxNDg4N2JhMGFhZDAyNTJjZmQ3YzExYzFkNDA4NmZlMw==
5
5
  data.tar.gz: !binary |-
6
- MDlhZjZhMWI5ZDNiMDdmMzk3OWU4NGM4MGVmYzhlODUyMzEyYzg3Ng==
6
+ YmY5MzM3N2ZiYTZkZWVjNzMzNTE2NjhjMmQ1NDZhN2UyYjgxODcxOQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- NWNkYWNkOTViODA2OWM0ZWM1NWJkNTlhY2Y2NWUwNmQ0NGQ1MjZiZjFkODgz
10
- MGFiYjRlYmZjYTQzZDkyYjMyMzFhYjU1ZTE3Y2JmMTMwZWEwMzBhYWFhZDIw
11
- Nzg3Y2I0MjUyYzQzOTJkOTg2MmI4NjdlZmQ0MTkwZTkzMzA2YTY=
9
+ OThlZDA3NGU5ZTAzMmUxZjY0ZmJkMGU3OGE0MDZhZGE0ZTE5NDI5ZDBlZmVi
10
+ ODFmYjEyYmVmNGUwYmMwMzllYjU0NGUwMTgwN2I5NTc5NjExM2U1MTI0MmY3
11
+ ZjYyOTJiZTMwN2I1ZGMwNmUzYWM5ZWZkMmZjZjJhYTkxOWRlYzI=
12
12
  data.tar.gz: !binary |-
13
- OGQ5ZTBhZTgyNTUwOTc3NTEwOGE3MTgwMmM0OWFiZjBiZDY1ZGJjYWRlMDJl
14
- YTM2NmY5ZmYzMGRhNDI3Yjk4NTgwYTU5MTdmYjllM2U4Y2M0ZDVlODM1ZTI1
15
- MThmOTkyZDNhZDNjNDVkYWVkZjBmMDJmMGEwMGZjMGRiZTAxYzI=
13
+ NDA3OTM0ZWMzOGNkMDY3M2JjNmYwYzg2Zjk4NmY0MjNlZDY2ZWZkY2I0NzMx
14
+ Y2E5MzJmODU5MTgyYTk4MjUxZWIxYWQwNzM5NjExNWRhNDFhYTgzYjM3NGE3
15
+ Yzk5NjVmMjBjMjBkMDcyZmM2YTAwNzI1YzcwZTA1NTBiZmIyZDM=
data/.gitignore CHANGED
@@ -1,4 +1,6 @@
1
1
  *.gem
2
2
  .bundle
3
+ .yardoc
3
4
  Gemfile.lock
5
+ doc
4
6
  pkg/*
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.0.1 (2013-04-19)
4
+
5
+ * Initial version, ported from "enumerating".
data/Gemfile CHANGED
@@ -4,3 +4,4 @@ gemspec
4
4
 
5
5
  gem "rake", "~> 10.0.0"
6
6
  gem "rspec", "~> 2.13.0"
7
+ gem "yard"
@@ -0,0 +1,147 @@
1
+ Lazily [![Build Status](https://secure.travis-ci.org/mdub/lazily.png?branch=master)](http://travis-ci.org/mdub/lazily)
2
+ ===========
3
+
4
+ Lazily provides various "lazy" Enumerable operations, similar to:
5
+
6
+ * Clojure's sequences
7
+ * Haskell's lists
8
+ * Scala's "views"
9
+ * Ruby 2.0's Enumerator::Lazy
10
+
11
+ Lazy filtering and transforming
12
+ -------------------------------
13
+
14
+ Lazy evaluation is triggered using `Enumerable#lazily`:
15
+
16
+ [1,2,3].lazily #=> #<Lazily::Proxy: [1, 2, 3]>
17
+
18
+ The resulting object implements lazy versions of various Enumerable methods. Rather than returning Arrays, the lazy forms return new Enumerable objects, which generate results incrementally, on demand.
19
+
20
+ Consider the following code, which squares a bunch of numbers, then takes the first 4 results.
21
+
22
+ >> (1..10).collect { |x| p x; x*x }.take(4)
23
+ 1
24
+ 2
25
+ 3
26
+ 4
27
+ 5
28
+ 6
29
+ 7
30
+ 8
31
+ 9
32
+ 10
33
+ => [1, 4, 9, 16]
34
+
35
+ See how it printed all the numbers from 1 to 10, indicating that the block given to `collect` was run ten times. We can do the same thing lazily, like this:
36
+
37
+ >> (1..10).lazily.collect { |x| p x; x*x }.take(4).to_a
38
+ 1
39
+ 2
40
+ 3
41
+ 4
42
+ => [1, 4, 9, 16]
43
+
44
+ Same result, but notice how the block was only evaluated four times.
45
+
46
+ Lazy pipelines
47
+ --------------
48
+
49
+ By combining two or more lazy operations, you can create an efficient "pipeline", e.g.
50
+
51
+ User.to_enum(:find_each).lazily.select do |u|
52
+ u.first_name[0] == u.last_name[0]
53
+ end.collect(&:company).uniq.to_a
54
+
55
+ In case you missed it:
56
+
57
+ # enumerate all users
58
+ users = User.to_enum(:find_each)
59
+
60
+ # lazily select those matching "X... X..." (e.g. "Mickey Mouse")
61
+ users = users.lazily.select { |u| u.first_name[0] == u.last_name[0] }
62
+
63
+ # grab their company (weeding out duplicates)
64
+ companies = users.collect(&:company).uniq
65
+
66
+ # force resolution
67
+ companies.to_a #=> ["Disney"]
68
+
69
+ Because the steps in the pipeline operate in parallel, without creation of intermediate collections (Arrays), you can efficiently operate on large (or even infinite) Enumerable collections.
70
+
71
+ This is analogous to a Unix shell pipeline, though of course here we're talking about streams of objects, rather than streams of text.
72
+
73
+ Lazy multi-threaded processing
74
+ ------------------------------
75
+
76
+ The `#in_threads` method is a multi-threaded version of `#collect`, allowing multiple elements of a collection to be processed in parallel. It requires a numeric argument specifying the maximum number of Threads to use.
77
+
78
+ Benchmark.realtime do
79
+ [1,2,3,4].lazily.in_threads(10) do |x|
80
+ sleep 1
81
+ x * 2
82
+ end.to_a #=> [2,4,6,8]
83
+ end.to_i #=> 1
84
+
85
+ Outputs will be yielded in the expected order, making it a drop-in replacement for `#collect`.
86
+
87
+ Unlike some other "parallel map" implementations, the output of `#in_threads` is lazy (though it does need to pre-fetch elements from the source collection as required to start Threads).
88
+
89
+ Lazy combination of Enumerables
90
+ -------------------------------
91
+
92
+ Lazily also provides some interesting ways to combine several Enumerable collections to create a new collection.
93
+
94
+ `Lazily.zip` pulls elements from a number of collections in parallel, yielding each group.
95
+
96
+ array1 = [1,3,6]
97
+ array2 = [2,4,7]
98
+ Lazily.zip(array1, array2) #=> [1,2], [3,4], [6,7]
99
+
100
+ `Lazily.concat` concatenates collections.
101
+
102
+ array1 = [1,3,6]
103
+ array2 = [2,4,7]
104
+ Lazily.concat(array1, array2) #=> [1,3,6,2,4,7]
105
+
106
+ `Lazily.merge` merges multiple collections, preserving sort-order. The inputs are assumed to be sorted already.
107
+
108
+ array1 = [1,4,5]
109
+ array2 = [2,3,6]
110
+ Lazily.merge(array1, array2) #=> [1,2,3,4,5,6]
111
+
112
+ A block can be provided to determine the sort-order.
113
+
114
+ array1 = %w(a dd cccc)
115
+ array2 = %w(eee bbbbb)
116
+ Lazily.merge(array1, array2) { |x| x.length }
117
+ #=> %w(a dd eee cccc bbbbb)
118
+
119
+ Same but different
120
+ ------------------
121
+
122
+ There are numerous similar implementations of lazy operations on Enumerables.
123
+
124
+ ### Lazily vs. Enumerating
125
+
126
+ Lazily supercedes "[Enumerating](http://github.com/mdub/enumerating)". Whereas Enumerating mixed lazy operations directly onto `Enumerable`, Lazily does not. Instead, it implements an API modelled on Ruby 2.0's `Enumerable#lazy`.
127
+
128
+ ### Lazily vs. Ruby 2.0
129
+
130
+ Q: Why use Lazily, when Ruby 2.x has built-in lazy enumerations?
131
+
132
+ - Compatibility: Perhaps you haven't managed to migrate to Ruby 2.0 yet. Lazily provides the same benefits, but works in older versions of Ruby (most features work even in Ruby-1.8.7).
133
+ - Consistency: Being pure-Ruby, you can use the same implementation of lazy enumeration across Ruby versions and interpreters.
134
+ - Features: Lazily provides some extra features not present in Ruby 2.0, such as multi-threaded lazy enumeration.
135
+ - Speed: Despite being implemented in pure Ruby, `Enumerable#lazily` actually performs a little better than `Enumerable#lazy`.
136
+
137
+ Q: When would you use built-in Ruby lazy enumerations, rather than Lazily?
138
+
139
+ - I wouldn't.
140
+
141
+ ### Others
142
+
143
+ See also:
144
+
145
+ * Greg Spurrier's gem "`lazing`"
146
+ * `Enumerable#defer` from the Ruby Facets library
147
+ * The "`backports`" gem, which implements `Enumerable#lazy` for Ruby pre-2.0.
data/Rakefile CHANGED
@@ -10,3 +10,9 @@ RSpec::Core::RakeTask.new do |t|
10
10
  end
11
11
 
12
12
  task "default" => "spec"
13
+
14
+ desc "Generate documentation"
15
+ task(:doc) { sh "yard" }
16
+
17
+ desc "Generate documentation incrementally"
18
+ task(:redoc) { sh "yard -c" }
@@ -0,0 +1,97 @@
1
+ require 'benchmark'
2
+
3
+ $: << File.expand_path("../../lib", __FILE__)
4
+
5
+ array = (1..100000).to_a
6
+
7
+ # Test scenario:
8
+ # - filter out even numbers
9
+ # - square them
10
+ # - grab the first thousand
11
+
12
+ printf "%-30s", "IMPLEMENTATION"
13
+ printf "%12s", "take(10)"
14
+ printf "%12s", "take(100)"
15
+ printf "%12s", "take(1000)"
16
+ printf "%12s", "to_a"
17
+ puts ""
18
+
19
+ def measure(&block)
20
+ printf "%12.5f", Benchmark.realtime(&block)
21
+ end
22
+
23
+ def benchmark(description, control_result = nil)
24
+ result = nil
25
+ printf "%-30s", description
26
+ measure { yield.take(10).to_a }
27
+ measure { yield.take(100).to_a }
28
+ measure { result = yield.take(1000).to_a }
29
+ measure { yield.to_a }
30
+ puts ""
31
+ unless control_result.nil? || result == control_result
32
+ raise "unexpected result from '#{description}': #{result.inspect}"
33
+ end
34
+ result
35
+ end
36
+
37
+ @control = benchmark "eager" do
38
+ array.select { |x| x.even? }.collect { |x| x*x }
39
+ end
40
+
41
+ def can_require?(library)
42
+ require(library)
43
+ true
44
+ rescue LoadError
45
+ false
46
+ end
47
+
48
+ if array.respond_to?(:lazy)
49
+
50
+ benchmark "ruby2", @control do
51
+ array.lazy.select { |x| x.even? }.collect { |x| x*x }
52
+ end
53
+
54
+ elsif can_require?("backports/2.0.0")
55
+
56
+ benchmark "backports", @control do
57
+ array.lazy.select { |x| x.even? }.collect { |x| x*x }
58
+ end
59
+
60
+ end
61
+
62
+ if can_require? "facets"
63
+
64
+ benchmark "facets", @control do
65
+ array.defer.select { |x| x.even? }.collect { |x| x*x }
66
+ end
67
+
68
+ end
69
+
70
+ if defined?(Fiber) && can_require?("lazing")
71
+
72
+ module Enumerable
73
+ alias :lazing_select :selecting
74
+ alias :lazing_collect :collecting
75
+ end
76
+
77
+ benchmark "lazing", @control do
78
+ array.lazing_select { |x| x.even? }.lazing_collect { |x| x*x }
79
+ end
80
+
81
+ end
82
+
83
+ if can_require?("enumerating")
84
+
85
+ benchmark "enumerating", @control do
86
+ array.selecting { |x| x.even? }.collecting { |x| x*x }
87
+ end
88
+
89
+ end
90
+
91
+ if can_require?("lazily")
92
+
93
+ benchmark "lazily", @control do
94
+ array.lazily.select { |x| x.even? }.collect { |x| x*x }
95
+ end
96
+
97
+ end
@@ -4,6 +4,11 @@ module Lazily
4
4
 
5
5
  class << self
6
6
 
7
+ # Concatenates two or more Enumerables.
8
+ #
9
+ # @param enumerables [Array<Enumerable>]
10
+ # @return [Enumerable] elements of all the enumerables
11
+ #
7
12
  def concat(*enumerables)
8
13
  Concatenator.new(enumerables)
9
14
  end
@@ -12,6 +17,13 @@ module Lazily
12
17
 
13
18
  module Enumerable
14
19
 
20
+ # Concatenates this and other Enumerables.
21
+ #
22
+ # @param others [Array<Enumerable>]
23
+ # @return [Enumerable] elements of this and other Enumerables
24
+ #
25
+ # @see Array#concat
26
+ #
15
27
  def concat(*others)
16
28
  Lazily.concat(self, *others)
17
29
  end
@@ -8,6 +8,10 @@ module Lazily
8
8
  true
9
9
  end
10
10
 
11
+ def lazily
12
+ self
13
+ end
14
+
11
15
  end
12
16
 
13
17
  end
@@ -4,8 +4,13 @@ module Lazily
4
4
 
5
5
  module Enumerable
6
6
 
7
- def collect
8
- Filter.new do |output|
7
+ # Transform elements using the block provided.
8
+ # @return [Enumerable] the transformed elements
9
+ #
10
+ # @see ::Enumerable#collect
11
+ #
12
+ def collect(&transformation)
13
+ filter("collect") do |output|
9
14
  each do |element|
10
15
  output.call yield(element)
11
16
  end
@@ -14,8 +19,14 @@ module Lazily
14
19
 
15
20
  alias map collect
16
21
 
17
- def select
18
- Filter.new do |output|
22
+ # Select elements using a predicate block.
23
+ #
24
+ # @return [Enumerable] the elements that pass the predicate
25
+ #
26
+ # @see ::Enumerable#select
27
+ #
28
+ def select(&predicate)
29
+ filter("select") do |output|
19
30
  each do |element|
20
31
  output.call(element) if yield(element)
21
32
  end
@@ -24,54 +35,87 @@ module Lazily
24
35
 
25
36
  alias find_all select
26
37
 
38
+ # Select elements that fail a test.
39
+ #
40
+ # @yield [element] each element
41
+ # @yieldreturn [Boolean] truthy if the element passed the test
42
+ # @return [Enumerable] the elements which failed the test
43
+ #
44
+ # @see ::Enumerable#reject
45
+ #
27
46
  def reject
28
- Filter.new do |output|
47
+ filter("reject") do |output|
29
48
  each do |element|
30
49
  output.call(element) unless yield(element)
31
50
  end
32
51
  end
33
52
  end
34
53
 
54
+ # Remove duplicate values.
55
+ #
56
+ # @return [Enumerable] elements which have not been previously encountered
57
+ # @overload uniq
58
+ #
59
+ # @overload uniq(&block)
60
+ #
61
+ # @see ::Enumerable#uniq
62
+ #
35
63
  def uniq
36
- Filter.new do |output|
64
+ filter("uniq") do |output|
37
65
  seen = Set.new
38
66
  each do |element|
39
- output.call(element) if seen.add?(element)
40
- end
41
- end
42
- end
43
-
44
- def uniq_by
45
- Filter.new do |output|
46
- seen = Set.new
47
- each do |element|
48
- output.call(element) if seen.add?(yield element)
67
+ key = if block_given?
68
+ yield element
69
+ else
70
+ element
71
+ end
72
+ output.call(element) if seen.add?(key)
49
73
  end
50
74
  end
51
75
  end
52
76
 
77
+ # Select the first n elements.
78
+ #
79
+ # @param n [Integer] the number of elements to take
80
+ # @return [Enumerable] the first N elements
81
+ #
82
+ # @see ::Enumerable#take
83
+ #
53
84
  def take(n)
54
- Filter.new do |output|
85
+ filter("take") do |output|
55
86
  if n > 0
56
87
  each_with_index do |element, index|
57
88
  output.call(element)
58
- throw Lazily::Filter::DONE if index + 1 == n
89
+ throw Filter::DONE if index + 1 == n
59
90
  end
60
91
  end
61
92
  end
62
93
  end
63
94
 
64
- def take_while
65
- Filter.new do |output|
95
+ # Select elements while a predicate returns true.
96
+ #
97
+ # @return [Enumerable] all elements before the first that fails the predicate
98
+ #
99
+ # @see ::Enumerable#take_while
100
+ #
101
+ def take_while(&predicate)
102
+ filter("take_while") do |output|
66
103
  each do |element|
67
- throw Lazily::Filter::DONE unless yield(element)
104
+ throw Filter::DONE unless yield(element)
68
105
  output.call(element)
69
106
  end
70
107
  end
71
108
  end
72
109
 
110
+ # Ignore the first n elements.
111
+ #
112
+ # @param n [Integer] the number of elements to drop
113
+ # @return [Enumerable] elements after the first N
114
+ #
115
+ # @see ::Enumerable#drop
116
+ #
73
117
  def drop(n)
74
- Filter.new do |output|
118
+ filter("drop") do |output|
75
119
  each_with_index do |element, index|
76
120
  next if index < n
77
121
  output.call(element)
@@ -79,8 +123,14 @@ module Lazily
79
123
  end
80
124
  end
81
125
 
82
- def drop_while
83
- Filter.new do |output|
126
+ # Reject elements while a predicate returns true.
127
+ #
128
+ # @return [Enumerable] all elements including and after the first that fails the predicate
129
+ #
130
+ # @see ::Enumerable#drop_while
131
+ #
132
+ def drop_while(&predicate)
133
+ filter("drop_while") do |output|
84
134
  take = false
85
135
  each do |element|
86
136
  take ||= !yield(element)
@@ -89,17 +139,102 @@ module Lazily
89
139
  end
90
140
  end
91
141
 
142
+ # Select elements matching a pattern.
143
+ #
144
+ # @return [Enumerable] elements for which the pattern matches
145
+ #
146
+ # @see ::Enumerable#grep
147
+ #
148
+ def grep(pattern)
149
+ filter("grep") do |output|
150
+ each do |element|
151
+ if pattern === element
152
+ result = if block_given?
153
+ yield element
154
+ else
155
+ element
156
+ end
157
+ output.call(result)
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ # Flatten the collection, such that Enumerable elements are included inline.
164
+ #
165
+ # @return [Enumerable] elements of elements of the collection
166
+ #
167
+ # @see ::Array#flatten
168
+ #
169
+ def flatten(level = 1)
170
+ filter("flatten") do |output|
171
+ each do |element|
172
+ if level > 0 && element.respond_to?(:each)
173
+ element.flatten(level - 1).each(&output)
174
+ else
175
+ output.call(element)
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ def flat_map(&block)
182
+ map(&block).flatten
183
+ end
184
+
185
+ alias collect_concat flat_map
186
+
187
+ # Ignore nil values.
188
+ #
189
+ # @return [Enumerable] the elements that are not nil
190
+ #
191
+ # @see ::Array#compact
192
+ #
193
+ def compact
194
+ filter("compact") do |output|
195
+ each do |element|
196
+ output.call(element) unless element.nil?
197
+ end
198
+ end
199
+ end
200
+
201
+ if ::Enumerable.method_defined?(:slice_before)
202
+
203
+ def slice_before(*args, &block)
204
+ super.lazily
205
+ end
206
+
207
+ end
208
+
209
+ if ::Enumerable.method_defined?(:chunk)
210
+
211
+ def chunk(*args, &block)
212
+ super.lazily
213
+ end
214
+
215
+ end
216
+
217
+ # @return the nth element
218
+ #
92
219
  def [](n)
93
220
  drop(n).first
94
221
  end
95
222
 
223
+ private
224
+
225
+ def filter(method, &block)
226
+ Filter.new(self, method, &block)
227
+ end
228
+
96
229
  end
97
230
 
98
231
  class Filter
99
232
 
100
233
  include Lazily::Enumerable
101
234
 
102
- def initialize(&generator)
235
+ def initialize(source, method, &generator)
236
+ @source = source
237
+ @method = method
103
238
  @generator = generator
104
239
  end
105
240
 
@@ -113,6 +248,10 @@ module Lazily
113
248
  end
114
249
  end
115
250
 
251
+ def inspect
252
+ "#<#{self.class}: #{@method} #{@source.inspect}>"
253
+ end
254
+
116
255
  end
117
256
 
118
257
  end
@@ -4,11 +4,7 @@ module Lazily
4
4
 
5
5
  class << self
6
6
 
7
- def merge(*enumerables)
8
- Merger.new(enumerables)
9
- end
10
-
11
- def merge_by(*enumerables, &block)
7
+ def merge(*enumerables, &block)
12
8
  Merger.new(enumerables, &block)
13
9
  end
14
10
 
@@ -27,6 +27,10 @@ module Lazily
27
27
  @source.each(&block)
28
28
  end
29
29
 
30
+ def inspect
31
+ "#<#{self.class}: #{@source.inspect}>"
32
+ end
33
+
30
34
  end
31
35
 
32
36
  end
@@ -8,7 +8,7 @@ module Lazily
8
8
  collect do |item|
9
9
  Thread.new { block.call(item) }
10
10
  end.prefetch(max_threads - 1).collect do |thread|
11
- thread.join; thread.value
11
+ thread.join.value
12
12
  end
13
13
  end
14
14
  end
@@ -1,3 +1,3 @@
1
1
  module Lazily
2
- VERSION = "0.0.1".freeze
2
+ VERSION = "0.1.0".freeze
3
3
  end
@@ -2,36 +2,32 @@ require "spec_helper"
2
2
 
3
3
  describe Lazily, "concatenating" do
4
4
 
5
- describe ".concat" do
5
+ let(:array1) { [1,5,3] }
6
+ let(:array2) { [2,9,4] }
6
7
 
7
- let(:array1) { [1,5,3] }
8
- let(:array2) { [2,9,4] }
8
+ describe ".concat" do
9
9
 
10
10
  it "concatenates multiple Enumerables" do
11
- result = Lazily.concat([1,5,3], [2,9,4])
12
- result.to_a.should == [1,5,3,2,9,4]
11
+ result = Lazily.concat(array1, array2)
12
+ result.to_a.should == array1 + array2
13
13
  end
14
14
 
15
15
  it "is lazy" do
16
- result = Lazily.concat([3,4], [1,2].with_time_bomb)
17
- result.take(3).to_a.should == [3,4,1]
16
+ Lazily.concat(array1, array2.ecetera).should be_lazy
18
17
  end
19
18
 
20
19
  end
21
20
 
22
21
  describe "#concat" do
23
22
 
24
- let(:array1) { [1,5,3] }
25
- let(:array2) { [2,9,4] }
26
-
27
23
  it "concatenates multiple Enumerables" do
28
- result = [1,5,3].lazily.concat([2,9,4])
29
- result.to_a.should == [1,5,3,2,9,4]
24
+ result = array1.lazily.concat(array2)
25
+ result.to_a.should == array1 + array2
30
26
  end
31
27
 
32
28
  it "is lazy" do
33
- result = [3,4].lazily.concat([1,2].with_time_bomb)
34
- result.take(3).to_a.should == [3,4,1]
29
+ result = array1.lazily.concat(array2.ecetera)
30
+ result.take(3).to_a.should == (array1 + array2).take(3)
35
31
  end
36
32
 
37
33
  end
@@ -9,7 +9,7 @@ describe Lazily, "filter" do
9
9
  end
10
10
 
11
11
  it "is lazy" do
12
- [1,2,3].with_time_bomb.lazily.collect { |x| x * 2 }.first.should == 2
12
+ [1,2,3].ecetera.lazily.collect { |x| x * 2 }.should be_lazy
13
13
  end
14
14
 
15
15
  end
@@ -21,7 +21,7 @@ describe Lazily, "filter" do
21
21
  end
22
22
 
23
23
  it "is lazy" do
24
- (1..6).with_time_bomb.lazily.select { |x| x%2 == 0 }.first == 2
24
+ (1..6).ecetera.lazily.select { |x| x%2 == 0 }.should be_lazy
25
25
  end
26
26
 
27
27
  end
@@ -33,7 +33,7 @@ describe Lazily, "filter" do
33
33
  end
34
34
 
35
35
  it "is lazy" do
36
- (1..6).with_time_bomb.lazily.reject { |x| x%2 == 0 }.first == 1
36
+ (1..6).ecetera.lazily.reject { |x| x%2 == 0 }.should be_lazy
37
37
  end
38
38
 
39
39
  end
@@ -45,16 +45,16 @@ describe Lazily, "filter" do
45
45
  end
46
46
 
47
47
  it "is lazy" do
48
- [1,2,3].with_time_bomb.lazily.uniq.first.should == 1
48
+ [1,2,3].ecetera.lazily.uniq.should be_lazy
49
49
  end
50
50
 
51
- end
51
+ context "with a block" do
52
52
 
53
- describe "#uniq_by" do
53
+ it "uses the block to derive identity" do
54
+ array = %w(A1 A2 B1 A3 C1 B2 C2)
55
+ array.lazily.uniq { |s| s[0,1] }.to_a.should == %w(A1 B1 C1)
56
+ end
54
57
 
55
- it "uses the block to derive identity" do
56
- @array = %w(A1 A2 B1 A3 C1 B2 C2)
57
- @array.lazily.uniq_by { |s| s[0,1] }.to_a.should == %w(A1 B1 C1)
58
58
  end
59
59
 
60
60
  end
@@ -62,16 +62,16 @@ describe Lazily, "filter" do
62
62
  describe "#take" do
63
63
 
64
64
  it "includes the specified number" do
65
- @array = [1,2,3,4]
66
- @array.lazily.take(3).to_a.should == [1,2,3]
65
+ array = [1,2,3,4]
66
+ array.lazily.take(3).to_a.should == [1,2,3]
67
67
  end
68
68
 
69
69
  it "is lazy" do
70
- [1,2].with_time_bomb.lazily.take(2).to_a.should == [1,2]
70
+ [1,2].ecetera.lazily.take(2).should be_lazy
71
71
  end
72
72
 
73
73
  it "copes with 0" do
74
- [].with_time_bomb.lazily.take(0).to_a.should == []
74
+ [].ecetera.lazily.take(0).to_a.should == []
75
75
  end
76
76
 
77
77
  end
@@ -79,12 +79,12 @@ describe Lazily, "filter" do
79
79
  describe "#take_while" do
80
80
 
81
81
  it "takees elements as long as the predicate is true" do
82
- @array = [2,4,6,3]
83
- @array.lazily.take_while(&:even?).to_a.should == [2,4,6]
82
+ array = [2,4,6,3]
83
+ array.lazily.take_while(&:even?).to_a.should == [2,4,6]
84
84
  end
85
85
 
86
86
  it "is lazy" do
87
- [2,3].with_time_bomb.lazily.take_while(&:even?).to_a.should == [2]
87
+ [2,3].ecetera.lazily.take_while(&:even?).should be_lazy
88
88
  end
89
89
 
90
90
  end
@@ -92,12 +92,12 @@ describe Lazily, "filter" do
92
92
  describe "#drop" do
93
93
 
94
94
  it "excludes the specified number" do
95
- @array = [1,2,3,4]
96
- @array.lazily.drop(2).to_a.should == [3,4]
95
+ array = [1,2,3,4]
96
+ array.lazily.drop(2).to_a.should == [3,4]
97
97
  end
98
98
 
99
99
  it "is lazy" do
100
- [1,2,3,4].with_time_bomb.lazily.drop(2).lazily.take(1).to_a.should == [3]
100
+ [1,2,3,4].ecetera.lazily.drop(2).lazily.take(1).to_a.should == [3]
101
101
  end
102
102
 
103
103
  end
@@ -105,28 +105,158 @@ describe Lazily, "filter" do
105
105
  describe "#drop_while" do
106
106
 
107
107
  it "drops elements as long as the predicate is true" do
108
- @array = [2,4,6,3,4]
109
- @array.lazily.drop_while(&:even?).to_a.should == [3,4]
108
+ array = [2,4,6,3,4]
109
+ array.lazily.drop_while(&:even?).to_a.should == [3,4]
110
+ end
111
+
112
+ it "is lazy" do
113
+ [2,3].ecetera.lazily.drop_while(&:even?).lazily.should be_lazy
114
+ end
115
+
116
+ end
117
+
118
+ describe "#grep" do
119
+
120
+ let(:fruits) { %w(apple banana pear orange) }
121
+
122
+ it "returns elements matching a pattern" do
123
+ fruits.lazily.grep(/e$/).to_a.should == %w(apple orange)
124
+ end
125
+
126
+ it "applies the associated block" do
127
+ fruits.lazily.grep(/e$/, &:capitalize).to_a.should == %w(Apple Orange)
128
+ end
129
+
130
+ it "is lazy" do
131
+ fruits.ecetera.lazily.grep(/p/).should be_lazy
132
+ end
133
+
134
+ end
135
+
136
+ describe "#flatten" do
137
+
138
+ let(:array1) { [1,2,3] }
139
+ let(:array2) { [4,5,6] }
140
+
141
+ it "flattens" do
142
+ [[1,2], [3,4]].lazily.flatten.to_a.should == [1,2,3,4]
143
+ end
144
+
145
+ it "handles non-Enumerable elements" do
146
+ [1,2].lazily.flatten.to_a.should == [1,2]
147
+ end
148
+
149
+ it "goes one level deep (by default)" do
150
+ [[1,2], [[3]]].lazily.flatten.to_a.should == [1,2,[3]]
151
+ end
152
+
153
+ it "allows the depth to be specified" do
154
+ [[1], [[2]], [[[3]]]].lazily.flatten(2).to_a.should == [1, 2, [3]]
155
+ end
156
+
157
+ it "is lazy" do
158
+ [array1.ecetera, array2].lazily.flatten.should be_lazy
159
+ end
160
+
161
+ end
162
+
163
+ describe "#flat_map" do
164
+
165
+ let(:array) { [1,2,3] }
166
+
167
+ it "flattens resulting Enumerables" do
168
+ array.lazily.flat_map { |n| [n] * n }.to_a.should == [1,2,2,3,3,3]
169
+ end
170
+
171
+ it "handles blocks that don't return Enumerables" do
172
+ array.lazily.flat_map { |n| n * n }.to_a.should == [1,4,9]
173
+ end
174
+
175
+ it "handles nils " do
176
+ array.lazily.flat_map { nil }.to_a.should == [nil, nil, nil]
177
+ end
178
+
179
+ it "is lazy" do
180
+ array.ecetera.lazily.flat_map { |n| [n] * n }.should be_lazy
181
+ end
182
+
183
+ end
184
+
185
+ if ::Enumerable.method_defined?(:slice_before)
186
+
187
+ describe "#slice_before" do
188
+
189
+ let(:words) do
190
+ %w(One two three Two two three)
191
+ end
192
+
193
+ it "can slice with a pattern" do
194
+ words.lazily.slice_before(/^[A-Z]/).to_a.should == [
195
+ %w(One two three),
196
+ %w(Two two three)
197
+ ]
198
+ end
199
+
200
+ it "can slice with a block" do
201
+ words.lazily.slice_before { |w| w =~ /^[A-Z]/ }.to_a.should == [
202
+ %w(One two three),
203
+ %w(Two two three)
204
+ ]
205
+ end
206
+
207
+ it "is lazy" do
208
+ words.ecetera.lazily.slice_before(/^[A-Z]/).should be_lazy
209
+ end
210
+
211
+ end
212
+
213
+ end
214
+
215
+ if ::Enumerable.method_defined?(:chunk)
216
+
217
+ describe "#chunk" do
218
+
219
+ it "groups elements by the result of a block" do
220
+ [3,1,4,1,5,9,2,6].lazily.chunk(&:even?).to_a.should == [
221
+ [false, [3, 1]],
222
+ [true, [4]],
223
+ [false, [1, 5, 9]],
224
+ [true, [2, 6]],
225
+ ]
226
+ end
227
+
228
+ it "is lazy" do
229
+ [3,1,4,1,5,9,2,6].ecetera.lazily.chunk(&:even?).should be_lazy
230
+ end
231
+
232
+ end
233
+
234
+ end
235
+
236
+ describe "#compact" do
237
+
238
+ it "omits nils" do
239
+ [1, "too", nil, true, nil, false].lazily.compact.to_a.should == [1, "too", true, false]
110
240
  end
111
241
 
112
242
  it "is lazy" do
113
- [2,3].with_time_bomb.lazily.drop_while(&:even?).lazily.take(1).to_a.should == [3]
243
+ [nil].ecetera.lazily.compact.should be_lazy
114
244
  end
115
245
 
116
246
  end
117
247
 
118
248
  describe "#[]" do
119
249
 
120
- before do
121
- @evens = [1,2,3,4,5].lazily.collect { |x| x * 2 }
250
+ let(:evens) do
251
+ (1..999).lazily.collect { |x| x * 2 }
122
252
  end
123
253
 
124
254
  it "finds the specified element" do
125
- @evens.lazily[2].should == 6
255
+ evens.lazily[2].should == 6
126
256
  end
127
257
 
128
258
  it "is lazy" do
129
- @evens.with_time_bomb.lazily[3].should == 8
259
+ evens.ecetera.lazily[3].should == 8
130
260
  end
131
261
 
132
262
  end
@@ -5,29 +5,29 @@ describe Lazily, :needs_enumerators => true do
5
5
  describe ".merge" do
6
6
 
7
7
  it "merges multiple Enumerators" do
8
- @array1 = [1,3,6]
9
- @array2 = [2,4,7]
10
- @array3 = [5,8]
11
- @merge = Lazily.merge(@array1, @array2, @array3)
12
- @merge.to_a.should == [1,2,3,4,5,6,7,8]
8
+ array1 = [1,3,6]
9
+ array2 = [2,4,7]
10
+ array3 = [5,8]
11
+ merged = Lazily.merge(array1, array2, array3)
12
+ merged.to_a.should == [1,2,3,4,5,6,7,8]
13
13
  end
14
14
 
15
15
  it "is lazy" do
16
- @enum1 = [1,3,6]
17
- @enum2 = [2,4,7].with_time_bomb
18
- @merge = Lazily.merge(@enum1, @enum2)
19
- @merge.take(4).to_a.should == [1,2,3,4]
16
+ enum1 = [1,3,6]
17
+ enum2 = [2,4,7].ecetera
18
+ merged = Lazily.merge(enum1, enum2)
19
+ merged.should be_lazy
20
20
  end
21
21
 
22
- end
22
+ context "with a block" do
23
23
 
24
- describe ".merge_by" do
24
+ it "uses the block to determine order" do
25
+ array1 = %w(cccc dd a)
26
+ array2 = %w(eeeee bbb)
27
+ merged = Lazily.merge(array1, array2) { |s| -s.length }
28
+ merged.to_a.should == %w(eeeee cccc bbb dd a)
29
+ end
25
30
 
26
- it "uses the block to determine order" do
27
- @array1 = %w(cccc dd a)
28
- @array2 = %w(eeeee bbb)
29
- @merge = Lazily.merge_by(@array1, @array2) { |s| -s.length }
30
- @merge.to_a.should == %w(eeeee cccc bbb dd a)
31
31
  end
32
32
 
33
33
  end
@@ -37,7 +37,7 @@ describe Lazily do
37
37
  end
38
38
 
39
39
  it "is lazy" do
40
- source.with_time_bomb.lazily.prefetch(2).first.should eq(source.first)
40
+ source.ecetera.lazily.prefetch(2).should be_lazy
41
41
  end
42
42
 
43
43
  it "pre-computes the specified number of elements" do
@@ -13,7 +13,7 @@ describe Lazily, "threading" do
13
13
  end
14
14
 
15
15
  it "is lazy" do
16
- [1,2,3].with_time_bomb.lazily.in_threads(2) { |x| x * 2 }.first.should == 2
16
+ [1,2,3].ecetera.lazily.in_threads(2) { |x| x * 2 }.should be_lazy
17
17
  end
18
18
 
19
19
  def round(n, accuracy = 0.02)
@@ -10,12 +10,11 @@ describe Lazily, "zipping", :needs_enumerators => true do
10
10
 
11
11
  it "zips together multiple Enumerables" do
12
12
  zip = Lazily.zip(array1, array2, array3)
13
- zip.to_a.should == [[1,2,5], [3,4,8], [6,7,nil]]
13
+ zip.to_a.should == array1.zip(array2, array3)
14
14
  end
15
15
 
16
16
  it "is lazy" do
17
- zip = Lazily.zip(%w(a b c), [1,2].with_time_bomb)
18
- zip.take(2).to_a.should == [["a", 1], ["b", 2]]
17
+ Lazily.zip(array1, array2.ecetera).should be_lazy
19
18
  end
20
19
 
21
20
  end
@@ -24,12 +23,11 @@ describe Lazily, "zipping", :needs_enumerators => true do
24
23
 
25
24
  it "zips an Enumerable with multiple others" do
26
25
  zip = array1.lazily.zip(array2, array3)
27
- zip.to_a.should == [[1,2,5], [3,4,8], [6,7,nil]]
26
+ zip.to_a.should == array1.zip(array2, array3)
28
27
  end
29
28
 
30
29
  it "is lazy" do
31
- zip = %w(a b c).lazily.zip([1,2].with_time_bomb)
32
- zip.take(2).to_a.should == [["a", 1], ["b", 2]]
30
+ array1.lazily.zip(array2.ecetera).should be_lazy
33
31
  end
34
32
 
35
33
  end
@@ -2,7 +2,7 @@ require "lazily"
2
2
 
3
3
  class NotLazyEnough < StandardError; end
4
4
 
5
- class WithTimeBomb
5
+ class Ecetera
6
6
 
7
7
  include Enumerable
8
8
 
@@ -20,8 +20,8 @@ end
20
20
  module Enumerable
21
21
 
22
22
  # extend an Enumerable to throw an exception after last element
23
- def with_time_bomb
24
- WithTimeBomb.new(self)
23
+ def ecetera
24
+ Ecetera.new(self)
25
25
  end
26
26
 
27
27
  unless method_defined?(:first)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lazily
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-04-19 00:00:00.000000000 Z
11
+ date: 2013-05-13 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: ! " Lazily implements \"lazy\" versions of many Enumerable methods,\n
14
14
  \ allowing streamed processing of large (or even infinite) collections.\n\n It's
@@ -22,8 +22,11 @@ files:
22
22
  - .gitignore
23
23
  - .rspec
24
24
  - .travis.yml
25
+ - CHANGES.md
25
26
  - Gemfile
27
+ - README.md
26
28
  - Rakefile
29
+ - benchmarks/pipeline_bench.rb
27
30
  - lazily.gemspec
28
31
  - lib/lazily.rb
29
32
  - lib/lazily/combining.rb
@@ -76,3 +79,4 @@ test_files:
76
79
  - spec/lazily/zipping_spec.rb
77
80
  - spec/lazily_spec.rb
78
81
  - spec/spec_helper.rb
82
+ has_rdoc: