pipe-ruby 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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