retriable 3.1.2 → 3.4.1

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.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "retriable"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -1,16 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "exponential_backoff"
2
4
 
3
5
  module Retriable
4
6
  class Config
5
- ATTRIBUTES = ExponentialBackoff::ATTRIBUTES + [
6
- :sleep_disabled,
7
- :max_elapsed_time,
8
- :intervals,
9
- :timeout,
10
- :on,
11
- :on_retry,
12
- :contexts,
13
- ].freeze
7
+ ATTRIBUTES = (ExponentialBackoff::ATTRIBUTES + %i[
8
+ sleep_disabled
9
+ max_elapsed_time
10
+ intervals
11
+ timeout
12
+ on
13
+ retry_if
14
+ on_retry
15
+ contexts
16
+ ]).freeze
14
17
 
15
18
  attr_accessor(*ATTRIBUTES)
16
19
 
@@ -27,11 +30,13 @@ module Retriable
27
30
  @intervals = nil
28
31
  @timeout = nil
29
32
  @on = [StandardError]
33
+ @retry_if = nil
30
34
  @on_retry = nil
31
35
  @contexts = {}
32
36
 
33
37
  opts.each do |k, v|
34
38
  raise ArgumentError, "#{k} is not a valid option" if !ATTRIBUTES.include?(k)
39
+
35
40
  instance_variable_set(:"@#{k}", v)
36
41
  end
37
42
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "../../retriable"
2
4
 
3
5
  module Kernel
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Retriable
2
4
  class ExponentialBackoff
3
- ATTRIBUTES = [
4
- :tries,
5
- :base_interval,
6
- :multiplier,
7
- :max_interval,
8
- :rand_factor,
5
+ ATTRIBUTES = %i[
6
+ tries
7
+ base_interval
8
+ multiplier
9
+ max_interval
10
+ rand_factor
9
11
  ].freeze
10
12
 
11
13
  attr_accessor(*ATTRIBUTES)
@@ -19,13 +21,14 @@ module Retriable
19
21
 
20
22
  opts.each do |k, v|
21
23
  raise ArgumentError, "#{k} is not a valid option" if !ATTRIBUTES.include?(k)
24
+
22
25
  instance_variable_set(:"@#{k}", v)
23
26
  end
24
27
  end
25
28
 
26
29
  def intervals
27
30
  intervals = Array.new(tries) do |iteration|
28
- [base_interval * multiplier**iteration, max_interval].min
31
+ [base_interval * (multiplier**iteration), max_interval].min
29
32
  end
30
33
 
31
34
  return intervals if rand_factor.zero?
@@ -36,7 +39,7 @@ module Retriable
36
39
  private
37
40
 
38
41
  def randomize(interval)
39
- delta = rand_factor * interval * 1.0
42
+ delta = rand_factor * interval.to_f
40
43
  min = interval - delta
41
44
  max = interval + delta
42
45
  rand(min..max)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Retriable
2
- VERSION = "3.1.2".freeze
4
+ VERSION = "3.4.1"
3
5
  end
data/lib/retriable.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "timeout"
2
4
  require_relative "retriable/config"
3
5
  require_relative "retriable/exponential_backoff"
@@ -16,61 +18,123 @@ module Retriable
16
18
 
17
19
  def with_context(context_key, options = {}, &block)
18
20
  if !config.contexts.key?(context_key)
19
- raise ArgumentError, "#{context_key} not found in Retriable.config.contexts. Available contexts: #{config.contexts.keys}"
21
+ raise ArgumentError,
22
+ "#{context_key} not found in Retriable.config.contexts. Available contexts: #{config.contexts.keys}"
20
23
  end
21
24
 
22
- retriable(config.contexts[context_key].merge(options), &block) if block
25
+ return unless block_given?
26
+
27
+ retriable(config.contexts[context_key].merge(options), &block)
23
28
  end
24
29
 
25
- def retriable(opts = {})
30
+ def retriable(opts = {}, &block)
26
31
  local_config = opts.empty? ? config : Config.new(config.to_h.merge(opts))
27
32
 
28
- tries = local_config.tries
29
- base_interval = local_config.base_interval
30
- max_interval = local_config.max_interval
31
- rand_factor = local_config.rand_factor
32
- multiplier = local_config.multiplier
33
- max_elapsed_time = local_config.max_elapsed_time
34
- intervals = local_config.intervals
35
- timeout = local_config.timeout
36
- on = local_config.on
37
- on_retry = local_config.on_retry
38
- sleep_disabled = local_config.sleep_disabled
33
+ tries = local_config.tries
34
+ intervals = build_intervals(local_config, tries)
35
+ timeout = local_config.timeout
36
+ on = local_config.on
37
+ retry_if = local_config.retry_if
38
+ on_retry = local_config.on_retry
39
+ sleep_disabled = local_config.sleep_disabled
40
+ max_elapsed_time = local_config.max_elapsed_time
39
41
 
40
42
  exception_list = on.is_a?(Hash) ? on.keys : on
41
- start_time = Time.now
42
- elapsed_time = -> { Time.now - start_time }
43
-
44
- if intervals
45
- tries = intervals.size + 1
46
- else
47
- intervals = ExponentialBackoff.new(
48
- tries: tries - 1,
49
- base_interval: base_interval,
50
- multiplier: multiplier,
51
- max_interval: max_interval,
52
- rand_factor: rand_factor
53
- ).intervals
54
- end
43
+ exception_list = [*exception_list]
44
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
45
+ elapsed_time = -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time }
46
+
47
+ tries = intervals.size + 1
55
48
 
