retriable 3.8.0 → 4.2.0

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.
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rbconfig"
4
- require "stringio"
5
4
 
6
5
  describe Retriable do
7
6
  let(:time_table_handler) do
@@ -39,6 +38,49 @@ describe Retriable do
39
38
  expect { retriable { increment_tries_with_exception } }.to raise_error(StandardError)
40
39
  expect(@tries).to eq(3)
41
40
  end
41
+
42
+ it "passes on_give_up through the kernel extension" do
43
+ require_relative "../lib/retriable/core_ext/kernel"
44
+ received_reason = nil
45
+ handler = proc { |_e, _try, _elapsed, _interval, reason| received_reason = reason }
46
+
47
+ expect { retriable(tries: 1, on_give_up: handler) { increment_tries_with_exception } }
48
+ .to raise_error(StandardError)
49
+
50
+ expect(received_reason).to eq(:tries_exhausted)
51
+ end
52
+
53
+ # These two specs lock in the anonymous block forwarding (`&`) semantics
54
+ # across both delegation layers: Kernel#retriable_with_context ->
55
+ # Retriable.with_context. If the `&` is dropped at either layer, the block
56
+ # is not forwarded and the `block_given?` guard in with_context raises
57
+ # ArgumentError instead of running the block.
58
+ it "forwards a block through Kernel#retriable_with_context" do
59
+ require_relative "../lib/retriable/core_ext/kernel"
60
+ Retriable.configure { |c| c.contexts[:sql] = { tries: 1 } }
61
+
62
+ retriable_with_context(:sql) { increment_tries }
63
+
64
+ expect(@tries).to eq(1)
65
+ end
66
+
67
+ it "raises an ArgumentError when Kernel#retriable_with_context is called without a block" do
68
+ require_relative "../lib/retriable/core_ext/kernel"
69
+ Retriable.configure { |c| c.contexts[:sql] = { tries: 1 } }
70
+
71
+ expect { retriable_with_context(:sql) }
72
+ .to raise_error(ArgumentError, /with_context requires a block/)
73
+ expect(@tries).to eq(0)
74
+ end
75
+
76
+ it "is not callable with an explicit receiver" do
77
+ require_relative "../lib/retriable/core_ext/kernel"
78
+
79
+ expect { "foo".retriable { increment_tries } }
80
+ .to raise_error(NoMethodError, /private method/)
81
+ expect { "foo".retriable_with_context(:sql) { increment_tries } }
82
+ .to raise_error(NoMethodError, /private method/)
83
+ end
42
84
  end
43
85
 
44
86
  context "#retriable" do
@@ -51,9 +93,6 @@ describe Retriable do
51
93
 
52
94
  it "raises a LocalJumpError if not given a block" do
53
95
  expect { described_class.retriable }.to raise_error(LocalJumpError)
54
- expect do
55
- expect { described_class.retriable(timeout: 2) }.to raise_error(LocalJumpError)
56
- end.to output(/timeout.*deprecated.*Retriable 4\.0/i).to_stderr
57
96
  end
58
97
 
59
98
  it "stops at first try if the block does not raise an exception" do
@@ -171,86 +210,8 @@ describe Retriable do
171
210
  end.to raise_error(ArgumentError, /tries/)
172
211
  end
173
212
 
