take2 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +1 -1
- data/.hound.yml +3 -0
- data/.rubocop.yml +1201 -0
- data/Gemfile +4 -1
- data/Gemfile.lock +20 -2
- data/lib/take2.rb +55 -30
- data/lib/take2/backoff.rb +48 -0
- data/lib/take2/configuration.rb +44 -19
- data/lib/take2/version.rb +4 -2
- data/spec/spec_helper.rb +3 -1
- data/spec/take2/configuration_spec.rb +41 -51
- data/spec/take2_spec.rb +116 -113
- data/take2.gemspec +8 -6
- metadata +5 -2
data/spec/take2_spec.rb
CHANGED
@@ -1,162 +1,147 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'spec_helper'
|
4
4
|
|
5
|
+
RSpec.describe(Take2) do
|
5
6
|
let!(:config) do
|
6
7
|
Take2.configure do |c|
|
7
8
|
c.retries = 1
|
8
9
|
c.retriable = [Net::HTTPServerException, Net::HTTPRetriableError].freeze
|
9
|
-
c.retry_condition_proc = proc {false}
|
10
|
+
c.retry_condition_proc = proc { false }
|
10
11
|
c.retry_proc = proc {}
|
11
|
-
c.time_to_sleep = 0
|
12
|
+
c.time_to_sleep = 0
|
13
|
+
c.backoff_intervals = Take2::Backoff.new(:linear, 1).intervals
|
12
14
|
end
|
13
15
|
end
|
14
16
|
let(:klass) { Class.new { include Take2 } }
|
15
17
|
let(:object) { klass.new }
|
16
18
|
|
17
19
|
describe 'default values' do
|
18
|
-
|
19
20
|
subject { klass.retriable_configuration }
|
20
21
|
|
21
22
|
it 'has a default value for :retries' do
|
22
|
-
expect(subject[:retries]).to
|
23
|
+
expect(subject[:retries]).to(eql(described_class.configuration[:retries]))
|
23
24
|
end
|
24
25
|
|
25
26
|
it 'has a default value for :retriable' do
|
26
|
-
expect(subject[:retriable]).to
|
27
|
+
expect(subject[:retriable]).to(eql(described_class.configuration[:retriable]))
|
27
28
|
end
|
28
29
|
|
29
30
|
it 'has a default value for :retry_condition_proc' do
|
30
|
-
expect(subject[:retry_condition_proc].call).to
|
31
|
+
expect(subject[:retry_condition_proc].call).to(eql(described_class.configuration[:retry_condition_proc].call))
|
31
32
|
end
|
32
33
|
|
33
34
|
it 'has a default value for :retry_proc' do
|
34
|
-
expect(subject[:retry_proc].call).to
|
35
|
+
expect(subject[:retry_proc].call).to(eql(described_class.configuration[:retry_proc].call))
|
35
36
|
end
|
36
37
|
|
37
38
|
it 'has a default value for :time_to_sleep' do
|
38
|
-
expect(subject[:time_to_sleep]).to
|
39
|
+
expect(subject[:time_to_sleep]).to(eql(described_class.configuration[:time_to_sleep]))
|
39
40
|
end
|
40
41
|
|
42
|
+
it 'has a default value for :backoff_intervals' do
|
43
|
+
expect(subject[:backoff_intervals]).to eql((1..10).to_a)
|
44
|
+
end
|
41
45
|
end
|
42
46
|
|
43
47
|
describe 'included class helpers' do
|
44
|
-
|
45
48
|
subject { klass.retriable_configuration }
|
46
49
|
|
47
50
|
describe '.number_of_retries' do
|
48
|
-
|
49
51
|
context 'with valid argument' do
|
50
|
-
|
51
52
|
it 'sets the :retries attribute' do
|
52
|
-
klass.number_of_retries
|
53
|
-
expect(subject[:retries]).to
|
53
|
+
klass.number_of_retries(1)
|
54
|
+
expect(subject[:retries]).to(eql(1))
|
54
55
|
end
|
55
|
-
|
56
56
|
end
|
57
57
|
|
58
58
|
context 'with invalid argument' do
|
59
|
-
|
60
59
|
it 'raises ArgumentError' do
|
61
|
-
expect { klass.number_of_retries
|
60
|
+
expect { klass.number_of_retries(0) }.to(raise_error(ArgumentError))
|
62
61
|
end
|
63
|
-
|
64
62
|
end
|
65
|
-
|
66
63
|
end
|
67
64
|
|
68
65
|
describe '.retriable_errors' do
|
69
|
-
|
70
66
|
context 'with valid argument' do
|
71
|
-
|
72
67
|
it 'sets the :retriable_errors attribute' do
|
73
68
|
retriables = IOError
|
74
|
-
klass.retriable_errors
|
75
|
-
expect(subject[:retriable]).to
|
69
|
+
klass.retriable_errors(retriables)
|
70
|
+
expect(subject[:retriable]).to(eql([retriables]))
|
76
71
|
end
|
77
|
-
|
78
72
|
end
|
79
73
|
|
80
74
|
context 'with invalid argument' do
|
81
|
-
|
82
75
|
it 'raises ArgumentError' do
|
83
76
|
class Klass; end
|
84
|
-
expect { klass.retriable_errors
|
77
|
+
expect { klass.retriable_errors(Klass) }.to(raise_error(ArgumentError))
|
85
78
|
end
|
86
|
-
|
87
79
|
end
|
88
|
-
|
89
80
|
end
|
90
81
|
|
91
82
|
describe '.retriable_condition' do
|
92
|
-
|
93
83
|
context 'with valid argument' do
|
94
|
-
|
95
84
|
it 'sets the :retriable_condition attribute' do
|
96
85
|
retriable_proc = proc { 'Ho-Ho-Ho' }
|
97
|
-
klass.retriable_condition
|
98
|
-
expect(subject[:retry_condition_proc].call).to
|
86
|
+
klass.retriable_condition(retriable_proc)
|
87
|
+
expect(subject[:retry_condition_proc].call).to(eql(retriable_proc.call))
|
99
88
|
end
|
100
|
-
|
101
89
|
end
|
102
90
|
|
103
91
|
context 'with invalid argument' do
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
end
|
108
|
-
|
92
|
+
it 'raises ArgumentError' do
|
93
|
+
expect { klass.retriable_condition(Class.new) }.to(raise_error(ArgumentError))
|
94
|
+
end
|
109
95
|
end
|
110
|
-
|
111
96
|
end
|
112
97
|
|
113
98
|
describe '.on_retry' do
|
114
|
-
|
115
99
|
context 'with valid argument' do
|
116
|
-
|
117
100
|
it 'sets the :on_retry attribute' do
|
118
101
|
retry_proc = proc { |el| el }
|
119
|
-
klass.on_retry
|
120
|
-
expect(subject[:retry_proc].call).to
|
102
|
+
klass.on_retry(retry_proc)
|
103
|
+
expect(subject[:retry_proc].call).to(eql(retry_proc.call))
|
121
104
|
end
|
122
|
-
|
123
|
-
end
|
105
|
+
end
|
124
106
|
|
125
107
|
context 'with invalid argument' do
|
126
|
-
|
127
108
|
it 'raises ArgumentError' do
|
128
|
-
expect { klass.on_retry
|
109
|
+
expect { klass.on_retry(Class.new) }.to(raise_error(ArgumentError))
|
129
110
|
end
|
130
|
-
|
131
111
|
end
|
132
|
-
|
133
112
|
end
|
134
113
|
|
135
114
|
describe '.sleep_before_retry' do
|
136
|
-
|
137
115
|
context 'with valid argument' do
|
138
|
-
|
139
116
|
it 'sets the :sleep_before_retry attribute' do
|
140
|
-
klass.sleep_before_retry
|
141
|
-
expect(subject[:time_to_sleep]).to
|
117
|
+
klass.sleep_before_retry(3.5)
|
118
|
+
expect(subject[:time_to_sleep]).to(eql(3.5))
|
142
119
|
end
|
143
|
-
|
144
120
|
end
|
145
|
-
|
146
|
-
context 'with invalid argument' do
|
147
121
|
|
122
|
+
context 'with invalid argument' do
|
148
123
|
it 'raises ArgumentError' do
|
149
|
-
expect { klass.sleep_before_retry
|
124
|
+
expect { klass.sleep_before_retry(-1) }.to(raise_error(ArgumentError))
|
150
125
|
end
|
151
|
-
|
152
126
|
end
|
153
|
-
|
154
127
|
end
|
155
128
|
|
129
|
+
describe '.backoff_setup' do
|
130
|
+
context 'with valid arguments' do
|
131
|
+
it 'sets the backoff_intervals' do
|
132
|
+
klass.backoff_strategy(type: :fibonacci, start: 3)
|
133
|
+
expect(subject[:backoff_intervals]).to eql([3, 5, 8, 13, 21, 34, 55, 89, 144, 233])
|
134
|
+
end
|
135
|
+
end
|
136
|
+
context 'with invalid backoff type' do
|
137
|
+
it 'raises ArgumentError' do
|
138
|
+
expect { klass.backoff_strategy(type: :not_real, start: 3) }.to(raise_error(ArgumentError))
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
156
142
|
end
|
157
143
|
|
158
144
|
describe '.call_api_with_retry' do
|
159
|
-
|
160
145
|
def increment_retry_counter
|
161
146
|
@tries += 1
|
162
147
|
end
|
@@ -167,114 +152,132 @@ RSpec.describe Take2 do
|
|
167
152
|
end
|
168
153
|
|
169
154
|
context 'when raised with non retriable error' do
|
170
|
-
|
171
|
-
let(:error) { StandardError.new 'Release the Kraken!!' }
|
155
|
+
let(:error) { StandardError.new('Release the Kraken!!') }
|
172
156
|
|
173
157
|
before(:each) { @tries = 0 }
|
174
158
|
|
175
159
|
it 're raises the original error' do
|
176
160
|
expect do
|
177
161
|
object.call_api_with_retry { wrath_the_gods_with error }
|
178
|
-
end.to
|
162
|
+
end.to(raise_error(error.class))
|
179
163
|
end
|
180
164
|
|
181
165
|
it 'is not retried' do
|
182
166
|
expect do
|
183
|
-
object.call_api_with_retry { wrath_the_gods_with error }
|
184
|
-
end.to
|
167
|
+
object.call_api_with_retry { wrath_the_gods_with error }
|
168
|
+
end.to(change { @tries }.from(0).to(1))
|
169
|
+
rescue
|
170
|
+
nil
|
185
171
|
end
|
186
|
-
|
187
|
-
# it 'logs the error' do
|
188
|
-
# expect(object).to receive(:log_error).with(error)
|
189
|
-
# object.call_api_with_retry { wrath_the_gods_with error } rescue nil
|
190
|
-
# end
|
191
|
-
|
192
172
|
end
|
193
173
|
|
194
174
|
context 'when raised with retriable error' do
|
195
|
-
|
196
|
-
let(:retriable_error) { Net::HTTPRetriableError.new 'Release the Kraken...many times!!', nil }
|
175
|
+
let(:retriable_error) { Net::HTTPRetriableError.new('Release the Kraken...many times!!', nil) }
|
197
176
|
|
198
177
|
before(:each) { @tries = 0 }
|
199
178
|
|
200
179
|
it 'retries correct number of times' do
|
201
180
|
expect do
|
202
|
-
object.call_api_with_retry { wrath_the_gods_with retriable_error }
|
203
|
-
end.to
|
181
|
+
object.call_api_with_retry { wrath_the_gods_with retriable_error }
|
182
|
+
end.to(change { @tries }.from(0).to(klass.retriable_configuration[:retries] + 1))
|
183
|
+
rescue
|
184
|
+
nil
|
204
185
|
end
|
205
186
|
|
206
187
|
it 'calls the retry proc' do
|
207
|
-
expect(klass.retriable_configuration[:retry_proc])
|
208
|
-
|
188
|
+
expect(klass.retriable_configuration[:retry_proc])
|
189
|
+
.to(receive(:call)
|
190
|
+
.exactly(klass.retriable_configuration[:retries]))
|
191
|
+
begin
|
192
|
+
object.call_api_with_retry { wrath_the_gods_with retriable_error }
|
193
|
+
rescue
|
194
|
+
nil
|
195
|
+
end
|
209
196
|
end
|
210
197
|
|
211
198
|
it 'calls the retry_condition proc' do
|
212
|
-
expect(klass.retriable_configuration[:retry_condition_proc])
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
199
|
+
expect(klass.retriable_configuration[:retry_condition_proc])
|
200
|
+
.to(receive(:call)
|
201
|
+
.exactly(klass.retriable_configuration[:retries]))
|
202
|
+
begin
|
203
|
+
object.call_api_with_retry { wrath_the_gods_with retriable_error }
|
204
|
+
rescue
|
205
|
+
nil
|
206
|
+
end
|
219
207
|
end
|
220
208
|
|
221
|
-
# it '
|
222
|
-
#
|
223
|
-
#
|
209
|
+
# it 'sleeps the correct amount of time' do
|
210
|
+
# allow_any_instance_of(Object).to(receive(:sleep).with(klass.retriable_configuration[:time_to_sleep]))
|
211
|
+
# begin
|
212
|
+
# object.call_api_with_retry { wrath_the_gods_with retriable_error }
|
213
|
+
# rescue
|
214
|
+
# nil
|
215
|
+
# end
|
224
216
|
# end
|
225
217
|
|
226
218
|
it 're raises the original error' do
|
227
219
|
expect do
|
228
220
|
object.call_api_with_retry { wrath_the_gods_with retriable_error }
|
229
|
-
end.to
|
221
|
+
end.to(raise_error(retriable_error.class))
|
230
222
|
end
|
231
|
-
|
232
223
|
end
|
233
224
|
|
234
225
|
context 'with custom options' do
|
235
|
-
|
236
|
-
let(:
|
237
|
-
let(:new_retriable_error) { IOError.new 'You shall not PASS!' }
|
226
|
+
let(:retriable_error) { Net::HTTPRetriableError.new('Release the Kraken...many times!!', nil) }
|
227
|
+
let(:new_retriable_error) { IOError.new('You shall not PASS!') }
|
238
228
|
|
239
229
|
before(:each) { @tries = 0 }
|
240
|
-
|
230
|
+
|
241
231
|
it 'overwrites the :retries' do
|
242
232
|
expect do
|
243
|
-
object.call_api_with_retry(retries: 3) { wrath_the_gods_with retriable_error }
|
244
|
-
end.to
|
233
|
+
object.call_api_with_retry(retries: 3) { wrath_the_gods_with retriable_error }
|
234
|
+
end.to(change { @tries }.from(0).to(4))
|
235
|
+
rescue
|
236
|
+
nil
|
245
237
|
end
|
246
238
|
|
247
239
|
it 'overwrites the :retry_proc' do
|
248
240
|
new_proc = proc { 1**1 }
|
249
|
-
expect(new_proc).to
|
250
|
-
|
241
|
+
expect(new_proc).to(receive(:call).exactly(klass.retriable_configuration[:retries]))
|
242
|
+
begin
|
243
|
+
object.call_api_with_retry(retry_proc: new_proc) { wrath_the_gods_with retriable_error }
|
244
|
+
rescue
|
245
|
+
nil
|
246
|
+
end
|
251
247
|
end
|
252
248
|
|
253
249
|
it 'overwrites the :retry_condition_proc' do
|
254
250
|
new_proc = proc { true }
|
255
|
-
expect(new_proc).to
|
256
|
-
|
251
|
+
expect(new_proc).to(receive(:call).exactly(klass.retriable_configuration[:retries]))
|
252
|
+
begin
|
253
|
+
object.call_api_with_retry(retry_condition_proc: new_proc) { wrath_the_gods_with retriable_error }
|
254
|
+
rescue
|
255
|
+
nil
|
256
|
+
end
|
257
257
|
end
|
258
258
|
|
259
|
-
it 'overwrites the :time_to_sleep' do
|
260
|
-
|
261
|
-
|
262
|
-
|
259
|
+
# it 'overwrites the :time_to_sleep' do
|
260
|
+
# allow_any_instance_of(Object).to(receive(:sleep).with(1.66))
|
261
|
+
# begin
|
262
|
+
# object.call_api_with_retry(time_to_sleep: 1.66) { wrath_the_gods_with retriable_error }
|
263
|
+
# rescue
|
264
|
+
# nil
|
265
|
+
# end
|
266
|
+
# end
|
263
267
|
|
264
268
|
it 'overwrites the :retriable' do
|
265
|
-
expect do
|
266
|
-
object.call_api_with_retry(retriable: [new_retriable_error]) { wrath_the_gods_with retriable_error }
|
267
|
-
end.to
|
269
|
+
expect do
|
270
|
+
object.call_api_with_retry(retriable: [new_retriable_error]) { wrath_the_gods_with retriable_error }
|
271
|
+
end.to(change { @tries }.from(0).to(1))
|
272
|
+
rescue
|
273
|
+
nil
|
268
274
|
end
|
269
275
|
|
270
276
|
it 'raises ArgumentError if there are invalid keys' do
|
271
|
-
expect do
|
277
|
+
expect do
|
272
278
|
object.call_api_with_retry(invalid_key: :nope) { wrath_the_gods_with retriable_error }
|
273
|
-
end.to
|
279
|
+
end.to(raise_error(ArgumentError))
|
274
280
|
end
|
275
|
-
|
276
281
|
end
|
277
|
-
|
278
282
|
end
|
279
|
-
|
280
|
-
end
|
283
|
+
end
|
data/take2.gemspec
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require File.expand_path("../lib/take2/version", __FILE__)
|
2
4
|
|
3
5
|
Gem::Specification.new do |s|
|
4
6
|
s.name = "take2"
|
5
7
|
s.version = Take2::VERSION
|
6
8
|
s.platform = Gem::Platform::RUBY
|
7
|
-
s.licenses =
|
9
|
+
s.licenses = %w(MIT)
|
8
10
|
s.authors = ["Anton Magids"]
|
9
11
|
s.email = ["evnomadx@gmail.com"]
|
10
12
|
s.homepage = "https://github.com/restaurant-cheetah/take2"
|
@@ -12,10 +14,10 @@ Gem::Specification.new do |s|
|
|
12
14
|
s.description = "Retry API calls, methods or blocks of code. Define take2 retry behavior or use defaults and you good to go."
|
13
15
|
s.post_install_message = "Getting Take2 is dead easy!"
|
14
16
|
|
15
|
-
all_files =
|
16
|
-
test_files =
|
17
|
-
|
17
|
+
all_files = %x(git ls-files).split("\n")
|
18
|
+
test_files = %x(git ls-files -- {test,spec,features}/*).split("\n")
|
19
|
+
|
18
20
|
s.files = all_files - test_files
|
19
21
|
s.test_files = test_files
|
20
|
-
s.require_paths =
|
21
|
-
end
|
22
|
+
s.require_paths = %w(lib)
|
23
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: take2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anton Magids
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Retry API calls, methods or blocks of code. Define take2 retry behavior
|
14
14
|
or use defaults and you good to go.
|
@@ -22,6 +22,8 @@ files:
|
|
22
22
|
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
23
23
|
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
24
24
|
- ".gitignore"
|
25
|
+
- ".hound.yml"
|
26
|
+
- ".rubocop.yml"
|
25
27
|
- CHANGELOG.md
|
26
28
|
- Gemfile
|
27
29
|
- Gemfile.lock
|
@@ -29,6 +31,7 @@ files:
|
|
29
31
|
- README.md
|
30
32
|
- Rakefile
|
31
33
|
- lib/take2.rb
|
34
|
+
- lib/take2/backoff.rb
|
32
35
|
- lib/take2/configuration.rb
|
33
36
|
- lib/take2/version.rb
|
34
37
|
- spec/spec_helper.rb
|