49
+ execute_tries(
50
+ tries: tries, intervals: intervals, timeout: timeout,
51
+ exception_list: exception_list, on: on, retry_if: retry_if, on_retry: on_retry,
52
+ elapsed_time: elapsed_time, max_elapsed_time: max_elapsed_time,
53
+ sleep_disabled: sleep_disabled, &block
54
+ )
55
+ end
56
+
57
+ def execute_tries( # rubocop:disable Metrics/ParameterLists
58
+ tries:, intervals:, timeout:, exception_list:,
59
+ on:, retry_if:, on_retry:, elapsed_time:, max_elapsed_time:, sleep_disabled:, &block
60
+ )
56
61
  tries.times do |index|
57
62
  try = index + 1
58
63
 
59
64
  begin
60
- return Timeout.timeout(timeout) { return yield(try) } if timeout
61
- return yield(try)
62
- rescue *[*exception_list] => exception
63
- if on.is_a?(Hash)
64
- raise unless exception_list.any? do |e|
65
- exception.is_a?(e) && ([*on[e]].empty? || [*on[e]].any? { |pattern| exception.message =~ pattern })
66
- end
67
- end
65
+ return call_with_timeout(timeout, try, &block)
66
+ rescue *exception_list => e
67
+ raise unless retriable_exception?(e, on, exception_list, retry_if)
68
68
 
69
69
  interval = intervals[index]
70
- on_retry.call(exception, try, elapsed_time.call, interval) if on_retry
71
- raise if try >= tries || (elapsed_time.call + interval) > max_elapsed_time
70
+ call_on_retry(on_retry, e, try, elapsed_time.call, interval)
71
+
72
+ raise unless can_retry?(try, tries, elapsed_time.call, interval, max_elapsed_time)
73
+
72
74
  sleep interval if sleep_disabled != true
73
75
  end
74
76
  end
75
77
  end