174
- it "will timeout after 1 second" do
175
- expect do
176
- expect { described_class.retriable(timeout: 1) { sleep(1.1) } }.to raise_error(Timeout::Error)
177
- end.to output(/timeout.*deprecated.*Retriable 4\.0/i).to_stderr
178
- end
179
-
180
- context "timeout: deprecation" do
181
- it "warns at most once per process across repeated retriable calls" do
182
- original_stderr = $stderr
183
- stderr = StringIO.new
184
- begin
185
- $stderr = stderr
186
-
187
- described_class.retriable(timeout: 5) { :noop }
188
- described_class.retriable(timeout: 5) { :noop }
189
- described_class.retriable(timeout: 5) { :noop }
190
-
191
- expect(stderr.string.scan("timeout:` option is deprecated").size).to eq(1)
192
- ensure
193
- $stderr = original_stderr
194
- end
195
- end
196
-
197
- it "warns when timeout is passed to retriable" do
198
- expect do
199
- described_class.retriable(timeout: 5) { :noop }
200
- end.to output(/timeout.*deprecated.*Retriable 4\.0/i).to_stderr
201
- end
202
-
203
- it "keeps applying timeout while deprecated" do
204
- original_stderr = $stderr
205
- begin
206
- $stderr = StringIO.new
207
- expect do
208
- described_class.retriable(timeout: 0.05, tries: 1) { sleep(0.5) }
209
- end.to raise_error(Timeout::Error)
210
- ensure
211
- $stderr = original_stderr
212
- end
213
- end
214
-
215
- it "warns when timeout is supplied through with_override" do
216
- expect do
217
- described_class.with_override(timeout: 5) do
218
- described_class.retriable { :noop }
219
- end
220
- end.to output(/timeout.*deprecated.*Retriable 4\.0/i).to_stderr
221
- end
222
-
223
- it "warns when timeout is supplied through configure" do
224
- original_config = described_class.config
225
- begin
226
- expect do
227
- described_class.configure { |config| config.timeout = 5 }
228
- described_class.retriable { :noop }
229
- end.to output(/timeout.*deprecated.*Retriable 4\.0/i).to_stderr
230
- ensure
231
- described_class.configure do |config|
232
- original_config.to_h.each { |key, value| config.public_send("#{key}=", value) }
233
- end
234
- end
235
- end
236
-
237
- it "is silenced by Warning[:deprecated] = false", if: WARN_CATEGORY_SUPPORTED do
238
- original = Warning[:deprecated]
239
- begin
240
- Warning[:deprecated] = false
241
- expect do
242
- described_class.retriable(timeout: 5) { :noop }
243
- end.not_to output.to_stderr
244
- ensure
245
- Warning[:deprecated] = original
246
- end
247
- end
248
-
249
- it "does not warn when timeout is absent" do
250
- expect do
251
- described_class.retriable { :noop }
252
- end.not_to output.to_stderr
253
- end
213
+ it "rejects timeout as an unknown option" do
214
+ expect { described_class.retriable(timeout: 1) { :noop } }.to raise_error(ArgumentError, /not a valid option/)
254
215
  end
255
216
 
256
217
  it "applies a randomized exponential backoff to each try" do
@@ -296,6 +257,187 @@ describe Retriable do
296
257
  end
297
258
  end
298
259
 
