pipe-ruby 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1a145f9639825143a1da34eba1c5445d795cef91
4
- data.tar.gz: f39bf8a5a57a07d07a9fa629c610635c0da72f7b
3
+ metadata.gz: 3360d89c8d7925a9c69a3fe6e0549722890e1235
4
+ data.tar.gz: b95e816b5f0a74e3712f3553f8c91f00bb93a0ec
5
5
  SHA512:
6
- metadata.gz: c9843754ada38925ac8503bac0ea3ac246a7b5a77c16a9a7bd20131a636dc6d979a3cf349e90fd9f7bee9d1e3b8606fd2739a3218dd71643f58d9bd9570a9ba0
7
- data.tar.gz: c32f267b6717d7d90f5a8737a7046a029599e2d5e95e673fa3e22dac4b62c81be4089271ad602f678259a9a4e86fc42d229806720c2a5e63259692c8218aeeae
6
+ metadata.gz: 87cf0cbe09d6660cb55ff502f19d7acf6cac4eb09aa7c89c94e72432fdb8f8cf29df32a9778ff0989f61c837b514c77da8f4868404a2ec039b6af271fcf1e87a
7
+ data.tar.gz: 221cd32905cfc0081a6b99f27071811140ef5e18800dd6fa46a0f745f46da451d8f719fad15a3087c7994b5a51990878deba4ecea4a87169b8288102d3785809
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/README.md CHANGED
@@ -151,26 +151,35 @@ stopped.
151
151
 
152
152
  ## Skipping / Stopping Execution
153
153
 
154
- Prior to the execution of each method in the `through` array,
155
- `Pipe::Config#break?` is called with the current value of `subject`. If it
156
- returns truthy, execution will be stopped and the current value of subject
157
- will be returned. A falsey response will allow the execution to move forward.
154
+ At the beginning of each iteration, `Pipe::Config#stop_on` is called. If it
155
+ returns truthy, execution will be stopped and the current value of subject will
156
+ be returned. A falsey response will allow the execution to move forward.
158
157
 
159
- If the break test allows us to move forward, `Pipe::Config#skip?` will be called
160
- with the current value of `subject`. Truthy responses from this method will
161
- cause the existing value of subject to be carried to the next method without
162
- executing the current specified method. Falsey responses will allow normal
163
- execution of the currently specified method.
158
+ If not stopped, `Pipe::Config#skip_on` will be called. Truthy responses will
159
+ cause the current value of subject to be passed to the next iteration without
160
+ calling the method specified in the current iteration. Falsey responses will
161
+ allow the specified method to be called.
164
162
 
165
- ## Testing
166
-
167
- I'll be adding tests in in the very near future. In the meantime, use at your
168
- own risk :)
163
+ Both skip_on and stop_on will receive three arguments when they're called, the
164
+ current value of subject, the method to be called on this iteration and the
165
+ value of `#through`.
169
166
 
170
167
  ## Contributing
171
168
 