78
+
79
+ def build_intervals(local_config, tries)
80
+ return local_config.intervals if local_config.intervals
81
+
82
+ ExponentialBackoff.new(
83
+ tries: tries - 1,
84
+ base_interval: local_config.base_interval,
85
+ multiplier: local_config.multiplier,
86
+ max_interval: local_config.max_interval,
87
+ rand_factor: local_config.rand_factor,
88
+ ).intervals
89
+ end
90
+
91
+ def call_with_timeout(timeout, try)
92
+ return Timeout.timeout(timeout) { yield(try) } if timeout
93
+
94
+ yield(try)
95
+ end
96
+
97
+ def call_on_retry(on_retry, exception, try, elapsed_time, interval)
98
+ return unless on_retry
99
+
100
+ on_retry.call(exception, try, elapsed_time, interval)
101
+ end
102
+
103
+ def can_retry?(try, tries, elapsed_time, interval, max_elapsed_time)
104
+ return false unless try < tries
105
+ return true if max_elapsed_time.nil?
106
+
107
+ (elapsed_time + interval) <= max_elapsed_time
108
+ end
109
+
110
+ # When `on` is a Hash, we need to verify the exception matches a pattern.
111
+ # For any non-Hash `on` value (e.g., Array of classes, single Exception class,
112
+ # or Module), the `rescue *exception_list` clause already guarantees the
113
+ # exception is retriable with respect to `on`; `retry_if`, if provided, is an
114
+ # additional gate that can still cause this method to return false.
115
+ def retriable_exception?(exception, on, exception_list, retry_if)
116
+ return false if on.is_a?(Hash) && !hash_exception_match?(exception, on, exception_list)
117
+ return false if retry_if && !retry_if.call(exception)
118
+
119
+ true
120
+ end
121
+
122
+ def hash_exception_match?(exception, on, exception_list)
123
+ exception_list.any? do |error_class|
124
+ next false unless exception.is_a?(error_class)
125
+
126
+ patterns = [*on[error_class]]
127
+ patterns.empty? || patterns.any? { |pattern| exception.message =~ pattern }
128
+ end
129
+ end
130
+
131
+ private_class_method(
132
+ :execute_tries,
133
+ :build_intervals,
134
+ :call_with_timeout,
135
+ :call_on_retry,
136
+ :can_retry?,
137
+ :retriable_exception?,
138
+ :hash_exception_match?,
139
+ )
76
140
  end
data/retriable.gemspec CHANGED
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require "retriable/version"
5
6
 
@@ -9,24 +10,20 @@ Gem::Specification.new do |spec|
9
10
  spec.authors = ["Jack Chu"]
10
11
  spec.email = ["jack@jackchu.com"]
11
12
  spec.summary = "Retriable is a simple DSL to retry failed code blocks with randomized exponential backoff"
12
- spec.description = "Retriable is a simple DSL to retry failed code blocks with randomized exponential backoff. This is especially useful when interacting external api/services or file system calls."
13
- spec.homepage = "http://github.com/kamui/retriable"
13
+ spec.description = "Retriable is a simple DSL to retry failed code blocks with randomized " \
14
+ "exponential backoff. This is especially useful when interacting with external " \
15
+ "APIs/services or file system calls."
16
+ spec.homepage = "https://github.com/kamui/retriable"
14
17
  spec.license = "MIT"
15
18
 
16
19
  spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
21
  spec.require_paths = ["lib"]
20
22
 
21
- spec.required_ruby_version = ">= 2.0.0"
23
+ spec.required_ruby_version = ">= 2.3.0"
22
24
 
23
25
  spec.add_development_dependency "bundler"
24
26
  spec.add_development_dependency "rspec", "~> 3"
25
27
 
26
- if RUBY_VERSION < "2.3"
27
- spec.add_development_dependency "ruby_dep", "~> 1.3.1"
28
- spec.add_development_dependency "listen", "~> 3.0.8"
29
- else
30
- spec.add_development_dependency "listen", "~> 3.1"
31
- end
28
+ spec.add_development_dependency "listen", "~> 3.1"
32
29
  end
data/sig/retriable.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Retriable
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
data/spec/config_spec.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  describe Retriable::Config do
2
4
  let(:default_config) { described_class.new }
3
5
 
@@ -38,6 +40,10 @@ describe Retriable::Config do
38
40
  expect(default_config.on).to eq([StandardError])