260
+ it "does not call on_retry when explicitly set to nil" do
261
+ callback_called = false
262
+ original_on_retry = described_class.config.on_retry
263
+
264
+ begin
265
+ described_class.configure do |c|
266
+ c.on_retry = proc { |_exception, _try, _elapsed_time, _next_interval| callback_called = true }
267
+ end
268
+
269
+ expect do
270
+ described_class.retriable(on_retry: nil, tries: 3) { increment_tries_with_exception }
271
+ end.to raise_error(StandardError)
272
+
273
+ expect(@tries).to eq(3)
274
+ expect(callback_called).to be(false)
275
+ ensure
276
+ described_class.configure do |c|
277
+ c.on_retry = original_on_retry
278
+ end
279
+ end
280
+ end
281
+
282
+ it "calls on_give_up with max elapsed time details before re-raising" do
283
+ described_class.configure { |c| c.sleep_disabled = false }
284
+ give_up_calls = []
285
+ on_give_up = proc do |exception, try, elapsed_time, next_interval, reason|
286
+ give_up_calls << [exception, try, elapsed_time, next_interval, reason]
287
+ end
288
+
289
+ expect do
290
+ described_class.retriable(
291
+ intervals: [1.0, 1.0],
292
+ max_elapsed_time: 0.5,
293
+ on_give_up: on_give_up,
294
+ ) do
295
+ increment_tries_with_exception
296
+ end
297
+ end.to raise_error(StandardError)
298
+
299
+ exception, try, elapsed_time, next_interval, reason = give_up_calls.fetch(0)
300
+ expect(give_up_calls.size).to eq(1)
301
+ expect(exception).to be_a(StandardError)
302
+ expect(exception.message).to eq("StandardError occurred")
303
+ expect(try).to eq(1)
304
+ expect(elapsed_time).to be >= 0
305
+ expect(next_interval).to eq(1.0)
306
+ expect(reason).to eq(:max_elapsed_time)
307
+ expect(@tries).to eq(1)
308
+ end
309
+
310
+ it "calls on_give_up with tries exhausted details before re-raising" do
311
+ give_up_calls = []
312
+ on_give_up = proc do |exception, try, elapsed_time, next_interval, reason|
313
+ give_up_calls << [exception, try, elapsed_time, next_interval, reason]
314
+ end
315
+
316
+ expect do
317
+ described_class.retriable(tries: 2, on_give_up: on_give_up) { increment_tries_with_exception }
318
+ end.to raise_error(StandardError)
319
+
320
+ exception, try, elapsed_time, next_interval, reason = give_up_calls.fetch(0)
321
+ expect(give_up_calls.size).to eq(1)
322
+ expect(exception).to be_a(StandardError)
323
+ expect(exception.message).to eq("StandardError occurred")
324
+ expect(try).to eq(2)
325
+ expect(elapsed_time).to be >= 0
326
+ expect(next_interval).to be_nil
327
+ expect(reason).to eq(:tries_exhausted)
328
+ expect(@tries).to eq(2)
329
+ end
330
+
331
+ it "does not call on_give_up when the block eventually succeeds" do
332
+ callback_called = false
333
+
334
+ described_class.retriable(tries: 3, on_give_up: proc { callback_called = true }) do
335
+ increment_tries
336
+ raise StandardError if @tries < 2
337
+ end
338
+
339
+ expect(callback_called).to be(false)
340
+ expect(@tries).to eq(2)
341
+ end
342
+
343
+ it "does not call on_give_up for non-retriable exception types" do
344
+ callback_called = false
345
+
346
+ expect do
347
+ described_class.retriable(on_give_up: proc { callback_called = true }) do
348
+ increment_tries_with_exception(NonStandardError)
349
+ end
350
+ end.to raise_error(NonStandardError)
351
+
352
+ expect(callback_called).to be(false)
353
+ expect(@tries).to eq(1)
354
+ end
355
+
356
+ it "does not call on_give_up when retry_if rejects the exception" do
357
+ callback_called = false
358
+
359
+ expect do
360
+ described_class.retriable(
361
+ tries: 3,
362
+ retry_if: ->(_exception) { false },
363
+ on_give_up: proc { callback_called = true },
364
+ ) do
365
+ increment_tries_with_exception
366
+ end
367
+ end.to raise_error(StandardError)
368
+
369
+ expect(callback_called).to be(false)
370
+ expect(@tries).to eq(1)
371
+ end
372
+
373
+ it "does not call on_give_up when explicitly set to false" do
374
+ callback_called = false
375
+ original_on_give_up = described_class.config.on_give_up
376
+
377
+ begin
378
+ described_class.configure do |c|
379
+ c.on_give_up = proc { callback_called = true }
380
+ end
381
+
382
+ expect do
383
+ described_class.retriable(on_give_up: false, tries: 1) { increment_tries_with_exception }
384
+ end.to raise_error(StandardError)
385
+
386
+ expect(callback_called).to be(false)
387
+ ensure
388
+ described_class.configure do |c|
389
+ c.on_give_up = original_on_give_up
390
+ end
391
+ end
392
+ end
393
+
394
+ it "does not call on_give_up when explicitly set to nil" do
395
+ callback_called = false
396
+ original_on_give_up = described_class.config.on_give_up
397
+
398
+ begin
399
+ described_class.configure do |c|
400
+ c.on_give_up = proc { callback_called = true }
401
+ end
402
+
403
+ expect do
404
+ described_class.retriable(on_give_up: nil, tries: 1) { increment_tries_with_exception }
405
+ end.to raise_error(StandardError)
406
+
407
+ expect(callback_called).to be(false)
408
+ ensure
409
+ described_class.configure do |c|
410
+ c.on_give_up = original_on_give_up
411
+ end
412
+ end
413
+ end
414
+
415
+ it "calls on_retry before on_give_up when giving up" do
416
+ events = []
417
+
418
+ expect do
419
+ described_class.retriable(
420
+ tries: 1,
421
+ on_retry: proc { events << :on_retry },
422
+ on_give_up: proc { events << :on_give_up },
423
+ ) do
424
+ increment_tries_with_exception
425
+ end
426
+ end.to raise_error(StandardError)
427
+
428
+ expect(events).to eq(%i[on_retry on_give_up])
429
+ end
430
+
431
+ it "propagates exceptions raised inside on_give_up, replacing the original exception" do
432
+ handler = proc { raise "handler exploded" }
433
+
434
+ expect do
435
+ described_class.retriable(tries: 1, on_give_up: handler) { increment_tries_with_exception }
436
+ end.to raise_error(RuntimeError, "handler exploded")
437
+
438
+ expect(@tries).to eq(1)
439
+ end
440
+
299
441
  context "with rand_factor 0.0 and an on_retry handler" do
