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 +4 -4
- data/README.md +124 -1
- data/lib/recoverable/base.rb +15 -7
- data/lib/recoverable/default.rb +13 -0
- data/lib/recoverable/version.rb +1 -1
- data/lib/recoverable.rb +1 -0
- data/spec/mocks/test_errors.rb +3 -0
- data/spec/recoverable_spec.rb +408 -111
- data/spec/spec_helper.rb +1 -3
- metadata +5 -8
- data/spec/mocks/custom_error.rb +0 -1
- data/spec/mocks/foo.rb +0 -10
- data/spec/mocks/unrecovered_error.rb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91d2cd9b2a88fc2cb068caa21ba39b4c6b2d7e15
|
4
|
+
data.tar.gz: 2eadd39119ec415d732ccff3b822e3c95ff96da1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
##
|
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
|
|
data/lib/recoverable/base.rb
CHANGED
@@ -1,27 +1,35 @@
|
|
1
1
|
module Recoverable
|
2
|
-
def recover(method_name, tries: 1, on: StandardError,
|
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,
|
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:,
|
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
|
-
|
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,
|
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
|
24
|
-
|
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?
|
data/lib/recoverable/version.rb
CHANGED
data/lib/recoverable.rb
CHANGED
data/spec/recoverable_spec.rb
CHANGED
@@ -1,178 +1,475 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
RSpec.describe Recoverable do
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
11
|
-
|
11
|
+
def bar; baz; end
|
12
|
+
def baz; end
|
12
13
|
|
13
|
-
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
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
|
-
|
29
|
-
class self::TestClass
|
30
|
-
extend Recoverable
|
31
|
-
recover :bar, tries: 2, on: CustomError
|
36
|
+
end
|
32
37
|
|
33
|
-
|
34
|
-
|
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
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
98
|
-
|
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
|
-
|
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
|
-
|
104
|
-
|
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
|
-
|
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
|
-
|
252
|
+
def method_call
|
253
|
+
"I'm a method call"
|
254
|
+
end
|
110
255
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
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 "
|
119
|
-
class
|
120
|
-
|
121
|
-
|
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
|
-
|
317
|
+
class self::TestChildClass < self::TestParentClass
|
318
|
+
def bar
|
319
|
+
super
|
320
|
+
end
|
128
321
|
|
129
|
-
|
130
|
-
arg
|
322
|
+
def baz; end
|
131
323
|
end
|
132
324
|
|
133
|
-
|
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
|
-
|
348
|
+
def baz; end
|
349
|
+
end
|
136
350
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
-
|
150
|
-
|
375
|
+
class self::TestSubChildClass < self::TestChildClass
|
376
|
+
def bar
|
377
|
+
super
|
378
|
+
end
|
151
379
|
end
|
152
380
|
|
153
|
-
|
381
|
+
let!(:instance) { self.class::TestSubChildClass.new }
|
382
|
+
subject{ instance.bar }
|
154
383
|
|
155
|
-
|
156
|
-
|
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
|
-
|
160
|
-
|
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
|
-
|
165
|
-
|
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
|
-
|
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
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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
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.
|
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-
|
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/
|
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/
|
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
|
data/spec/mocks/custom_error.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
class CustomError < StandardError; end
|
data/spec/mocks/foo.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
class UnrecoveredError < StandardError; end
|