39
41
  end
40
42
 
43
+ it "retry_if defaults to nil" do
44
+ expect(default_config.retry_if).to be_nil
45
+ end
46
+
41
47
  it "on_retry handler defaults to nil" do
42
48
  expect(default_config.on_retry).to be_nil
43
49
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  describe Retriable::ExponentialBackoff do
2
4
  context "defaults" do
3
5
  let(:backoff_config) { described_class.new }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  describe Retriable do
2
4
  let(:time_table_handler) do
3
5
  ->(_exception, try, _elapsed_time, next_interval) { @next_interval_table[try] = next_interval }
@@ -94,6 +96,28 @@ describe Retriable do
94
96
  expect(@tries).to eq(10)
95
97
  end
96
98
 
99
+ it "does not call on_retry when explicitly set to false" do
100
+ callback_called = false
101
+ original_on_retry = described_class.config.on_retry
102
+
103
+ begin
104
+ described_class.configure do |c|
105
+ c.on_retry = proc { |_exception, _try, _elapsed_time, _next_interval| callback_called = true }
106
+ end
107
+
108
+ expect do
109
+ described_class.retriable(on_retry: false, tries: 3) { increment_tries_with_exception }
110
+ end.to raise_error(StandardError)
111
+
112
+ expect(@tries).to eq(3)
113
+ expect(callback_called).to be(false)
114
+ ensure
115
+ described_class.configure do |c|
116
+ c.on_retry = original_on_retry
117
+ end
118
+ end
119
+ end
120
+
97
121
  context "with rand_factor 0.0 and an on_retry handler" do
98
122
  let(:tries) { 6 }
99
123
  let(:no_rand_timetable) { { 1 => 0.5, 2 => 0.75, 3 => 1.125 } }
@@ -130,6 +154,34 @@ describe Retriable do
130
154
  expect(@next_interval_table).to eq(interval_hash)
131
155
  expect(@tries).to eq(intervals.size + 1)
132
156
  end
157
+
158
+ it "intervals option overrides tries, base_interval, max_interval, rand_factor, and multiplier" do
159
+ # Even though we specify tries: 10, base_interval: 1.0, max_interval: 100.0,
160
+ # rand_factor: 0.8, and multiplier: 2.0, the explicit intervals should take precedence
161
+ custom_intervals = [0.1, 0.2, 0.3]
162
+
163
+ expect do
164
+ described_class.retriable(
165
+ intervals: custom_intervals,
166
+ tries: 10,
167
+ base_interval: 1.0,
168
+ max_interval: 100.0,
169
+ rand_factor: 0.8,
170
+ multiplier: 2.0,
171
+ on_retry: time_table_handler,
172
+ ) do
173
+ increment_tries_with_exception
174
+ end
175
+ end.to raise_error(StandardError)
176
+
177
+ # Should have 4 tries (3 intervals + 1), not 10
178
+ expect(@tries).to eq(4)
179
+ # Should use the exact intervals provided, not generate them
180
+ expect(@next_interval_table[1]).to eq(0.1)
181
+ expect(@next_interval_table[2]).to eq(0.2)
182
+ expect(@next_interval_table[3]).to eq(0.3)
183
+ expect(@next_interval_table[4]).to be_nil
184
+ end
133
185
  end
134
186
 
135
187
  context "with an array :on parameter" do
@@ -207,6 +259,50 @@ describe Retriable do
207
259
  end
208
260
  end
209
261
 