300
442
  let(:tries) { 6 }
301
443
  let(:no_rand_timetable) { { 1 => 0.5, 2 => 0.75, 3 => 1.125 } }
@@ -375,6 +517,19 @@ describe Retriable do
375
517
  end
376
518
  end
377
519
 
520
+ context "with a Set :on parameter" do
521
+ it "retries each exception class in the Set" do
522
+ described_class.retriable(on: Set[StandardError, NonStandardError]) do
523
+ increment_tries
524
+
525
+ raise StandardError if @tries == 1
526
+ raise NonStandardError if @tries == 2
527
+ end
528
+
529
+ expect(@tries).to eq(3)
530
+ end
531
+ end
532
+
378
533
  context "with a hash :on parameter" do
379
534
  let(:on_hash) { { NonStandardError => /NonStandardError occurred/ } }
380
535
 
@@ -407,6 +562,20 @@ describe Retriable do
407
562
  expect(@tries).to eq(1)
408
563
  end
409
564
 
565
+ it "does not call on_give_up when exception class matches but message does not" do
566
+ callback_called = false
567
+
568
+ expect do
569
+ described_class.retriable(on: on_hash, on_give_up: proc { callback_called = true }) do
570
+ increment_tries
571
+ raise SecondNonStandardError, "not a match"
572
+ end
573
+ end.to raise_error(SecondNonStandardError, /not a match/)
574
+
575
+ expect(callback_called).to be(false)
576
+ expect(@tries).to eq(1)
577
+ end
578
+
410
579
  it "successfully retries when the values are arrays of exception message patterns" do
411
580
  exceptions = []
412
581
  handler = ->(exception, try, _elapsed_time, _next_interval) { exceptions[try] = exception }
@@ -601,6 +770,38 @@ describe Retriable do
601
770
  end
602
771
  end
603
772
 
773
+ context "#retriable tries/intervals precedence" do
774
+ it "lets a per-call tries clear globally configured intervals" do
775
+ described_class.configure { |c| c.intervals = [0.5, 1.0] }
776
+
777
+ expect do
778
+ described_class.retriable(tries: 1) { increment_tries_with_exception }
779
+ end.to raise_error(StandardError)
780
+
781
+ expect(@tries).to eq(1)
782
+ end
783
+
784
+ it "still lets per-call intervals win when both intervals and tries are given" do
785
+ expect do
786
+ described_class.retriable(intervals: [0.5, 1.0], tries: 1) { increment_tries_with_exception }
787
+ end.to raise_error(StandardError)
788
+
789
+ expect(@tries).to eq(3) # intervals.size + 1
790
+ end
791
+
792
+ it "lets a with_context tries clear context intervals" do
793
+ described_class.configure do |c|
794
+ c.contexts[:api] = { intervals: [0.5, 1.0] }
795
+ end
796
+
797
+ expect do
798
+ described_class.with_context(:api, tries: 1) { increment_tries_with_exception }
799
+ end.to raise_error(StandardError)
800
+
801
+ expect(@tries).to eq(1)
802
+ end
803
+ end
804
+
604
805
  context "#with_override" do