169
+ First: please check out our [style guides](https://github.com/teamsnap/guides/tree/master/ruby)...
170
+ we will hold you to them :)
171
+
172
172
  1. Fork it ( https://github.com/[my-github-username]/pipe-ruby/fork )
173
173
  2. Create your feature branch (`git checkout -b my-new-feature`)
174
- 3. Commit your changes (`git commit -am 'Add some feature'`)
175
- 4. Push to the branch (`git push origin my-new-feature`)
176
- 5. Create a new Pull Request
174
+ 3. Make sure you're green (`bundle exec rspec`)
175
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
176
+ 5. Push to the branch (`git push origin my-new-feature`)
177
+ 6. Create a new Pull Request
178
+
179
+ ## Testing
180
+
181
+ `bundle exec rspec`
182
+
183
+ We like to have good coverage of each major feature. Before contributing with a
184
+ PR, please make sure you've added tests and are fully green.
185
+
data/lib/pipe/config.rb CHANGED
@@ -19,28 +19,30 @@ module Pipe
19
19
  error_handlers << block if block_given?
20
20
  end
21
21
 
22
- def break?(subj)
23
- stop_on.call(subj)
24
- rescue ArgumentError
25
- stop_on.call
22
+ def break?(*args)
23
+ stop_on.call(*args) ? true : false
26
24
  end
27
25
 
28
26
  def raise_on_error?
29
27
  raise_on_error ? true : false
30
28
  end
31
29
 
32
- def skip?(subj)
33
- skip_on.call(subj)
34
- rescue ArgumentError
35
- skip_on.call
30
+ def skip?(*args)
31
+ skip_on.call(*args) ? true : false
36
32
  end
37
33
 
38
34
  def skip_on=(val)
39
- @skip_on = (val.respond_to?(:call) ? val : lambda { |obj| val })
35
+ @skip_on = as_proc(val)
40
36
  end
41
37
 
42
38
  def stop_on=(val)
43
- @stop_on = (val.respond_to?(:call) ? val : lambda { |obj| val })
39
+ @stop_on = as_proc(val)
40
+ end
41
+
42
+ private
43
+
44
+ def as_proc(val)
45
+ val.respond_to?(:call) ? val : proc { val }
44
46
  end
45
47
  end
46
48
  end
data/lib/pipe/iterator.rb CHANGED
@@ -31,7 +31,7 @@ module Pipe
31
31
  if config.raise_on_error?
32
32
  Error.process(
33
33
  :data => { :subject => subject },
34
- :error => e,
34
+ :error => error,
35
35
  :namespace => IterationError,
36
36
  )
37
37
  end
data/lib/pipe/reducer.rb CHANGED
@@ -10,7 +10,7 @@ module Pipe
10
10
  def reduce
11
11
  through.reduce(subject) { |subj, method|
12
12
  begin
13
- break subj if config.break?(subj)
13
+ break subj if config.break?(subj, method, through)
14
14
 
15
15
  process(subj, method)
16
16
  rescue => e
@@ -41,7 +41,7 @@ module Pipe
41
41
  end
42
42
 
43
43
  def process(subj, method)
44
- if config.skip?(subj)
44
+ if config.skip?(subj, method, through)
45
45
  subj
46
46
  else
47
47
  context.send(method, subj)
data/lib/pipe/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pipe
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/pipe-ruby.gemspec CHANGED
@@ -17,7 +17,10 @@ Gem::Specification.new do |spec|
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
+ spec.required_ruby_version = "~> 2.1"
20
21
 
21
22
  spec.add_development_dependency "bundler", "~> 1.7"
22
23
  spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.1"
25
+ spec.add_development_dependency "pry"
23
26
  end
@@ -0,0 +1,131 @@
1
+ require "spec_helper"
2
+
3
+ describe Pipe::Config do
4
+ describe "defaults" do
5
+ it "sets error handlers to a blank array" do
6
+ expect(Pipe::Config.new.error_handlers).to eq([])
7
+ end
8
+
9
+ it "sets raise_on_error to true" do
10
+ expect(Pipe::Config.new.raise_on_error).to eq(true)
11
+ end
12
+
13
+ it "sets skip_on to a proc which returns false" do
14
+ expect(Pipe::Config.new.skip_on.call).to eq(false)
15
+ end
16
+
17
+ it "sets stop_on to a proc which returns false" do
18
+ expect(Pipe::Config.new.stop_on.call).to eq(false)
19
+ end
20
+ end
21
+
22
+ describe "#error_handler" do
23
+ context "when passed a block" do
24
+ it "adds the block to the error_handlers array" do
25
+ config = Pipe::Config.new
26
+ block = proc { "error handler" }
27
+
28
+ expect{ config.error_handler(&block) }
29
+ .to change{ config.error_handlers.size }
30
+ .by(1)
31
+
32
+ expect(config.error_handlers.last).to eq(block)
33
+ end
34
+ end
35
+ end
36
+
37
+ describe "#break?" do
38
+ it "returns a truthy version of the result of calling stop_on" do
39
+ config = Pipe::Config.new(:stop_on => proc { 1 })
40
+ result = config.break?("")
41
+ expect(result).to eq(true)
42
+ config = Pipe::Config.new(:stop_on => proc { nil })
43
+ result = config.break?("")
44
+ expect(result).to eq(false)
45
+ end
46
+
47
+ it "passes the first argument to stop_on" do
48
+ config = Pipe::Config.new
49
+ arg = Object.new
50
+
51
+ expect(config.stop_on).to receive(:call).with(arg)
52
+ config.break?(arg)
53
+ end
54
+ end
55
+
56
+ describe "#raise_on_error?" do
57
+ it "returns a truthy version of raise_on_error" do
58
+ config = Pipe::Config.new
59
+ config.raise_on_error = nil
60
+ expect(config.raise_on_error?).to eq(false)
61
+ config.raise_on_error = true
62
+ expect(config.raise_on_error?).to eq(true)
63
+ config.raise_on_error = "yes"
64
+ expect(config.raise_on_error?).to eq(true)
65
+ end
66
+ end
67
+
68
+ describe "#skip?" do
69
+ it "returns a truthy version of the result of calling skip_on" do
70
+ config = Pipe::Config.new(:skip_on => proc { 42 })
71
+ result = config.skip?("", "", "", "")
72
+ expect(result).to eq(true)
73
+ config = Pipe::Config.new(:skip_on => proc { false })
74
+ result = config.skip?("", "", "", "")
75
+ expect(result).to eq(false)
76
+ end
77
+
78
+ it "passes it's arguments to skip_on" do
79
+ config = Pipe::Config.new
80
+
81
+ expect(config.skip_on).to receive(:call).with(1,2,3,4)
82
+ config.skip?(1,2,3,4)
83
+ end
84
+ end
85
+
86
+ describe "#skip_on=" do
87
+ describe "when the value passed is a proc" do
88
+ it "assigns the value as is" do
89
+ val = proc { nil }
90
+ config = Pipe::Config.new
91
+
92
+ config.skip_on = val
93
+ expect(config.skip_on).to eq(val)
94
+ end
95
+ end
96
+
97
+ describe "when the value passed is not a proc" do
98
+ it "wraps the value in a proc prior to assignment" do
99
+ val = "not a proc"
100
+ config = Pipe::Config.new
101
+
102
+ config.skip_on = val
103
+ expect(config.skip_on).to be_a(Proc)
104
+ expect(config.skip_on.call).to eq(val)
105
+ end
106
+ end
107
+ end
108
+
109
+ describe "#stop_on=" do
110
+ describe "when the value passed is a proc" do
111
+ it "assigns the value as is" do
112
+ val = proc { nil }
113
+ config = Pipe::Config.new
114
+
115
+ config.stop_on = val
116
+ expect(config.stop_on).to eq(val)
117
+ end
118
+ end
119
+
120
+ describe "when the value passed is not a proc" do
121
+ it "wraps the value in a proc prior to assignment" do
122
+ val = "not a proc"
123
+ config = Pipe::Config.new
124
+
125
+ config.stop_on = val
126
+ expect(config.stop_on).to be_a(Proc)
127
+ expect(config.stop_on.call).to eq(val)
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,54 @@
1
+ require "spec_helper"
2
+
3
+ Namespace = Module.new
4
+
5
+ describe Pipe::Error do
6
+ describe ".process" do
7
+ it "re-renders the error under the namespace passed" do
8
+ begin
9
+ begin
10
+ not_a_method(:boom)
11
+ rescue => e
12
+ Pipe::Error.process(:error => e, :namespace => Namespace)
13
+ end
14
+ rescue => e
15
+ @err = e
16
+ end
17
+
18
+ expect(@err.class.name).to eq("Namespace::NoMethodError")
19
+ end
20
+
21
+ it "adds the original error class to the message" do
22
+ begin
23
+ begin
24
+ not_a_method(:boom)
25
+ rescue => e
26
+ Pipe::Error.process(:error => e, :namespace => Namespace)
27
+ end
28
+ rescue => e
29
+ @err = e
30
+ end
31
+
32
+ expect(@err.message).to match(Regexp.new("NoMethodError"))
33
+ end
34
+
35
+ it "adds the data passed to the message" do
36
+ data = {:one => 1, :two => 2}
37
+ begin
38
+ begin
39
+ not_a_method(:boom)
40
+ rescue => e
41
+ Pipe::Error.process(
42
+ :data => data,
43
+ :error => e,
44
+ :namespace => Namespace
45
+ )
46
+ end
47
+ rescue => e
48
+ @err = e
49
+ end
50
+
51
+ expect(@err.message).to match(Regexp.new("#{data}"))
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,87 @@
1
+ require "spec_helper"
2
+
3
+ describe Pipe::Iterator do
4
+ describe "#iterate" do
5
+ it "passes config, context and through to Reducer for each subject" do
6
+ subjects = [1, 2]
7
+ config = Pipe::Config.new
8
+ context = Object.new
9
+ through = [:to_s, :to_sym]
10
+ iterator = Pipe::Iterator.new(
11
+ :config => config,
12
+ :context => context,
13
+ :subjects => subjects,
14
+ :through => through
15
+ )
16
+
17
+ subjects.each do |subject|
18
+ expect(Pipe::Reducer).to receive(:new).with(
19
+ :config => config,
20
+ :context => context,
21
+ :subject => subject,
22
+ :through => through
23
+ ).and_return(double(:reduce => true))
24
+ end
25
+
26
+ iterator.iterate
27
+ end
28
+
29
+ describe "when an error occurs" do
30
+ describe "and Config#raise_on_error is set to false" do
31
+ it "does not raise" do
32
+ dub = Object.new
33
+ dub.singleton_class.send(:define_method, :reduce) do
34
+ raise StandardError, "fail"
35
+ end
36
+ iterator = Pipe::Iterator.new(
37
+ :config => Pipe::Config.new(:raise_on_error => false),
38
+ :context => Object.new,
39
+ :subjects => [Object.new],
40
+ :through => [:to_s]
41
+ )
42
+ expect(Pipe::Reducer).to receive(:new).and_return(dub)
43
+
44
+ expect{ iterator.iterate }.to_not raise_error
45
+ end
46
+
47
+ it "returns an array of the original items" do
48
+ dub = Object.new
49
+ dub.singleton_class.send(:define_method, :reduce) do
50
+ raise StandardError, "fail"
51
+ end
52
+ subjects = [Object.new, Object.new]
53
+ iterator = Pipe::Iterator.new(
54
+ :config => Pipe::Config.new(:raise_on_error => false),
55
+ :context => Object.new,
56
+ :subjects => subjects,
57
+ :through => [:to_s]
58
+ )
59
+ expect(Pipe::Reducer)
60
+ .to receive(:new)
61
+ .and_return(dub)
62
+ .exactly(2).times
63
+
64
+ expect(iterator.iterate).to eq(subjects)
65
+ end
66
+ end
67
+
68
+ describe "and Config#raise_on_error is set to true" do
69
+ it "raises" do
70
+ dub = Object.new
71
+ dub.singleton_class.send(:define_method, :reduce) do
72
+ raise StandardError, "fail"
73
+ end
74
+ iterator = Pipe::Iterator.new(
75
+ :config => Pipe::Config.new(:raise_on_error => true),
76
+ :context => Object.new,
77
+ :subjects => [Object.new],
78
+ :through => [:to_s]
79
+ )
80
+ expect(Pipe::Reducer).to receive(:new).and_return(dub)
81
+
82
+ expect{ iterator.iterate }.to raise_error
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,160 @@
1
+ require "spec_helper"
2
+
3
+ describe Pipe::Reducer do
4
+ describe "#reduce" do
5
+ it "calls each method in #through, passing #subject as the first arg" do
6
+ context = Class.new do
7
+ def stringify(subj)
8
+ subj.to_s
9
+ end
10
+
11
+ def symbolize(subj)
12
+ subj.to_sym
13
+ end
14
+ end.new
15
+ subject = Object.new
16
+ str_subject = subject.to_s
17
+ through = [:stringify, :symbolize]
18
+ reducer = Pipe::Reducer.new(
19
+ :config => Pipe::Config.new,
20
+ :context => context,
21
+ :subject => subject,
22
+ :through => through
23
+ )
24
+
25
+ expect(subject).to receive(:to_s).and_return(str_subject)
26
+ expect(str_subject).to receive(:to_sym)
27
+
28
+ reducer.reduce
29
+ end
30
+
31
+ it "returns the resulting value" do
32
+ context = Class.new do
33
+ def stringify(subj)
34
+ subj.to_s
35
+ end
36
+
37
+ def symbolize(subj)
38
+ subj.to_sym
39
+ end
40
+ end.new
41
+ subject = Object.new
42
+ through = [:stringify, :symbolize]
43
+ reducer = Pipe::Reducer.new(
44
+ :config => Pipe::Config.new,
45
+ :context => context,
46
+ :subject => subject,
47
+ :through => through
48
+ )
49
+
50
+ expect(reducer.reduce).to eq(subject.to_s.to_sym)
51
+ end
52
+
53
+ it "breaks when told to and returns the current altered value of subject" do
54
+ config = Pipe::Config.new(
55
+ :stop_on => proc { |_, method|
56
+ method == :downcase
57
+ }
58
+ )
59
+ context = Class.new do
60
+ def downcase(subj)
61
+ subj.downcase
62
+ end
63
+
64
+ def stringify(subj)
65
+ subj.to_s
66
+ end
67
+
68
+ def symbolize(subj)
69
+ subj.to_sym
70
+ end
71
+ end.new
72
+ subject = Object.new
73
+ through = [:stringify, :downcase, :symbolize]
74
+ reducer = Pipe::Reducer.new(
75
+ :config => config,
76
+ :context => context,
77
+ :subject => subject,
78
+ :through => through
79
+ )
80
+
81
+ expect(reducer.reduce).to eq(subject.to_s)
82
+ end
83
+
84
+ it "skips when told to" do
85
+ config = Pipe::Config.new(
86
+ :skip_on => proc { |_, method|
87
+ method == :downcase
88
+ }
89
+ )
90
+ context = Class.new do
91
+ def downcase(subj)
92
+ subj.downcase
93
+ end
94
+
95
+ def stringify(subj)
96
+ subj.to_s
97
+ end
98
+
99
+ def symbolize(subj)
100
+ subj.to_sym
101
+ end
102
+ end.new
103
+ subject = Object.new
104
+ through = [:stringify, :downcase, :symbolize]
105
+ reducer = Pipe::Reducer.new(
106
+ :config => config,
107
+ :context => context,
108
+ :subject => subject,
109
+ :through => through
110
+ )
111
+
112
+ expect(reducer.reduce).to eq(subject.to_s.to_sym)
113
+ end
114
+
115
+ it "calls the error handlers when a raise occurs" do
116
+ handler1 = Proc.new {}
117
+ handler2 = Proc.new {}
118
+ config = Pipe::Config.new(:error_handlers => [handler1, handler2])
119
+ context = Class.new do
120
+ ExpectedError = Class.new(StandardError)
121
+
122
+ def bomb
123
+ raise ExpectedError, "BOOM!"
124
+ end
125
+ end
126
+
127
+ expect(handler1).to receive(:call)
128
+ expect(handler2).to receive(:call)
129
+
130
+ expect{
131
+ Pipe::Reducer.new(
132
+ :config => config,
133
+ :context => context,
134
+ :subject => Object.new,
135
+ :through => [:bomb]
136
+ ).reduce
137
+ }.to raise_error
138
+ end
139
+
140
+ it "honors Config#raise_on_error" do
141
+ config = Pipe::Config.new(:raise_on_error => false)
142
+ context = Class.new do
143
+ AnotherExpectedError = Class.new(StandardError)
144
+
145
+ def bomb
146
+ raise AnotherExpectedError, "BOOM!"
147
+ end
148
+ end
149
+
150
+ expect{
151
+ Pipe::Reducer.new(
152
+ :config => config,
153
+ :context => context,
154
+ :subject => Object.new,
155
+ :through => [:bomb]
156
+ ).reduce
157
+ }.to_not raise_error
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,18 @@
1
+ require "pry"
2
+ require "pipe"
3
+
4
+ module VerifyAndResetHelpers
5
+ def verify(object)
6
+ RSpec::Mocks.proxy_for(object).verify
7
+ end
8
+
9
+ def reset(object)
10
+ RSpec::Mocks.proxy_for(object).reset
11
+ end
12
+ end
13
+
14
+ RSpec.configure do |config|
15
+ config.order = "random"
16
+
17
+ config.include VerifyAndResetHelpers
18
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pipe-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Matthews
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-23 00:00:00.000000000 Z
11
+ date: 2015-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  description: ''
42
70
  email:
43
71
  - oss@teamsnap.com
@@ -46,6 +74,7 @@ extensions: []
46
74
  extra_rdoc_files: []
47
75
  files:
48
76
  - ".gitignore"
77
+ - ".rspec"
49
78
  - Gemfile
50
79
  - LICENSE.txt
51
80
  - README.md
@@ -59,6 +88,11 @@ files:
59
88
  - lib/pipe/reducer.rb
60
89
  - lib/pipe/version.rb
61
90
  - pipe-ruby.gemspec
91
+ - spec/pipe/config_spec.rb
92
+ - spec/pipe/error_spec.rb
93
+ - spec/pipe/iterator_spec.rb
94
+ - spec/pipe/reducer_spec.rb
95
+ - spec/spec_helper.rb
62
96
  homepage: ''
63
97
  licenses:
64
98
  - MIT
@@ -69,9 +103,9 @@ require_paths:
69
103
  - lib
70
104
  required_ruby_version: !ruby/object:Gem::Requirement
71
105
  requirements:
72
- - - ">="
106
+ - - "~>"
73
107
  - !ruby/object:Gem::Version
74
- version: '0'
108
+ version: '2.1'
75
109
  required_rubygems_version: !ruby/object:Gem::Requirement
76
110
  requirements:
77
111
  - - ">="
@@ -83,4 +117,9 @@ rubygems_version: 2.2.2
83
117
  signing_key:
84
118
  specification_version: 4
85
119
  summary: Ruby implementation of the UNIX pipe
86
- test_files: []
120
+ test_files:
121
+ - spec/pipe/config_spec.rb
122
+ - spec/pipe/error_spec.rb
123
+ - spec/pipe/iterator_spec.rb
124
+ - spec/pipe/reducer_spec.rb
125
+ - spec/spec_helper.rb