262
+ context "with a :retry_if parameter" do
263
+ it "retries only when retry_if returns true" do
264
+ described_class.retriable(tries: 3, retry_if: ->(_exception) { @tries < 3 }) do
265
+ increment_tries
266
+ raise StandardError, "StandardError occurred" if @tries < 3
267
+ end
268
+
269
+ expect(@tries).to eq(3)
270
+ end
271
+
272
+ it "does not retry when retry_if returns false" do
273
+ expect do
274
+ described_class.retriable(tries: 3, retry_if: ->(_exception) { false }) do
275
+ increment_tries_with_exception
276
+ end
277
+ end.to raise_error(StandardError)
278
+
279
+ expect(@tries).to eq(1)
280
+ end
281
+
282
+ it "can retry based on the wrapped exception cause" do
283
+ root_cause_class = Class.new(StandardError)
284
+ wrapper_class = Class.new(StandardError)
285
+
286
+ described_class.retriable(
287
+ on: [wrapper_class],
288
+ tries: 3,
289
+ retry_if: ->(exception) { exception.cause.is_a?(root_cause_class) },
290
+ ) do
291
+ increment_tries
292
+
293
+ if @tries < 3
294
+ begin
295
+ raise root_cause_class, "root cause"
296
+ rescue root_cause_class
297
+ raise wrapper_class, "wrapped"
298
+ end
299
+ end
300
+ end
301
+
302
+ expect(@tries).to eq(3)
303
+ end
304
+ end
305
+
210
306
  it "runs for a max elapsed time of 2 seconds" do
211
307
  described_class.configure { |c| c.sleep_disabled = false }
212
308
 
@@ -219,12 +315,55 @@ describe Retriable do
219
315
  expect(@tries).to eq(2)
220
316
  end
221
317
 
318
+ it "retries up to tries limit when max_elapsed_time is nil" do
319
+ expect do
320
+ described_class.retriable(tries: 4, max_elapsed_time: nil) { increment_tries_with_exception }
321
+ end.to raise_error(StandardError)
322
+
323
+ expect(@tries).to eq(4)
324
+ end
325
+
326
+ it "uses monotonic clock for elapsed time tracking" do
327
+ # Stub Process.clock_gettime to return controlled values so we can
328
+ # verify elapsed_time passed to on_retry is derived from the monotonic clock.
329
+ clock_calls = 0
330
+ allow(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC) do
331
+ value = clock_calls.to_f
332
+ clock_calls += 1
333
+ value
334
+ end
335
+
336
+ elapsed_times = []
337
+ on_retry = ->(_exception, _try, elapsed_time, _next_interval) { elapsed_times << elapsed_time }
338
+
339
+ expect do
340
+ described_class.retriable(tries: 3, on_retry: on_retry) { increment_tries_with_exception }
341
+ end.to raise_error(StandardError)
342
+
343
+ # start_time (call 0) + at least one elapsed_time computation per retry
344
+ expect(clock_calls).to be >= 3
345
+ # elapsed_time values should be positive and non-decreasing
346
+ expect(elapsed_times).to all(be > 0)
347
+ expect(elapsed_times).to eq(elapsed_times.sort)
348
+ end
349
+
222
350
  it "raises ArgumentError on invalid options" do
223
351
  expect { described_class.retriable(does_not_exist: 123) { increment_tries } }.to raise_error(ArgumentError)
224
352
  end
225
353
  end
226
354
 
227
355
  context "#configure" do
356
+ it "exposes only the intended public API" do
357
+ public_api_methods = %i[
358
+ retriable
359
+ with_context
360
+ configure
361
+ config
362
+ ]
363
+
364
+ expect(described_class.singleton_methods(false)).to match_array(public_api_methods)
365
+ end
366
+
228
367
  it "raises NoMethodError on invalid configuration" do
229
368
  expect { described_class.configure { |c| c.does_not_exist = 123 } }.to raise_error(NoMethodError)
230
369
  end
@@ -245,6 +384,22 @@ describe Retriable do
245
384
  expect(@tries).to eq(1)
246
385
  end
247
386
 
387
+ it "returns nil when called without a block" do
388
+ expect(described_class.with_context(:sql)).to be_nil
389
+ expect(@tries).to eq(0)
390
+ end
391
+
392
+ it "passes try count through to the context block" do
393
+ seen_tries = []
394
+
395
+ described_class.with_context(:api) do |try|
396
+ seen_tries << try
397
+ raise StandardError if try < 3
398
+ end
399
+
400
+ expect(seen_tries).to eq([1, 2, 3])
401
+ end
402
+
248
403
  it "respects the context options" do