605
806
  it "takes precedence over both global config and local options" do
606
807
  described_class.configure { |c| c.tries = 2 }
@@ -737,17 +938,15 @@ describe Retriable do
737
938
  end
738
939
 
739
940
  it "treats non-hash configured contexts as empty when override contexts are hash" do
740
- begin
741
- described_class.configure { |c| c.contexts = nil }
742
-
743
- described_class.with_override(contexts: { api: { tries: 1 } }) do
744
- described_class.with_context(:api) { increment_tries }
745
- end
941
+ described_class.configure { |c| c.contexts = nil }
746
942
 
747
- expect(@tries).to eq(1)
748
- ensure
749
- described_class.configure { |c| c.contexts = {} }
943
+ described_class.with_override(contexts: { api: { tries: 1 } }) do
944
+ described_class.with_context(:api) { increment_tries }
750
945
  end
946
+
947
+ expect(@tries).to eq(1)
948
+ ensure
949
+ described_class.configure { |c| c.contexts = {} }
751
950
  end
752
951
 
753
952
  it "ignores nil override contexts values in with_context" do
@@ -1017,6 +1216,31 @@ describe Retriable do
1017
1216
 
1018
1217
  expect(other_thread_tries).to eq(3)
1019
1218
  end
1219
+
1220
+ it "applies overridden on_give_up handlers" do
1221
+ callback_called = false
1222
+
1223
+ expect do
1224
+ described_class.with_override(on_give_up: proc { callback_called = true }) do
1225
+ described_class.retriable(tries: 1) { increment_tries_with_exception }
1226
+ end
1227
+ end.to raise_error(StandardError)
1228
+
1229
+ expect(callback_called).to be(true)
1230
+ end
1231
+
1232
+ it "applies on_give_up handlers configured via per-context overrides" do
1233
+ received_reason = nil
1234
+ handler = proc { |_e, _try, _elapsed, _interval, reason| received_reason = reason }
1235
+
1236
+ expect do
1237
+ described_class.with_override(contexts: { api: { tries: 1, on_give_up: handler } }) do
1238
+ described_class.with_context(:api) { increment_tries_with_exception }
1239
+ end
1240
+ end.to raise_error(StandardError)
1241
+
1242
+ expect(received_reason).to eq(:tries_exhausted)
1243
+ end
1020
1244
  end
1021
1245
 
1022
1246
  context "#with_context" do
@@ -1034,8 +1258,15 @@ describe Retriable do
1034
1258
  expect(@tries).to eq(1)
1035
1259
  end
1036
1260
 
1037
- it "returns nil when called without a block" do
1038
- expect(described_class.with_context(:sql)).to be_nil
1261
+ it "raises an ArgumentError when called without a block" do
1262
+ expect { described_class.with_context(:sql) }
1263
+ .to raise_error(ArgumentError, /with_context requires a block/)
1264
+ expect(@tries).to eq(0)
1265
+ end
1266
+
1267
+ it "checks for a block before looking up the context" do
1268
+ expect { described_class.with_context(:missing) }
1269
+ .to raise_error(ArgumentError, /with_context requires a block/)
1039
1270
  expect(@tries).to eq(0)
1040
1271
  end
1041
1272
 
@@ -1075,5 +1306,24 @@ describe Retriable do
1075
1306
  described_class.with_context(:broken) { increment_tries }
1076
1307
  expect(@tries).to eq(1)
1077
1308
  end
1309
+
1310
+ it "surfaces an invalid context on any retriable call before that context is used" do
1311
+ described_class.configure { |c| c.contexts[:unused] = { contexts: {} } }
1312
+
1313
+ expect { described_class.retriable { :ok } }
1314
+ .to raise_error(ArgumentError, /contexts is not a valid option/)
1315
+ end
1316
+
1317
+ it "invokes on_give_up configured on a context" do
1318
+ callback_called = false
1319
+ described_class.configure do |c|
1320
+ c.contexts[:flaky] = { tries: 1, on_give_up: proc { callback_called = true } }
1321
+ end
1322
+
1323
+ expect { described_class.with_context(:flaky) { increment_tries_with_exception } }
1324
+ .to raise_error(StandardError)
1325
+
1326
+ expect(callback_called).to be(true)
1327
+ end
1078
1328
  end
