recoverable 0.0.3 → 0.0.4

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: ef2d6b67667197eda7be6937a1852cd3479b8054
4
- data.tar.gz: ba58fff01c7747b79285db1bcf1412470620c2a4
3
+ metadata.gz: 91d2cd9b2a88fc2cb068caa21ba39b4c6b2d7e15
4
+ data.tar.gz: 2eadd39119ec415d732ccff3b822e3c95ff96da1
5
5
  SHA512:
6
- metadata.gz: a739f0dca356eb2bf9cb349b777be6aae48a4432399c9fd1ea3897e168dd1edb97ecdf8a6a662c36d85be388566857f51c1d5a0d3f6b403bad880655a2290115
7
- data.tar.gz: acae59acb3450432c5abd0c5af29fc30c2f4852072f64798a3dcc17c99bfb033f66d4a524686c177f68f3c40ac3d84d3123f2188032183fdd1ee208b2144ec82
6
+ metadata.gz: b1fe4d4f02d158f7e0625d6282f382218b95d35e2ac753f8b7b802838bd5a68e1b023fed31934b28f762d10343ed399cdcd57d8ee6cb853aa022ee8d9f153aee
7
+ data.tar.gz: af71452c0b87834ba674960dbf8dc08e7110b4cb76237973600a31aefc998337f3991d4282ff000d0a0b91df407f6228a515fa79bf9352bdcf3ae591de566745
data/README.md CHANGED
@@ -3,14 +3,137 @@
3
3
  [![Maintainability](https://api.codeclimate.com/v1/badges/dd436c45c8a52dc8c13c/maintainability)](https://codeclimate.com/github/Benjaminpjacobs/recoverable/maintainability)
4
4
  [![Test Coverage](https://api.codeclimate.com/v1/badges/dd436c45c8a52dc8c13c/test_coverage)](https://codeclimate.com/github/Benjaminpjacobs/recoverable/test_coverage)
5
5
 
6
- ## Recoverbale
6
+ ## Recoverable
7
7
 
8
+ Recoverable is a simple DSL that works at the class level to configure retries of instance methods. With a multitude of customizations this gem can combined with ruby's class inheritence can be a powerful tool for drying up code.
8
9
 
9
10
  ## Installation
10
11
 
12
+ Install the gem:
13
+
14
+ ```sh
15
+ $ gem install 'recoverable'
16
+ ```
17
+
18
+ Add it to your gemfile:
19
+
20
+ ```ruby
21
+ gem 'recoverable'
22
+ ```
23
+
24
+ Then run bundle to install the Gem:
25
+
26
+ ```sh
27
+ $ bundle install
28
+ ```
11
29
 
12
30
  ## Usage
13
31
 
32
+ Recoverable gives you a dynamic way to retry and handle errors on an instance of a class or an inherited class.
33
+
34
+ ### Default Behavior
35
+
36
+ You can add recoverable to your class by simply extending the Gem and then telling it which method you would like to recover from and how many times you would like to retry.
37
+
38
+ ```ruby
39
+ class Foo
40
+ extend Recoverable
41
+ recover :bar, tries: 2
42
+
43
+ def bar
44
+ baz
45
+ end
46
+
47
+ end
48
+ ```
49
+ With the above configuration any instance of `Foo` will recover any `StandardError` on `#bar` and retry 2 times without a sleep between retries. After the second retry it will raise the error `Recoverable::RetryCountExceeded` along with the information about what error had occured.
50
+
51
+ ### Configuration Options
52
+
53
+ Recoverable allows for varied configurations to alter the behavior of the rescue and retry.
54
+
55
+ #### Errors
56
+
57
+ Setting up your class with the following will specifically recover on `CustomError`.
58
+
59
+ ```ruby
60
+ class Foo
61
+ extend Recoverable
62
+ recover :bar, tries: 2, on: CustomError
63
+
64
+ def bar
65
+ baz
66
+ end
67
+
68
+ end
69
+ ```
70
+ Note that this configuration will on rescue and retry on CustomError and will not rescue any other error including `StandardError`.
71
+
72
+ Recoverable can rescue on a collection of errors as well, however these must be passed to `on:` as an array.
73
+
74
+ ```ruby
75
+ recover :bar, tries: 2, on: [ CustomError, OtherCustomError ]
76
+ ```
77
+ In the above case both `CustomError` and `OtherCustomError` will be rescued on the `#bar` method.
78
+
79
+ #### Sleep
80
+
81
+ Setting up your class with the following configuration will insert a 3 second sleep command between each retry:
82
+
83
+ ```ruby
84
+ class Foo
85
+ extend Recoverable
86
+ recover :bar, tries: 2, sleep: 3
87
+
88
+ def bar
89
+ baz
90
+ end
91
+
92
+ end
93
+ ```
94
+
95
+ #### Custom Exception
96
+
97
+ In addition to retrying, recoverable allows you to raise a custom exception after the rescue and retry attempts.
98
+
99
+ ```ruby
100
+
101
+ class MyException < StandardError; end
102
+
103
+ class Foo
104
+ extend Recoverable
105
+ recover :bar, tries: 2, custom_exception: MyException
106
+
107
+ def bar
108
+ baz
109
+ end
110
+
111
+ end
112
+ ```
113
+
114
+ In this configuration after bar was retried twice recoverable would not raise `StandardError` or `Recoverable::RetryCountExceeded` but would instead raise `MyException`
115
+
116
+ #### Custom Handler
117
+
118
+ Recoverable also allows you to configure a custom error handling method. This should be a method defined on the class or parent class of the instance.
119
+
120
+ ```ruby
121
+ class Foo
122
+ extend Recoverable
123
+ recover :bar, tries: 2, custom_handler: :handle_error
124
+
125
+ def bar
126
+ baz
127
+ end
128
+
129
+ def handle_error(:error)
130
+ "#{error} was retried twice, raised and then this method was called."
131
+ end
132
+ end
133
+ ```
134
+
135
+ Please note that the name of the handler method should be passed to the configuration as a symbol. Also, the handler method can take either no arguments or a single keyword argument for `error:` if you would like access to the error inside the handler. Any other data inside the handler should be retrieved via instance methods or instance variables.
136
+
14
137
 
15
138
  ## How to contribute
16
139
 
@@ -1,27 +1,35 @@
1
1
  module Recoverable
2
- def recover(method_name, tries: 1, on: StandardError, sleep: nil, custom_handler: nil, custom_exception: RetryCountExceeded)
2
+ def recover(method_name, tries: 1, on: StandardError, wait: nil, wait_method: nil,custom_handler: nil, throw: RetryCountExceeded)
3
3
  recoverable = Array.wrap(on)
4
- proxy = create_proxy(method_name: method_name, tries: tries, recoverable: recoverable, sleep: sleep, custom_handler: custom_handler, custom_exception: custom_exception)
4
+ proxy = create_proxy(method_name: method_name, tries: tries, recoverable: recoverable, wait: wait, wait_method: wait_method, custom_handler: custom_handler, throw: throw)
5
5
  self.prepend proxy
6
6
  end
7
7
 
8
- def create_proxy(method_name:, tries:, recoverable:, sleep:, custom_handler:, custom_exception:)
8
+ def create_proxy(method_name:, tries:, recoverable:, wait:, wait_method:, custom_handler:, throw:)
9
9
  Module.new do
10
10
  define_method(method_name) do |*args|
11
11
  retries = tries
12
12
  begin
13
13
  super *args
14
14
  rescue *recoverable => error
15
- sleep(sleep) if sleep
15
+ self.class.handle_wait(wait, wait_method) if wait
16
16
  retry if (retries -= 1) > 0
17
- self.class.handle_exception(instance: self, custom_handler: custom_handler, args: args, error: error, custom_exception: custom_exception)
17
+ self.class.handle_exception(instance: self, custom_handler: custom_handler, args: args, error: error, throw: throw)
18
18
  end
19
19
  end
20
20
  end
21
21
  end
22
22
 
23
- def handle_exception(instance:, custom_handler:, args:, error:, custom_exception: )
24
- raise custom_exception.new(error) unless custom_handler
23
+ def handle_wait(wait, wait_method)
24
+ if wait_method
25
+ wait_method.call(wait)
26
+ else
27
+ Defaults.wait(wait)
28
+ end
29
+ end
30
+
31
+ def handle_exception(instance:, custom_handler:, args:, error:, throw: )
32
+ raise throw.new(error) unless custom_handler
25
33
  req_params = retrieve_required_parameters(instance, custom_handler)
26
34
 
27
35
  return instance.send(custom_handler) if req_params.empty?
@@ -0,0 +1,13 @@
1
+ module Recoverable
2
+ class Defaults
3
+ @wait_method = Proc.new{ |int| Kernel.sleep int }
4
+ class << self
5
+
6
+ attr_accessor :wait_method
7
+
8
+ def wait(int)
9
+ @wait_method.call(int)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,3 +1,3 @@
1
1
  module Recoverable
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/recoverable.rb CHANGED
@@ -4,6 +4,7 @@ require 'logger'
4
4
 
5
5
  # App
6
6
  require 'recoverable/errors'
7
+ require 'recoverable/default'
7
8
  require 'recoverable/base'
8
9
 
9
10
  # Models
@@ -0,0 +1,3 @@
1
+ class CustomError < StandardError ; end
2
+ class UnrecoveredError < StandardError ; end
3
+ class AlternateCustomError < StandardError ; end
@@ -1,178 +1,475 @@
1
1
  require 'spec_helper'
2
2
  RSpec.describe Recoverable do
3
- let!(:instance) { self.class::TestClass.new }
4
- subject{ instance.bar }
5
- context "passing no errors" do
6
- class self::TestClass
7
- extend Recoverable
8
- recover :bar, tries: 2
3
+ context "Recover Directly on Class" do
4
+ let!(:instance) { self.class::TestClass.new }
5
+ subject{ instance.bar }
6
+ context "With Basic Deafults" do
7
+ class self::TestClass
8
+ extend Recoverable
9
+ recover :bar, tries: 2
9
10
 
10
- def bar; baz; end
11
- def baz; end
11
+ def bar; baz; end
12
+ def baz; end
12
13
 
13
- end
14
+ end
14
15
 
15
- it "defaults to standard error and no sleep" do
16
- expect_any_instance_of(Kernel).to receive(:sleep).never
17
- allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(StandardError)
18
- expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
19
- end
16
+ it "rescues on standard error and does not wait" do
17
+ expect_any_instance_of(Kernel).to receive(:sleep).never
18
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(StandardError)
19
+ expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
20
+ end
20
21
 
21
- it "recovers from inheritors of standard error" do
22
- allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
23
- expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
22
+ it "rescues from inheritors of standard error" do
23
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
24
+ expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
25
+ end
24
26
  end
25
27
 
26
- end
28
+ context "Configured for a specific error to resuce" do
29
+ class self::TestClass
30
+ extend Recoverable
31
+ recover :bar, tries: 2, on: CustomError
32
+
33
+ def bar; baz; end
34
+ def baz; end
27
35
 
28
- context "passing specific errors" do
29
- class self::TestClass
30
- extend Recoverable
31
- recover :bar, tries: 2, on: CustomError
36
+ end
32
37
 
33
- def bar; baz; end
34
- def baz; end
38
+ it "rescues from that error" do
39
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
40
+ expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
41
+ end
35
42
 
36
- end
43
+ it "does not rescue from different raised error" do
44
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(UnrecoveredError)
45
+ expect{ subject }.to raise_error(UnrecoveredError)
46
+ end
37
47
 
38
- it "recovers from specific error" do
39
- allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
40
- expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
48
+ it "does not rescue from standard error" do
49
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(StandardError)
50
+ expect{ subject }.to raise_error(StandardError)
51
+ end
41
52
  end
42
53
 
43
- it "does not recover from different raised error" do
44
- allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(UnrecoveredError)
45
- expect{ subject }.to raise_error(UnrecoveredError)
46
- end
54
+ context "Cofigured to rescue multiple specific errors" do
55
+ class self::TestClass
56
+ extend Recoverable
57
+ recover :bar, tries: 2, on: [CustomError, AlternateCustomError]
58
+
59
+ def bar; baz; end
60
+ def baz; end
47
61
 
48
- it "does not recover from standard error" do
49
- allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(StandardError)
50
- expect{ subject }.to raise_error(StandardError)
62
+ end
63
+
64
+ it "rescues from error A" do
65
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
66
+ expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
67
+ end
68
+
69
+ it "rescues from error B" do
70
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(AlternateCustomError)
71
+ expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
72
+ end
73
+
74
+ it "does not rescue from different raised error" do
75
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(UnrecoveredError)
76
+ expect{ subject }.to raise_error(UnrecoveredError)
77
+ end
78
+
79
+ it "does not rescue from standard error" do
80
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(StandardError)
81
+ expect{ subject }.to raise_error(StandardError)
82
+ end
51
83
  end
52
84
 
53
- end
85
+ context "Configured for custom wait time with defualt" do
86
+ class self::TestClass
87
+ extend Recoverable
88
+ recover :bar, tries: 2, on: CustomError, wait: 3
89
+
90
+ def bar; baz; end
91
+ def baz; end
92
+
93
+ end
54
94
 
55
- context "passing specific sleep" do
56
- class self::TestClass
57
- extend Recoverable
58
- recover :bar, tries: 2, on: CustomError, sleep: 3
95
+ it "sleeps for configured time" do
96
+ expect(Recoverable::Defaults.wait_method).to be_a(Proc)
97
+ expect(Kernel).to receive(:sleep).with(3).exactly(2).times
59
98
 
60
- def bar; baz; end
61
- def baz; end
99
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
62
100
 
101
+ expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
102
+ end
63
103
  end
64
104
 
65
- it "recovers from specific error" do
66
- expect_any_instance_of(Kernel).to receive(:sleep).with(3).exactly(2).times
67
- allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
68
- expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
105
+ context "Configured for custom wait time with new default waiter method" do
106
+ class self::TestClass
107
+ extend Recoverable
108
+ recover :bar, tries: 2, on: CustomError, wait: 3
109
+
110
+ def bar; baz; end
111
+ def baz; end
112
+
113
+ end
114
+ let(:custom_waiter) { Proc.new{|int| p "Called Custom Default with arg: #{int}"} }
115
+
116
+ before(:each) do
117
+ Recoverable::Defaults.wait_method = custom_waiter
118
+ end
119
+
120
+ it "custom waiter method for configured time" do
121
+ expect(Recoverable::Defaults.wait_method).to eq(custom_waiter)
122
+ expect(Recoverable::Defaults).to receive(:wait).with(3).exactly(2).times.and_call_original
123
+
124
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
125
+
126
+ expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
127
+ end
69
128
  end
70
- end
71
129
 
72
- context "passing custom error handler" do
73
- class self::TestClass
74
- extend Recoverable
75
- recover :bar, tries: 2, on: CustomError, custom_handler: :handle_error
130
+ context "Configured for custom wait time with custom waiter method" do
131
+ class self::TestClass
132
+ extend Recoverable
133
+ recover :bar, tries: 2, on: CustomError, wait: 3, wait_method: Proc.new{|int| p "Called Custom Waiter with arg: #{int}"}
134
+
135
+ def bar; baz; end
136
+ def baz; end
76
137
 
77
- def bar; baz; end
78
- def baz; end
79
- def handle_error
80
- "Handled"
81
138
  end
82
139
 
140
+ it "custom waiter method for configured time" do
141
+ expect(Recoverable::Defaults).to receive(:wait).never
142
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
143
+
144
+ expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
145
+ end
83
146
  end
84
147
 
85
- it "recovers from specific error" do
86
- allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
87
- expect{ subject }.to_not raise_error(Recoverable::RetryCountExceeded)
88
- expect(subject).to eq("Handled")
148
+ context "Configured to raise custom exception" do
149
+ class self::TestClass
150
+ class TestCustomExecption < StandardError; end
151
+ extend Recoverable
152
+ recover :bar, tries: 2, on: CustomError, throw: TestCustomExecption
153
+
154
+ def bar; baz; end
155
+ def baz; end
156
+
157
+ end
158
+
159
+ it "rescues error and raises custom exception" do
160
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
161
+ expect{ subject }.to_not raise_error(Recoverable::RetryCountExceeded)
162
+ expect{ subject }.to raise_error(self.class::TestClass::TestCustomExecption)
163
+ end
89
164
  end
90
- end
91
-
92
- context "passing custom error handler with error message" do
93
- class self::TestClass
94
- extend Recoverable
95
- recover :bar, tries: 2, on: CustomError, custom_handler: :handle_error
96
165
 
97
- def bar(arg:nil)
98
- baz
166
+
167
+ context "Configured to use a custom error handling method" do
168
+ class self::TestClass
169
+ extend Recoverable
170
+ recover :bar, tries: 2, on: CustomError, custom_handler: :handle_error
171
+
172
+ def bar; baz; end
173
+ def baz; end
174
+ def handle_error
175
+ "Handled"
176
+ end
177
+
178
+ end
179
+
180
+ it "recovers from configured error by running custom handler method" do
181
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
182
+ expect{ subject }.to_not raise_error(Recoverable::RetryCountExceeded)
183
+ expect(subject).to eq("Handled")
99
184
  end
100
185
 
101
- def baz; end
186
+ context "handler method utilizes returned error" do
187
+ class self::TestClass
188
+ extend Recoverable
189
+ recover :bar, tries: 2, on: CustomError, custom_handler: :handle_error
190
+
191
+ def bar(arg:nil)
192
+ baz
193
+ end
194
+
195
+ def baz; end
102
196
 
103
- def handle_error(error:)
104
- error.message
197
+ def handle_error(error:)
198
+ error.message
199
+ end
200
+ end
201
+
202
+ subject { instance.bar}
203
+
204
+ it "has access to the raised error" do
205
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError.new("Custom Error!"))
206
+ expect{ subject }.to_not raise_error(Recoverable::RetryCountExceeded)
207
+ expect(subject).to eq("Custom Error!")
208
+ end
105
209
  end
106
210
 
107
- end
211
+ context "handler method utilizes args" do
212
+ class self::TestClass
213
+ extend Recoverable
214
+ recover :bar, tries: 2, on: CustomError, custom_handler: :handle_error
215
+
216
+ def bar(arg:nil)
217
+ baz
218
+ end
219
+
220
+ def baz; end
221
+
222
+ def handle_error(error:, arg:)
223
+ arg
224
+ end
225
+
226
+ end
227
+
228
+ subject { instance.bar(arg: "I'm a keyword Arg")}
229
+
230
+ it "has access to the keyword args" do
231
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
232
+ expect{ subject }.to_not raise_error(Recoverable::RetryCountExceeded)
233
+ expect(subject).to eq("I'm a keyword Arg")
234
+ end
235
+ end
236
+
237
+ context "handler method utilizes other private and public instance methods" do
238
+ class self::TestClass
239
+ extend Recoverable
240
+ recover :bar, tries: 2, on: CustomError, custom_handler: :handle_error
241
+
242
+ def bar(arg:nil)
243
+ baz
244
+ end
245
+
246
+ def baz; end
247
+
248
+ def handle_error(error:)
249
+ "#{method_call}, #{private_method_call}"
250
+ end
108
251
 
109
- subject { instance.bar(arg: "I'm a keyword Arg")}
252
+ def method_call
253
+ "I'm a method call"
254
+ end
110
255
 
111
- it "recovers from specific error" do
112
- allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError.new("Custom Error!"))
113
- expect{ subject }.to_not raise_error(Recoverable::RetryCountExceeded)
114
- expect(subject).to eq("Custom Error!")
256
+ private
257
+
258
+ def private_method_call
259
+ "I'm a private method call"
260
+ end
261
+ end
262
+
263
+ subject { instance.bar}
264
+
265
+ it "has access to private and public methods" do
266
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
267
+ expect{ subject }.to_not raise_error(Recoverable::RetryCountExceeded)
268
+ expect(subject).to eq( "I'm a method call, I'm a private method call")
269
+ end
270
+ end
271
+
272
+ context "handler method utilizes instance variables" do
273
+ class self::TestClass
274
+ extend Recoverable
275
+ recover :bar, tries: 2, on: CustomError, custom_handler: :handle_error
276
+ attr_reader :qux
277
+
278
+ def initialize
279
+ @qux = "I'm an instance variable"
280
+ end
281
+
282
+ def bar(arg:nil)
283
+ baz
284
+ end
285
+
286
+ def baz; end
287
+
288
+ def handle_error(error:)
289
+ "#{@qux}"
290
+ end
291
+
292
+ end
293
+
294
+ subject { instance.bar}
295
+
296
+ it "has access to instance variables" do
297
+ allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
298
+ expect{ subject }.to_not raise_error(Recoverable::RetryCountExceeded)
299
+ expect(subject).to eq( "I'm an instance variable")
300
+ end
301
+ end
115
302
  end
303
+
116
304
  end
117
305
 
118
- context "passing custom error handler with arg" do
119
- class self::TestClass
120
- extend Recoverable
121
- recover :bar, tries: 2, on: CustomError, custom_handler: :handle_error
306
+ context "Recover Through Inheritence" do
307
+ context 'Configuring recoverable on the parent class' do
308
+ class self::TestParentClass
309
+ extend Recoverable
310
+ recover :bar, tries: 2
311
+ def bar
312
+ baz
313
+ end
122
314
 
123
- def bar(arg:nil)
124
- baz
125
315
  end
126
316
 
127
- def baz; end
317
+ class self::TestChildClass < self::TestParentClass
318
+ def bar
319
+ super
320
+ end
128
321
 
129
- def handle_error(error:, arg:)
130
- arg
322
+ def baz; end
131
323
  end
132
324
 
133
- end
325
+ let!(:instance) { self.class::TestChildClass.new }
326
+ subject{ instance.bar }
327
+
328
+ it "can recover an error through inheritence chain" do
329
+ allow_any_instance_of(self.class::TestChildClass).to receive(:baz).and_raise(StandardError)
330
+ expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
331
+ end
332
+
333
+ context "recovered method overridden on the child class" do
334
+ class self::TestParentClass
335
+ extend Recoverable
336
+ recover :bar, tries: 2
337
+
338
+ def bar
339
+ baz
340
+ end
341
+ end
342
+
343
+ class self::TestChildClass < self::TestParentClass
344
+ def bar
345
+ baz
346
+ end
134
347
 
135
- subject { instance.bar(arg: "I'm a keyword Arg")}
348
+ def baz; end
349
+ end
136
350
 
137
- it "recovers from specific error" do
138
- allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
139
- expect{ subject }.to_not raise_error(Recoverable::RetryCountExceeded)
140
- expect(subject).to eq("I'm a keyword Arg")
351
+ let!(:instance) { self.class::TestChildClass.new }
352
+ subject{ instance.bar }
353
+
354
+ it "will not recovers through inheritence chain" do
355
+ allow_any_instance_of(self.class::TestChildClass).to receive(:baz).and_raise(StandardError)
356
+ expect{ subject }.to raise_error(StandardError)
357
+ end
358
+ end
141
359
  end
142
- end
143
360
 
144
- context "passing custom error handler with a method call" do
145
- class self::TestClass
146
- extend Recoverable
147
- recover :bar, tries: 2, on: CustomError, custom_handler: :handle_error
361
+ context 'Configuring recoverable on the parent class through multiple levels' do
362
+ class self::TestParentClass
363
+ extend Recoverable
364
+ recover :bar, tries: 2
365
+ def bar
366
+ baz
367
+ end
368
+
369
+ end
370
+
371
+ class self::TestChildClass < self::TestParentClass
372
+ def baz; end
373
+ end
148
374
 
149
- def bar(arg:nil)
150
- baz
375
+ class self::TestSubChildClass < self::TestChildClass
376
+ def bar
377
+ super
378
+ end
151
379
  end
152
380
 
153
- def baz; end
381
+ let!(:instance) { self.class::TestSubChildClass.new }
382
+ subject{ instance.bar }
154
383
 
155
- def handle_error(error:)
156
- "#{method_call}, #{private_method_call}"
384
+ it "can recover through the inheritence chain" do
385
+ allow_any_instance_of(self.class::TestChildClass).to receive(:baz).and_raise(StandardError)
386
+ expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
157
387
  end
388
+ end
158
389
 
159
- def method_call
160
- "I'm a method call"
390
+ context 'Configuring recoverable on child class' do
391
+ class self::TestParentClass
392
+ def bar
393
+ baz
394
+ end
161
395
  end
162
- private
163
396
 
164
- def private_method_call
165
- "I'm a private method call"
397
+ class self::TestChildClass < self::TestParentClass
398
+ extend Recoverable
399
+ recover :bar, tries: 2
400
+ def baz; end
166
401
  end
167
402
 
403
+ let!(:instance) { self.class::TestChildClass.new }
404
+ subject{ instance.bar }
405
+
406
+ it "can recover through inheritence chain" do
407
+ allow_any_instance_of(self.class::TestChildClass).to receive(:baz).and_raise(StandardError)
408
+ expect{ subject }.to raise_error(Recoverable::RetryCountExceeded)
409
+ end
168
410
  end
169
411
 
170
- subject { instance.bar(arg: "I'm a keyword Arg")}
412
+ context "Handler Method" do
413
+ context "Handler method defined on the parent class" do
414
+ class self::TestParentClass
415
+ extend Recoverable
416
+ recover :bar, tries: 2, custom_handler: :handle_error
417
+
418
+ def bar
419
+ baz
420
+ end
421
+
422
+ def handle_error(error:)
423
+ "Parent Handler!"
424
+ end
425
+ end
426
+
427
+ class self::TestChildClass < self::TestParentClass
428
+ def baz; end
429
+ end
430
+
431
+ let!(:instance) { self.class::TestChildClass.new }
432
+ subject{ instance.bar }
433
+
434
+ it "calls handler from the parent class" do
435
+ allow_any_instance_of(self.class::TestChildClass).to receive(:baz).and_raise(StandardError)
436
+ expect{ subject }.to_not raise_error(StandardError)
437
+ expect(subject).to eq("Parent Handler!")
438
+ end
439
+ end
440
+
441
+ context "Handler method defined on the child class" do
442
+ class self::TestParentClass
443
+ extend Recoverable
444
+ recover :bar, tries: 2, custom_handler: :handle_error
445
+
446
+ def bar
447
+ baz
448
+ end
171
449
 
172
- it "recovers from specific error" do
173
- allow_any_instance_of(self.class::TestClass).to receive(:baz).and_raise(CustomError)
174
- expect{ subject }.to_not raise_error(Recoverable::RetryCountExceeded)
175
- expect(subject).to eq( "I'm a method call, I'm a private method call")
450
+ def handle_error(error:)
451
+ "Parent Handler!"
452
+ end
453
+
454
+ end
455
+
456
+ class self::TestChildClass < self::TestParentClass
457
+ def baz; end
458
+
459
+ def handle_error(error:)
460
+ "Child Handler!"
461
+ end
462
+ end
463
+
464
+ let!(:instance) { self.class::TestChildClass.new }
465
+ subject{ instance.bar }
466
+
467
+ it "calls handler from child class" do
468
+ allow_any_instance_of(self.class::TestChildClass).to receive(:baz).and_raise(StandardError)
469
+ expect{ subject }.to_not raise_error(StandardError)
470
+ expect(subject).to eq("Child Handler!")
471
+ end
472
+ end
176
473
  end
177
474
  end
178
475
  end
data/spec/spec_helper.rb CHANGED
@@ -7,9 +7,7 @@ SimpleCov.start do
7
7
  end
8
8
 
9
9
  require 'recoverable'
10
- require 'mocks/foo'
11
- require 'mocks/custom_error'
12
- require 'mocks/unrecovered_error'
10
+ require 'mocks/test_errors'
13
11
  require 'factory_bot'
14
12
 
15
13
  RSpec.configure do |config|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recoverable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Jacobs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-09 00:00:00.000000000 Z
11
+ date: 2018-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: her
@@ -119,11 +119,10 @@ files:
119
119
  - README.md
120
120
  - lib/recoverable.rb
121
121
  - lib/recoverable/base.rb
122
+ - lib/recoverable/default.rb
122
123
  - lib/recoverable/errors.rb
123
124
  - lib/recoverable/version.rb
124
- - spec/mocks/custom_error.rb
125
- - spec/mocks/foo.rb
126
- - spec/mocks/unrecovered_error.rb
125
+ - spec/mocks/test_errors.rb
127
126
  - spec/recoverable_spec.rb
128
127
  - spec/spec_helper.rb
129
128
  homepage:
@@ -151,8 +150,6 @@ signing_key:
151
150
  specification_version: 4
152
151
  summary: Class Level retry DSL for ruby
153
152
  test_files:
154
- - spec/mocks/foo.rb
155
- - spec/mocks/custom_error.rb
156
- - spec/mocks/unrecovered_error.rb
153
+ - spec/mocks/test_errors.rb
157
154
  - spec/spec_helper.rb
158
155
  - spec/recoverable_spec.rb
@@ -1 +0,0 @@
1
- class CustomError < StandardError; end
data/spec/mocks/foo.rb DELETED
@@ -1,10 +0,0 @@
1
- class Foo
2
- extend Recoverable
3
-
4
- def bar
5
- baz
6
- end
7
-
8
- def baz
9
- end
10
- end
@@ -1 +0,0 @@
1
- class UnrecoveredError < StandardError; end