249
404
  expect { described_class.with_context(:api) { increment_tries_with_exception } }.to raise_error(StandardError)
250
405
  expect(@tries).to eq(api_tries)
data/spec/spec_helper.rb CHANGED
@@ -1,15 +1,11 @@
1
- require "codeclimate-test-reporter"
2
- require "simplecov"
3
-
4
- CodeClimate::TestReporter.configure do |config|
5
- config.logger.level = Logger::WARN
6
- end
1
+ # frozen_string_literal: true
7
2
 
3
+ require "simplecov"
8
4
  SimpleCov.start
9
5
 
10
6
  require "pry"
11
7
  require_relative "../lib/retriable"
12
- require_relative "support/exceptions.rb"
8
+ require_relative "support/exceptions"
13
9
 
14
10
  RSpec.configure do |config|
15
11
  config.before(:each) do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class NonStandardError < Exception; end
2
4
  class SecondNonStandardError < NonStandardError; end
3
5
  class DifferentError < Exception; end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: retriable
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.2
4
+ version: 3.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jack Chu
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2018-06-11 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bundler
@@ -53,7 +52,7 @@ dependencies:
53
52
  - !ruby/object:Gem::Version
54
53
  version: '3.1'
55
54
  description: Retriable is a simple DSL to retry failed code blocks with randomized
56
- exponential backoff. This is especially useful when interacting external api/services
55
+ exponential backoff. This is especially useful when interacting with external APIs/services
57
56
  or file system calls.
58
57
  email:
59
58
  - jack@jackchu.com
@@ -61,31 +60,36 @@ executables: []
61
60
  extensions: []
62
61
  extra_rdoc_files: []
63
62
  files:
63
+ - ".github/dependabot.yml"
64
+ - ".github/workflows/main.yml"
64
65
  - ".gitignore"
65
66
  - ".hound.yml"
66
67
  - ".rspec"
67
68
  - ".rubocop.yml"
68
- - ".travis.yml"
69
69
  - CHANGELOG.md
70
+ - CODE_OF_CONDUCT.md
70
71
  - Gemfile
71
72
  - LICENSE
72
73
  - README.md
74
+ - Rakefile
75
+ - bin/console
76
+ - bin/setup
73
77
  - lib/retriable.rb
74
78
  - lib/retriable/config.rb
75
79
  - lib/retriable/core_ext/kernel.rb
76
80
  - lib/retriable/exponential_backoff.rb
77
81
  - lib/retriable/version.rb
78
82
  - retriable.gemspec
83
+ - sig/retriable.rbs
79
84
  - spec/config_spec.rb
80
85
  - spec/exponential_backoff_spec.rb
81
86
  - spec/retriable_spec.rb
82
87
  - spec/spec_helper.rb
83
88
  - spec/support/exceptions.rb
84
- homepage: http://github.com/kamui/retriable
89
+ homepage: https://github.com/kamui/retriable
85
90
  licenses:
86
91
  - MIT
87
92
  metadata: {}
88
- post_install_message:
89
93
  rdoc_options: []
90
94
  require_paths:
91
95
  - lib
@@ -93,16 +97,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
93
97
  requirements:
94
98
  - - ">="
95
99
  - !ruby/object:Gem::Version
96
- version: 2.0.0
100
+ version: 2.3.0
97
101
  required_rubygems_version: !ruby/object:Gem::Requirement
98
102
  requirements:
99
103
  - - ">="
100
104
  - !ruby/object:Gem::Version
101
105
  version: '0'
102
106
  requirements: []
103
- rubyforge_project:
104
- rubygems_version: 2.7.6
105
- signing_key:
107
+ rubygems_version: 4.0.3
106
108
  specification_version: 4
107
109
  summary: Retriable is a simple DSL to retry failed code blocks with randomized exponential
108
110
  backoff