1079
1329
  end
data/spec/spec_helper.rb CHANGED
@@ -1,27 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "simplecov"
4
- SimpleCov.start
4
+ SimpleCov.start do
5
+ minimum_coverage 95
6
+ end
5
7
 
6
8
  require "pry"
7
9
  require_relative "../lib/retriable"
8
10
  require_relative "support/exceptions"
9
11
 
10
- # Make Retriable's deprecation notices observable to RSpec's
11
- # `output().to_stderr` matcher. On Ruby 3.0+ the `:deprecated` warning category
12
- # is suppressed by default, which would hide the notices we want to assert on.
13
- WARNING_DEPRECATION_SUPPORTED = defined?(Warning) && Warning.respond_to?(:[])
14
- Warning[:deprecated] = true if WARNING_DEPRECATION_SUPPORTED
15
-
16
- # Used by deprecation specs that only make sense on Rubies where `Kernel#warn`
17
- # supports the `category:` keyword (added in Ruby 2.7).
18
- WARN_CATEGORY_SUPPORTED = WARNING_DEPRECATION_SUPPORTED &&
19
- Kernel.method(:warn).parameters.include?(%i[key category])
20
-
21
12
  RSpec.configure do |config|
22
13
  config.before(:each) do
23
14
  srand(0)
24
- Retriable::Config.timeout_deprecation_warned = false
25
- Warning[:deprecated] = true if WARNING_DEPRECATION_SUPPORTED
26
15
  end
27
16
  end
metadata CHANGED
@@ -1,56 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: retriable
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.8.0
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jack Chu
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies:
12
- - !ruby/object:Gem::Dependency
13
- name: bundler
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
16
- - - ">="
17
- - !ruby/object:Gem::Version
18
- version: '0'
19
- type: :development
20
- prerelease: false
21
- version_requirements: !ruby/object:Gem::Requirement
22
- requirements:
23
- - - ">="
24
- - !ruby/object:Gem::Version
25
- version: '0'
26
- - !ruby/object:Gem::Dependency
27
- name: rspec
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: '3'
33
- type: :development
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '3'
40
- - !ruby/object:Gem::Dependency
41
- name: listen
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '3.1'
47
- type: :development
48
- prerelease: false
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '3.1'
11
+ dependencies: []
54
12
  description: Retriable is a simple DSL to retry failed code blocks with randomized
55
13
  exponential backoff. This is especially useful when interacting with external APIs/services
56
14
  or file system calls.
@@ -91,7 +49,8 @@ files:
91
49
  homepage: https://github.com/kamui/retriable
92
50
  licenses:
93
51
  - MIT
94
- metadata: {}
52
+ metadata:
53
+ rubygems_mfa_required: 'true'
95
54
  rdoc_options: []
96
55
  require_paths:
97
56
  - lib
@@ -99,20 +58,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
99
58
  requirements:
100
59
  - - ">="
101
60
  - !ruby/object:Gem::Version
102
- version: 2.3.0
61
+ version: '3.2'
103
62
  required_rubygems_version: !ruby/object:Gem::Requirement
104
63
  requirements:
105
64
  - - ">="
106
65
  - !ruby/object:Gem::Version
107
66
  version: '0'
108
67
  requirements: []
109
- rubygems_version: 4.0.3
68
+ rubygems_version: 3.6.9
110
69
  specification_version: 4
111
70
  summary: Retriable is a simple DSL to retry failed code blocks with randomized exponential
112
71
  backoff
113
- test_files:
114
- - spec/config_spec.rb
115
- - spec/exponential_backoff_spec.rb
116
- - spec/retriable_spec.rb
117
- - spec/spec_helper.rb
118
- - spec/support/exceptions.rb
72
+ test_files: []