waterfall 1.0.2 → 1.0.3

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: 129bb57a29df2394c12511053c406490b8a99674
4
- data.tar.gz: e7260b30ce91910cdb8d1466a39898000bde0fa5
3
+ metadata.gz: 926586c0692a21fc3e8110a28818a907c8b10d2d
4
+ data.tar.gz: 438651277ce425c2a9667e003a9f4deb56bfd61a
5
5
  SHA512:
6
- metadata.gz: 80c9da8ab97a26854f8fa5700e1a8669409f2212fe16660805dff074894df2ba89aea2bec52efd232b874646de286cc992f9ad8d3be679fd63beefffdaa132fd
7
- data.tar.gz: c9253aa6ce5ce77577398006957eab695b8489db2b914adfb68e44b0319820d6e8fa92159ed6a77aed93726f803e476194e7227bb8a844f82adf7283de6ddd9a
6
+ metadata.gz: c4c1bb9dc927cd752e7b184274373d624ad625d5d316e0930238c96a75c3fa0b5ee9129f4ef3865271d490f5e1d86d0eadd60e553f6a06e3c9636a15cbba5fa6
7
+ data.tar.gz: f10b48892968cb75add57c80b13374413820902a12324c97f3b531a6c1f96c4d832762732c3b61d6e3116fa0e247f3830b3e93edf10f762330182a78a7de58eb
@@ -2,6 +2,8 @@ language: ruby
2
2
  rvm:
3
3
  - 2.0.0
4
4
  - 2.1.0
5
+ - 2.2.0
6
+ - 2.3.0
5
7
  script: bundle exec rspec spec
6
8
  addons:
7
9
  code_climate:
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- waterfall (1.0.2)
4
+ waterfall (1.0.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -51,3 +51,6 @@ DEPENDENCIES
51
51
  rake
52
52
  rspec (= 3.2)
53
53
  waterfall!
54
+
55
+ BUNDLED WITH
56
+ 1.13.4
data/README.md CHANGED
@@ -6,95 +6,78 @@
6
6
 
7
7
  Be able to chain ruby commands, and treat them like a flow.
8
8
 
9
- General presentation slides can [be found here](https://slides.com/apneadiving/code-ruby-like-you-build-legos).
9
+ It provides a new approach to flow control.
10
10
 
11
- Check [the slides here](https://slides.com/apneadiving/handling-error-in-ruby-rails) for a refactoring example.
11
+ When logic is complicated, waterfalls show their true power and let you write intention revealing code. Above all they excel at chaining services.
12
12
 
13
+ General presentation slides can [be found here](https://slides.com/apneadiving/code-ruby-like-you-build-lego).
13
14
 
14
- #### Basic example
15
+ #### Overview
15
16
 
16
- ```ruby
17
- Wf.new
18
- .when_falsy { @user.update(user_params) }
19
- .dam { @user.errors }
20
- .chain { render json: @user }
21
- .on_dam { |errors| render json: { errors: errors.full_messages }, status: 422 }
22
- ```
17
+ A waterfall object has its own flow of commands, you can chain your commands and if something wrong happens, you dam the flow which bypasses the rest of the commands.
23
18
 
24
- When logic is complicated, waterfalls show their true power and let you write intention revealing code. Above all they excel at chaining services.
19
+ Here is a basic representation:
20
+ - green, the flow goes on, `chain` by `chain`
21
+ - red its bypassed and only `on_dam` blocks are executed.
25
22
 
26
- #### Rationale
27
- Coding is all about writing a flow of commands.
23
+ ![Waterfall Logo](http://apneadiving.github.io/images/waterfall_principle.png)
28
24
 
29
- Generally you basically go on, unless something wrong happens. Whenever this happens you have to halt the flow and send feedback to the user.
25
+ #### Example
30
26
 
31
- When conditions stack up, readability decreases.
32
-
33
- One way to solve it is to create abstractions to wrap your business logic (service objects or the like). There some questions arise:
34
- * what should a good service return?
35
- * how to handle errors?
36
- * how to call a service within a service?
37
- * how to chain services / commands
38
-
39
- Those topics are discussed in [the slides here](https://slides.com/apneadiving/service-objects-waterfall-rails).
27
+ ```ruby
28
+ class FetchUser
29
+ include Waterfall
40
30
 
31
+ def initialize(user_id)
32
+ @user_id = user_id
33
+ end
41
34
 
42
- ## Wf object
35
+ def call
36
+ chain { @response = HTTParty.get("https://jsonplaceholder.typicode.com/users/#{@user_id}") }
37
+ when_falsy { @response.success? }
38
+ .dam { "Error status #{@response.code}" }
39
+ chain(:user) { @response.body }
40
+ end
41
+ end
42
+ ```
43
43
 
44
- The `Wf` class just includes the `Waterfall` module. It makes it easy to create standalone waterfalls mostly to chain actions or to chain services including `Waterfall` or returning a `Wf` object.
44
+ and call / chain:
45
45
 
46
- Basically `chain` statements are executed in the order they appear. But if ever the waterfall is dammed, they are skipped.
46
+ ```ruby
47
+ Wf.new
48
+ .chain(user1: :user) { FetchUser.new(1) }
49
+ .chain(user2: :user) { FetchUser.new(2) }
50
+ .chain {|outflow| puts(outflow.user1, outflow.user2) } # report success
51
+ .on_dam {|error| puts(error) } # report error
52
+ ```
47
53
 
48
- If a main waterfall chains another waterfall and the child waterfall is dammed, the main waterfall would be dammed.
54
+ Which works like:
49
55
 
50
- The point is to be able to be able to chain an expected set of actions whenever everything works fine. And to be able to quickly stop and get the errors back whenever something wrong happens.
56
+ ![Waterfall Logo](http://apneadiving.github.io/images/waterfall_full_example.png)
51
57
 
52
58
  ## Installation
53
59
 
54
- There exists a gem on rubygem with the same name but its not mine :)
55
-
56
- For installation:
60
+ For installation, in your gemfile:
57
61
 
58
- gem 'waterfall', git: 'git://github.com/apneadiving/waterfall.git'
62
+ gem 'waterfall'
59
63
 
64
+ then `bundle` as usual.
60
65
 
61
66
  ## Waterfall mixin
62
67
 
63
- ### Overview
64
-
65
- The following are equivalent:
66
- ```ruby
67
- # 1
68
- Wf.new.chain{ 1 + 1 }
69
-
70
- # 2
71
- class MyService
72
- include Waterfall
73
-
74
- def call
75
- self.chain{ 1 + 1 }
76
- end
77
- end
78
-
79
- MyService.new.call
80
- ```
81
-
82
- This illustrates one convention classes including the mixin should obey: respond to `call`
83
-
84
68
  ### Outputs
85
69
 
86
70
  Each waterfall has its own `outflow` and `error_pool`.
87
71
 
88
72
  `outflow` is an Openstruct so you can get/set its property like a hash or like a standard object.
89
73
 
90
- For the `error_pool`, its up to you. But using Rails, I usually `include ActiveModel::Validations` in my services.
74
+ ### Wiki
75
+ Wiki contains many details, please check appropriate pages:
91
76
 
92
- Thus you:
77
+ - [Predicates](https://github.com/apneadiving/waterfall/wiki/Predicates)
78
+ - [Wf Object](https://github.com/apneadiving/waterfall/wiki/Wf-object)
79
+ - [Testing](https://github.com/apneadiving/waterfall/wiki/Testing)
93
80
 
94
- * have a standard way to deal with errors
95
- * can deal with multiple errors
96
- * support I18n out of the box
97
- * can use your model errors out of the box
98
81
 
99
82
  ## Illustration of chaining
100
83
  Doing
@@ -121,80 +104,6 @@ is the same as doing:
121
104
 
122
105
  Hopefully you better get the chaining power this way.
123
106
 
124
- ## Predicates
125
-
126
- ### chain(name_or_mapping = nil, &block) | block signature: (outflow, waterfall)
127
-
128
- Chain is the main predicate, what it does depends on what the block returns
129
- ```ruby
130
- # main waterfall
131
- Wf.new
132
- .chain(foo: :bar) do
133
- # child waterfall
134
- Wf.new.chain(:bar){ 1 }.chain(:baz){ 2 }.chain{ 3 }
135
- end
136
- ```
137
- ##### when block doesn't return a waterfall
138
-
139
- The child waterfall would have the following outflow: `{ bar: 1, baz: 2 }`
140
-
141
- This illustrates that when the block returns a value which is not a waterfall, it stores the returned value of the block inside the `name_or_mapping` key of the `outflow` or doesn't store it if `name_or_mapping` is `nil`.
142
-
143
- Be aware those are equivalent:
144
-
145
- ```ruby
146
- Wf.new.chain(:foo) { 1 }
147
- Wf.new.chain{|outflow| outflow[:foo] = 1 }
148
- Wf.new.chain{|outflow| outflow.foo = 1 }
149
- Wf.new.chain{|outflow, waterfall| waterfall.update_outflow(:foo, 1) }
150
- Wf.new.chain{|outflow, waterfall| waterfall.outflow.foo = 1 }
151
- ```
152
- ##### when block returns a waterfall
153
-
154
- The main waterfall would have the following outflow: `{ foo: 1 }`
155
-
156
- The main waterfall above receives the child waterfall as a return value of its `chain` block.
157
- All waterfalls have independent outflows.
158
-
159
- If `name_or_mapping` is `nil`, the main waterfall's `outflow` wouldnt be affected by its child (but if the child is dammed, the parent will be dammed).
160
-
161
- If `name_or_mapping` is a `hash`, the format must be read as `{ name_in_parent_waterfall: :name_from_child_waterfall}`. In the above example, the child returned an `outflow` with a `bar` key which has be renamed as `foo` in the main one.
162
-
163
- It may look useless, because most of the time you may not rename, but... It makes things clear. You know exactly what you expect and you know exactly that you dont expect the rest the child may provide.
164
-
165
- ### when_falsy(&block) | block signature: (error_pool, waterfall)
166
-
167
- This predicate must ***always*** be used followed with `dam` like:
168
-
169
- ```ruby
170
- Wf.new
171
- .chain(:foo) { 1 }
172
- .when_falsy { true }
173
- .dam { "this wouldnt be executed" }
174
- .when_falsy { false }
175
- .dam { "errrrr" }
176
- .chain(:bar) { 2 }
177
- .on_dam {|error_pool| puts error_pool }
178
- ```
179
-
180
- If the block returns a falsy value, it executes the `dam` block, which will store the returned value in the `error_pool`.
181
-
182
- Once the waterfall is dammed, all following `chain` blocks are skipped (wont be executed). And all the following `on_dam` block would be executed.
183
-
184
- As a result the example above would return a waterfall object having its `outflow` equal to `{ foo: 1 }`. Remember: it has been dammed before `bar` would have been set.
185
-
186
- Its `error_pool` would be `"errrrr"` and it would be `puts` as a result of the `on_dam`
187
-
188
- Be aware those are equivalent:
189
-
190
- ```ruby
191
- Wf.new.when_falsy{ false }.dam{ 'errrr' }
192
- Wf.new.chain{ |outflow, waterfall| waterfall.dam('errrr') unless false }
193
- ```
194
-
195
- ### when_truthy(&block) | block signature: (error_pool, waterfall)
196
-
197
- Behaves the same as `when_falsy` except it dams when its return value is truthy
198
107
 
199
108
  ## Syntactic sugar
200
109
  Given:
@@ -219,85 +128,7 @@ Wf.new
219
128
  ```
220
129
  Both are the same: if a block returns a waterfall which was not executed, it will execute it (hence the `call` convention)
221
130
 
222
- ### on_dam(&block) | block signature: (error_pool, outflow, waterfall)
223
-
224
- Its block is executed whenever the waterfall is dammed, skipped otherwise.
225
-
226
- ```ruby
227
- Wf.new
228
- .when_falsy { false }
229
- .on_dam {|error_pool, outflow, waterfall| puts error_pool }
230
- ```
231
-
232
- ## Error propagation
233
131
 
234
- Whenever a a waterfall is dammed, all the following chains are skipped.
235
-
236
- * all the following chains are skipped
237
- * all `on_dam` blocks are executed
238
-
239
- ## Testing a Waterfall service
240
-
241
- Say I have this service:
242
- ```ruby
243
- class AuthenticateUser
244
- include Waterfall
245
- include ActiveModel::Validations
246
-
247
- validates :user, presence: true
248
- attr_reader :user
249
-
250
- def initialize(email, password)
251
- @email, @password = email, @password
252
- end
253
-
254
- def call
255
- self
256
- .chain { @user = User.authenticate(@email, @password) }
257
- .when_falsy { valid? }
258
- .dam { errors }
259
- .chain(:user) { user }
260
- end
261
- end
262
- ```
263
- I could spec it this way:
264
- ```ruby
265
- describe AuthenticateUser do
266
- let(:email) { 'email@email.com' }
267
- let(:password) { 'password' }
268
- subject(:service) { AuthenticateUser.new(email, password).call }
269
-
270
- context "when given valid credentials" do
271
- let(:user) { double(:user) }
272
-
273
- before do
274
- allow(User).to receive(:authenticate).with(email, password).and_return(user)
275
- end
276
-
277
- it "succeeds" do
278
- expect(service.dammed?).to be false
279
- end
280
-
281
- it "provides the user" do
282
- expect(service.outflow.user).to eq(user)
283
- end
284
- end
285
-
286
- context "when given invalid credentials" do
287
- before do
288
- allow(User).to receive(:authenticate).with(email, password).and_return(nil)
289
- end
290
-
291
- it "fails" do
292
- expect(service.dammed?).to be true
293
- end
294
-
295
- it "provides a failure message" do
296
- expect(service.error_pool).to be_present
297
- end
298
- end
299
- end
300
- ```
301
132
  Syntax advice
302
133
  =========
303
134
  ```ruby
@@ -323,6 +154,16 @@ self
323
154
 
324
155
  Tips
325
156
  =========
157
+ ### Error pool
158
+ For the error_pool, its up to you. But using Rails, I usually include ActiveModel::Validations in my services.
159
+
160
+ Thus you:
161
+
162
+ - have a standard way to deal with errors
163
+ - can deal with multiple errors
164
+ - support I18n out of the box
165
+ - can use your model errors out of the box
166
+
326
167
  ### Conditional Flow
327
168
  In a service, there is one and single flow, so if you need conditionals to branch off, you can do:
328
169
  ```ruby
@@ -335,11 +176,60 @@ else
335
176
  end
336
177
  ```
337
178
 
179
+ ### Rails and transactions
180
+ I'm used to wrap every single object involving database interactions within transactions, so it can be rolled back on error.
181
+ Here is my usual setup:
182
+ ```ruby
183
+ module Waterfall
184
+ extend ActiveSupport::Concern
338
185
 
339
- Examples
340
- =========
341
- Check the [wiki for other examples](https://github.com/apneadiving/waterfall/wiki).
186
+ class Rollback < StandardError; end
187
+
188
+ def with_transaction(&block)
189
+ ActiveRecord::Base.transaction(requires_new: true) do
190
+ yield
191
+ on_dam do
192
+ raise Waterfall::Rollback
193
+ end
194
+ end
195
+ rescue Waterfall::Rollback
196
+ self
197
+ end
198
+ end
199
+ ```
200
+
201
+ And to use it:
202
+ ```ruby
203
+ class AuthenticateUser
204
+ include Waterfall
205
+ include ActiveModel::Validations
206
+
207
+ validates :user, presence: true
208
+ attr_reader :user
209
+
210
+ def initialize(email, password)
211
+ @email, @password = email, @password
212
+ end
213
+
214
+ def call
215
+ with_transaction do
216
+ chain { @user = User.authenticate(@email, @password) }
217
+ when_falsy { valid? }
218
+ .dam { errors }
219
+ chain(:user) { user }
220
+ end
221
+ end
222
+ end
223
+ ```
224
+ The huge benefit is that if you call services from services, everything will be rolled back.
225
+
226
+ Examples / Presentations
227
+ ========================
228
+ - Check the [wiki for other examples](https://github.com/apneadiving/waterfall/wiki/Refactoring-examples).
229
+ - [Structure and chain your POROs](http://slides.com/apneadiving/structure-and-chain-your-poros).
230
+ - [Service objects implementations](https://slides.com/apneadiving/service-objects-waterfall-rails).
231
+ - [Handling error in Rails](https://slides.com/apneadiving/handling-error-in-ruby-rails).
342
232
 
343
233
  Thanks
344
234
  =========
345
- Huge thanks to [laxrph10](https://github.com/laxrph10) for the help during infinite naming brainstorming.
235
+ Huge thanks to [robhorrigan](https://github.com/robhorrigan) for the help during infinite naming brainstorming.
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,6 @@
1
+ ===1.0.3
2
+ - Small refactors
3
+ - if waterfall 1 calls waterfall 2 and waterfall2 is dammed, now waterfall1 is able to get values from waterfall2's outflow
4
+
5
+ ===1.0.2
6
+ Initial release
@@ -5,17 +5,6 @@ module Waterfall
5
5
  obj.respond_to?(:is_waterfall?) && obj.is_waterfall?
6
6
  end
7
7
 
8
- def chained_waterfall(child_waterfall)
9
- child_waterfall.call unless child_waterfall.flowing?
10
-
11
- if child_waterfall.dammed?
12
- @root.dam child_waterfall.error_pool
13
- else
14
- yield
15
- end
16
- self
17
- end
18
-
19
8
  def yield_args
20
9
  [@root.outflow, @root]
21
10
  end
@@ -16,11 +16,17 @@ module Waterfall
16
16
  end
17
17
 
18
18
  def map_waterfalls(child_waterfall, mapping)
19
- chained_waterfall(child_waterfall) do
20
- mapping.each do |k, v|
21
- @root.update_outflow(k, child_waterfall.outflow[v])
22
- end
19
+ child_waterfall.call unless child_waterfall.flowing?
20
+
21
+ mapping.each do |k, v|
22
+ @root.update_outflow(k, child_waterfall.outflow[v])
23
+ end
24
+
25
+ if child_waterfall.dammed?
26
+ @root.dam child_waterfall.error_pool
23
27
  end
28
+
29
+ self
24
30
  end
25
31
  end
26
32
  end
@@ -1,19 +1,10 @@
1
- module Waterfall
2
- class WhenFalsy < Base
3
-
4
- def initialize(root)
5
- @root = root
6
- end
1
+ require_relative 'when_truthy'
7
2
 
8
- def call
9
- @output = yield(*yield_args)
10
- end
3
+ module Waterfall
4
+ class WhenFalsy < WhenTruthy
11
5
 
12
- def dam
13
- if !@root.dammed? && !@output
14
- @root.dam yield(*yield_args)
15
- end
16
- @root
6
+ def condition?
7
+ ! super
17
8
  end
18
9
  end
19
10
  end
@@ -10,10 +10,14 @@ module Waterfall
10
10
  end
11
11
 
12
12
  def dam
13
- if !@root.dammed? && @output
13
+ if !@root.dammed? && condition?
14
14
  @root.dam yield(*yield_args)
15
15
  end
16
16
  @root
17
17
  end
18
+
19
+ def condition?
20
+ @output
21
+ end
18
22
  end
19
23
  end
@@ -1,3 +1,3 @@
1
1
  module Waterfall
2
- VERSION = "1.0.2"
2
+ VERSION = "1.0.3"
3
3
  end
@@ -7,33 +7,48 @@ describe 'Wf' do
7
7
 
8
8
  it "yields wf outflow" do
9
9
  wf
10
- .chain {|outflow| outflow[:bar] = 'bar' }
11
- .chain {|outflow| @bar = outflow[:bar] }
12
- expect(wf.outflow[:bar]).to eq 'bar'
10
+ .chain {|outflow| outflow.bar = 'bar' }
11
+ .chain {|outflow| @bar = outflow.bar }
12
+
13
+ expect(wf.outflow.bar).to eq 'bar'
13
14
  expect(@bar).to eq 'bar'
14
15
  end
15
16
 
16
17
  it "assigns outflow's key the value of the block" do
17
18
  wf
18
19
  .chain(:bar) { 'bar' }
19
- expect(wf.outflow[:bar]).to eq 'bar'
20
+ expect(wf.outflow.bar).to eq 'bar'
20
21
  end
21
22
 
22
23
  context "wf internals" do
23
24
  it "dam from within" do
24
25
  wf
25
26
  .chain {|outflow, waterfall| waterfall.dam('errrrr') }
26
- .on_dam {|error_pool| @errors = error_pool }
27
27
 
28
28
  expect(wf.dammed?).to be true
29
- expect(@errors).to eq 'errrrr'
29
+ expect(wf.error_pool).to eq 'errrrr'
30
+ end
31
+
32
+ it "expose child waterfall outflow even if dammed (or at least what was computed)" do
33
+ wf
34
+ .chain(bar: :bar, baz: :baz) do
35
+ Wf.new
36
+ .chain(:bar) { 'bar' }
37
+ .dam('boom')
38
+ .chain(:baz) { 'baz' }
39
+ end
40
+
41
+ expect(wf.dammed?).to be true
42
+ expect(wf.error_pool).to eq 'boom'
43
+ expect(wf.outflow.bar).to eq 'bar'
44
+ expect(wf.outflow.baz).to eq nil
30
45
  end
31
46
 
32
47
  it "outflow from within" do
33
48
  wf
34
- .chain {|outflow, waterfall| waterfall.outflow[:foo] = 1 }
49
+ .chain {|outflow, waterfall| waterfall.outflow.foo = 1 }
35
50
 
36
- expect(wf.outflow[:foo]).to eq 1
51
+ expect(wf.outflow.foo).to eq 1
37
52
  end
38
53
  end
39
54
 
@@ -47,7 +62,7 @@ describe 'Wf' do
47
62
 
48
63
  expect(wf.outflow.foo).to be nil
49
64
  expect(wf.outflow.bar).to be nil
50
- expect(wf.outflow[:baz]).to eq waterfall.outflow[:foo]
65
+ expect(wf.outflow.baz).to eq waterfall.outflow.foo
51
66
  end
52
67
  end
53
68
  end
@@ -103,7 +118,6 @@ describe 'Wf' do
103
118
  .when_falsy { my_proc.call(bool) }
104
119
  .dam { 'err' }
105
120
  .chain { @foo = 1 }
106
- .on_dam { |error_pool| @error = error_pool }
107
121
  end
108
122
 
109
123
  context "main context not dammed" do
@@ -111,13 +125,13 @@ describe 'Wf' do
111
125
 
112
126
  it "when actually falsy" do
113
127
  action false
114
- expect(@error).to eq 'err'
128
+ expect(wf.error_pool).to eq 'err'
115
129
  expect(@foo).to_not eq 1
116
130
  end
117
131
 
118
132
  it "when actually truthy" do
119
133
  action true
120
- expect(@error).to_not eq 'err'
134
+ expect(wf.error_pool).to_not eq 'err'
121
135
  expect(@foo).to eq 1
122
136
  end
123
137
  end
@@ -128,7 +142,8 @@ describe 'Wf' do
128
142
  it "when actually falsy" do
129
143
  expect(my_proc).to_not receive(:call)
130
144
  action false
131
- end
145
+ expect(wf.error_pool).to eq 'dammed'
146
+ end
132
147
  end
133
148
  end
134
149
 
@@ -141,7 +156,6 @@ describe 'Wf' do
141
156
  .when_truthy { my_proc.call(bool) }
142
157
  .dam { 'err' }
143
158
  .chain { @foo = 1 }
144
- .on_dam { |error_pool| @error = error_pool }
145
159
  end
146
160
 
147
161
  context "main context not dammed" do
@@ -149,13 +163,13 @@ describe 'Wf' do
149
163
 
150
164
  it "when actually falsy" do
151
165
  action false
152
- expect(@error).to_not eq 'err'
166
+ expect(wf.error_pool).to_not eq 'err'
153
167
  expect(@foo).to eq 1
154
168
  end
155
169
 
156
170
  it "when actually truthy" do
157
171
  action true
158
- expect(@error).to eq 'err'
172
+ expect(wf.error_pool).to eq 'err'
159
173
  expect(@foo).to_not eq 1
160
174
  end
161
175
  end
@@ -166,6 +180,7 @@ describe 'Wf' do
166
180
  it "when actually truthy" do
167
181
  expect(my_proc).to_not receive(:call)
168
182
  action true
183
+ expect(wf.error_pool).to eq 'dammed'
169
184
  end
170
185
  end
171
186
  end
@@ -188,10 +203,21 @@ describe 'Wf' do
188
203
  wf
189
204
  .chain { FailingChain.new }
190
205
  .chain { @foo = 1 }
191
- .on_dam { |error_pool| @error = error_pool }
192
206
 
193
207
  expect(@foo).to_not eq 1
194
- expect(@error).to eq FailingChain.error
208
+ expect(wf.error_pool).to eq FailingChain.error
209
+ end
210
+ end
211
+
212
+ describe "undam" do
213
+ it "flow goes back to green path" do
214
+ wf
215
+ .chain { wf.dam('err') }
216
+ .on_dam { wf.undam }
217
+ .chain { @foo = 1 }
218
+ .on_dam { raise('shouldnt happen') }
219
+
220
+ expect(@foo).to eq 1
195
221
  end
196
222
  end
197
223
  end
metadata CHANGED
@@ -1,69 +1,69 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: waterfall
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Roth
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-27 00:00:00.000000000 Z
11
+ date: 2016-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.3'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: pry
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>'
31
+ - - ">"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0.10'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>'
38
+ - - ">"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.10'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: pry-nav
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
@@ -88,13 +88,15 @@ executables: []
88
88
  extensions: []
89
89
  extra_rdoc_files: []
90
90
  files:
91
- - .gitignore
92
- - .rspec
93
- - .travis.yml
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - ".travis.yml"
94
94
  - Gemfile
95
95
  - Gemfile.lock
96
96
  - LICENSE.txt
97
97
  - README.md
98
+ - Rakefile
99
+ - changelog.md
98
100
  - lib/waterfall.rb
99
101
  - lib/waterfall/predicates/base.rb
100
102
  - lib/waterfall/predicates/chain.rb
@@ -115,17 +117,17 @@ require_paths:
115
117
  - lib
116
118
  required_ruby_version: !ruby/object:Gem::Requirement
117
119
  requirements:
118
- - - '>='
120
+ - - ">="
119
121
  - !ruby/object:Gem::Version
120
122
  version: '0'
121
123
  required_rubygems_version: !ruby/object:Gem::Requirement
122
124
  requirements:
123
- - - '>='
125
+ - - ">="
124
126
  - !ruby/object:Gem::Version
125
127
  version: '0'
126
128
  requirements: []
127
129
  rubyforge_project:
128
- rubygems_version: 2.0.3
130
+ rubygems_version: 2.4.5
129
131
  signing_key:
130
132
  specification_version: 4
131
133
  summary: A slice of functional programming to chain ruby services and blocks. Make