retriable 4.1.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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +18 -2
- data/CHANGELOG.md +61 -0
- data/Gemfile +2 -0
- data/README.md +12 -2
- data/lib/retriable/config.rb +28 -6
- data/lib/retriable/core_ext/kernel.rb +2 -0
- data/lib/retriable/exponential_backoff.rb +13 -5
- data/lib/retriable/validation.rb +7 -0
- data/lib/retriable/version.rb +1 -1
- data/lib/retriable.rb +5 -2
- data/sig/retriable.rbs +29 -1
- data/spec/config_spec.rb +46 -0
- data/spec/retriable_spec.rb +31 -7
- data/spec/spec_helper.rb +3 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a551efb6db5acea9e82dde8e48afdea1869f6b93461a3c48c33f8385fbea6392
|
|
4
|
+
data.tar.gz: fbf6bb8c5ade48420e0f2a43532bafff0e4159eb4cf149bce559116ec1c91e50
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 19d5ac72e1614b7fdb3984697db76ad3f6ae2e9b1f60ff1eb470e210738cd67154afc2776b55329fca345fad6263035a41eb0748626f427fd2d05cc0bfea9c89
|
|
7
|
+
data.tar.gz: c1d46594742b7c19985a63037199125b718e6c6395dd8276013719d8c7f9cd64376637471b90fa9249869767da9a2474cafc31afd04f373f73249d8ebdfa0337
|
data/.github/workflows/main.yml
CHANGED
|
@@ -30,8 +30,6 @@ jobs:
|
|
|
30
30
|
"4.0",
|
|
31
31
|
jruby,
|
|
32
32
|
]
|
|
33
|
-
env:
|
|
34
|
-
CC_TEST_REPORTER_ID: 20a1139ef1830b4f813a10a03d90e8aa179b5226f75e75c5a949b25756ebf558
|
|
35
33
|
|
|
36
34
|
steps:
|
|
37
35
|
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
|
@@ -63,3 +61,21 @@ jobs:
|
|
|
63
61
|
|
|
64
62
|
- name: Run rubocop
|
|
65
63
|
run: bundle exec rubocop
|
|
64
|
+
|
|
65
|
+
- name: Validate RBS
|
|
66
|
+
run: bundle exec rbs -I sig validate
|
|
67
|
+
|
|
68
|
+
audit:
|
|
69
|
+
runs-on: ubuntu-24.04
|
|
70
|
+
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
73
|
+
|
|
74
|
+
- name: Setup ruby
|
|
75
|
+
uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1
|
|
76
|
+
with:
|
|
77
|
+
ruby-version: "3.3"
|
|
78
|
+
bundler-cache: true
|
|
79
|
+
|
|
80
|
+
- name: Run bundler-audit
|
|
81
|
+
run: bundle exec bundle-audit check --update
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,66 @@
|
|
|
1
1
|
# HEAD
|
|
2
2
|
|
|
3
|
+
## 4.2.0
|
|
4
|
+
|
|
5
|
+
### Bug fixes
|
|
6
|
+
|
|
7
|
+
- The `Kernel` extension methods (`require "retriable/core_ext/kernel"`) are now
|
|
8
|
+
private, matching idiomatic `Kernel` helpers like `puts` and `rand`.
|
|
9
|
+
Previously `retriable` and `retriable_with_context` were public instance
|
|
10
|
+
methods, so they leaked onto every object's public API and could be invoked
|
|
11
|
+
with an explicit receiver (e.g. `"foo".retriable { ... }`). They remain
|
|
12
|
+
callable in the documented receiver-less form.
|
|
13
|
+
([#146](https://github.com/kamui/retriable/pull/146))
|
|
14
|
+
- `Retriable.with_context` (and `Kernel#retriable_with_context`) now raises
|
|
15
|
+
`ArgumentError` when called without a block, matching `with_override`.
|
|
16
|
+
Previously a missing block was silently ignored: the call returned `nil` and
|
|
17
|
+
the intended block never ran, hiding a caller bug. Behavior change: code that
|
|
18
|
+
relied on the silent no-op will now raise.
|
|
19
|
+
- `Config#validate!` now validates the structure of each entry in `contexts`,
|
|
20
|
+
so configured contexts are checked on every `Retriable.retriable`/
|
|
21
|
+
`with_context` call rather than only when a given context is first used. A
|
|
22
|
+
context whose options contain an unknown key (including a nested `contexts`
|
|
23
|
+
key) now raises `ArgumentError, "<key> is not a valid option"`, matching the
|
|
24
|
+
`with_override` path. Non-Hash `contexts` and non-Hash per-context values
|
|
25
|
+
remain leniently treated as empty options (no behavior change). Option
|
|
26
|
+
_values_ are still validated lazily at retry time, unchanged.
|
|
27
|
+
|
|
28
|
+
### Docs
|
|
29
|
+
|
|
30
|
+
- Document that `on_retry` receives `next_interval: nil` on the final rescued
|
|
31
|
+
attempt, when Retriable is about to give up because `tries` are exhausted.
|
|
32
|
+
`on_retry` still fires before `on_give_up` (unchanged behavior); the `nil`
|
|
33
|
+
contract is now called out in the `on_retry` documentation so handlers guard
|
|
34
|
+
arithmetic or logging on `next_interval`.
|
|
35
|
+
|
|
36
|
+
### Performance
|
|
37
|
+
|
|
38
|
+
- `Config#initialize` no longer allocates a throwaway `ExponentialBackoff` (and
|
|
39
|
+
runs its redundant `validate!`) just to read default values. Defaults now live
|
|
40
|
+
in a frozen `ExponentialBackoff::DEFAULTS` constant, removing an allocation and
|
|
41
|
+
redundant validation from the `retriable` hot path.
|
|
42
|
+
([#149](https://github.com/kamui/retriable/pull/149))
|
|
43
|
+
|
|
44
|
+
## 4.1.1
|
|
45
|
+
|
|
46
|
+
### Bug fixes
|
|
47
|
+
|
|
48
|
+
- `retry_if`, `on_retry`, and `on_give_up` are now validated to be callable
|
|
49
|
+
(respond to `#call`) or falsy. A non-callable truthy value raises
|
|
50
|
+
`ArgumentError` at configuration time instead of a later `NoMethodError` on a
|
|
51
|
+
retry path. ([#140](https://github.com/kamui/retriable/pull/140))
|
|
52
|
+
|
|
53
|
+
### Internal
|
|
54
|
+
|
|
55
|
+
- Add RBS type signatures for the public API (`Retriable.configure`, `config`,
|
|
56
|
+
`retriable`, `with_override`, `with_context`, and `Retriable::Config`) and
|
|
57
|
+
validate them in CI with `rbs validate`.
|
|
58
|
+
([#142](https://github.com/kamui/retriable/pull/142))
|
|
59
|
+
- Enforce a minimum test coverage floor and add a `bundler-audit` dependency
|
|
60
|
+
audit job to CI. ([#143](https://github.com/kamui/retriable/pull/143))
|
|
61
|
+
- Remove an unused `CC_TEST_REPORTER_ID` from the CI workflow.
|
|
62
|
+
([#141](https://github.com/kamui/retriable/pull/141))
|
|
63
|
+
|
|
3
64
|
## 4.1.0
|
|
4
65
|
|
|
5
66
|
### Bug fixes
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -248,8 +248,13 @@ do not inherit it. This makes `#with_override` safe to use in parallel test
|
|
|
248
248
|
runners. Fibers running inside the same thread share the thread's active
|
|
249
249
|
override.
|
|
250
250
|
|
|
251
|
-
`#with_override` stores the provided options
|
|
252
|
-
|
|
251
|
+
`#with_override` stores the provided options hash **by reference** and reads
|
|
252
|
+
from it on every attempt while the block runs. Treat the hash and all of its
|
|
253
|
+
nested values as immutable for the duration of the block: do not mutate them
|
|
254
|
+
from inside the block, and do not mutate them from another thread or fiber that
|
|
255
|
+
shares this thread's active override. Mutating the options mid-block results in
|
|
256
|
+
undefined retry behavior. If options must be computed, build the hash before
|
|
257
|
+
calling `#with_override` and do not retain a reference you will later mutate.
|
|
253
258
|
|
|
254
259
|
For test-integration patterns (RSpec `around`, helper methods, Minitest, etc.),
|
|
255
260
|
see [docs/testing.md](docs/testing.md).
|
|
@@ -367,6 +372,8 @@ Retriable.retriable(on_retry: do_this_on_each_retry) do
|
|
|
367
372
|
end
|
|
368
373
|
```
|
|
369
374
|
|
|
375
|
+
> **Note:** On the final rescued attempt — when Retriable is about to give up because `tries` are exhausted — `on_retry` still fires (before `on_give_up`; see below), but `next_interval` is **`nil`** because there is no next retry. Guard any handler that does arithmetic or formatting on `next_interval` (for example `next_interval&.*(1000)`, or `if next_interval`), and avoid unconditionally logging messages like `"retrying in #{next_interval}s"` since no retry is coming. This mirrors the `nil` contract documented for [`on_give_up`](#callbacks) below.
|
|
376
|
+
|
|
370
377
|
#### Disabling a Configured Callback Per Call
|
|
371
378
|
|
|
372
379
|
If `on_retry` is set in `Retriable.configure`, every call uses it by default. To opt a specific call out — for example, a critical call site that should not log on retry — pass `on_retry: false` or `on_retry: nil`.
|
|
@@ -471,6 +478,9 @@ Retriable.with_context(:mysql, tries: 30) do
|
|
|
471
478
|
end
|
|
472
479
|
```
|
|
473
480
|
|
|
481
|
+
`#with_context` requires a block and raises `ArgumentError` if called without
|
|
482
|
+
one.
|
|
483
|
+
|
|
474
484
|
## Kernel Extension
|
|
475
485
|
|
|
476
486
|
If you want to call `Retriable.retriable` without the `Retriable` module prefix and you don't mind extending `Kernel`,
|
data/lib/retriable/config.rb
CHANGED
|
@@ -18,16 +18,19 @@ module Retriable
|
|
|
18
18
|
contexts
|
|
19
19
|
]).freeze
|
|
20
20
|
|
|
21
|
+
CONTEXT_ATTRIBUTES = (ATTRIBUTES - %i[contexts]).freeze
|
|
22
|
+
private_constant :CONTEXT_ATTRIBUTES
|
|
23
|
+
|
|
21
24
|
attr_accessor(*ATTRIBUTES)
|
|
22
25
|
|
|
23
26
|
def initialize(opts = {})
|
|
24
|
-
|
|
27
|
+
defaults = ExponentialBackoff::DEFAULTS
|
|
25
28
|
|
|
26
|
-
@tries =
|
|
27
|
-
@base_interval =
|
|
28
|
-
@max_interval =
|
|
29
|
-
@rand_factor =
|
|
30
|
-
@multiplier =
|
|
29
|
+
@tries = defaults[:tries]
|
|
30
|
+
@base_interval = defaults[:base_interval]
|
|
31
|
+
@max_interval = defaults[:max_interval]
|
|
32
|
+
@rand_factor = defaults[:rand_factor]
|
|
33
|
+
@multiplier = defaults[:multiplier]
|
|
31
34
|
@sleep_disabled = false
|
|
32
35
|
@max_elapsed_time = 900 # 15 min
|
|
33
36
|
@intervals = nil
|
|
@@ -51,6 +54,10 @@ module Retriable
|
|
|
51
54
|
end
|
|
52
55
|
|
|
53
56
|
def validate!
|
|
57
|
+
validate_contexts
|
|
58
|
+
validate_callable(:retry_if, retry_if)
|
|
59
|
+
validate_callable(:on_retry, on_retry)
|
|
60
|
+
validate_callable(:on_give_up, on_give_up)
|
|
54
61
|
validate_on(on)
|
|
55
62
|
validate_intervals
|
|
56
63
|
if unbounded_tries?(tries)
|
|
@@ -67,6 +74,21 @@ module Retriable
|
|
|
67
74
|
|
|
68
75
|
private
|
|
69
76
|
|
|
77
|
+
def validate_contexts
|
|
78
|
+
return unless contexts.is_a?(Hash)
|
|
79
|
+
return if contexts.empty?
|
|
80
|
+
|
|
81
|
+
contexts.each_value do |options|
|
|
82
|
+
next unless options.is_a?(Hash)
|
|
83
|
+
|
|
84
|
+
options.each_key do |k|
|
|
85
|
+
next if CONTEXT_ATTRIBUTES.include?(k)
|
|
86
|
+
|
|
87
|
+
raise ArgumentError, "#{k} is not a valid option"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
70
92
|
def validate_backoff_options
|
|
71
93
|
validate_non_negative_number(:base_interval, base_interval)
|
|
72
94
|
validate_non_negative_number(:multiplier, multiplier)
|
|
@@ -14,14 +14,22 @@ module Retriable
|
|
|
14
14
|
rand_factor
|
|
15
15
|
].freeze
|
|
16
16
|
|
|
17
|
+
DEFAULTS = {
|
|
18
|
+
tries: 3,
|
|
19
|
+
base_interval: 0.5,
|
|
20
|
+
max_interval: 60,
|
|
21
|
+
rand_factor: 0.5,
|
|
22
|
+
multiplier: 1.5
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
17
25
|
attr_accessor(*ATTRIBUTES)
|
|
18
26
|
|
|
19
27
|
def initialize(opts = {})
|
|
20
|
-
@tries =
|
|
21
|
-
@base_interval =
|
|
22
|
-
@max_interval =
|
|
23
|
-
@rand_factor =
|
|
24
|
-
@multiplier =
|
|
28
|
+
@tries = DEFAULTS[:tries]
|
|
29
|
+
@base_interval = DEFAULTS[:base_interval]
|
|
30
|
+
@max_interval = DEFAULTS[:max_interval]
|
|
31
|
+
@rand_factor = DEFAULTS[:rand_factor]
|
|
32
|
+
@multiplier = DEFAULTS[:multiplier]
|
|
25
33
|
|
|
26
34
|
opts.each do |k, v|
|
|
27
35
|
raise ArgumentError, "#{k} is not a valid option" if !ATTRIBUTES.include?(k)
|
data/lib/retriable/validation.rb
CHANGED
|
@@ -28,6 +28,13 @@ module Retriable
|
|
|
28
28
|
validate_non_negative_number(name, value)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
def validate_callable(name, value)
|
|
32
|
+
return unless value # nil/false disable the callback
|
|
33
|
+
return if value.respond_to?(:call)
|
|
34
|
+
|
|
35
|
+
raise ArgumentError, "#{name} must respond to #call or be nil"
|
|
36
|
+
end
|
|
37
|
+
|
|
31
38
|
def validate_rand_factor
|
|
32
39
|
return if finite_number?(rand_factor) && rand_factor >= 0 && rand_factor <= 1
|
|
33
40
|
|
data/lib/retriable/version.rb
CHANGED
data/lib/retriable.rb
CHANGED
|
@@ -41,6 +41,8 @@ module Retriable
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def with_context(context_key, options = {}, &)
|
|
44
|
+
raise ArgumentError, "with_context requires a block" unless block_given?
|
|
45
|
+
|
|
44
46
|
contexts = available_contexts
|
|
45
47
|
|
|
46
48
|
if !contexts.key?(context_key)
|
|
@@ -48,8 +50,6 @@ module Retriable
|
|
|
48
50
|
"#{context_key} not found in Retriable contexts (including overrides). Available contexts: #{contexts.keys}"
|
|
49
51
|
end
|
|
50
52
|
|
|
51
|
-
return unless block_given?
|
|
52
|
-
|
|
53
53
|
retriable(context_options_for(context_key, options), &)
|
|
54
54
|
end
|
|
55
55
|
|
|
@@ -97,6 +97,9 @@ module Retriable
|
|
|
97
97
|
rescue *exception_list => e
|
|
98
98
|
raise unless retriable_exception?(e, on, exception_list, retry_if)
|
|
99
99
|
|
|
100
|
+
# On the final attempt `interval_for` returns nil (no next retry), and
|
|
101
|
+
# `on_retry` intentionally fires before the give-up check below, so it
|
|
102
|
+
# receives `interval: nil`. See the on_retry/on_give_up README contract.
|
|
100
103
|
interval = interval_for.call(try - 1)
|
|
101
104
|
call_on_retry(on_retry, e, try, elapsed_time.call, interval)
|
|
102
105
|
|
data/sig/retriable.rbs
CHANGED
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
module Retriable
|
|
2
2
|
VERSION: String
|
|
3
|
-
|
|
3
|
+
OVERRIDE_THREAD_KEY: Symbol
|
|
4
|
+
|
|
5
|
+
def self.configure: () { (Config) -> void } -> void
|
|
6
|
+
def self.config: () -> Config
|
|
7
|
+
def self.with_override: (Hash[Symbol, untyped] options) { () -> untyped } -> untyped
|
|
8
|
+
def self.with_context: (Symbol context_key, ?Hash[Symbol, untyped] options) { (Integer) -> untyped } -> untyped
|
|
9
|
+
def self.retriable: (?Hash[Symbol, untyped] options) { (Integer) -> untyped } -> untyped
|
|
10
|
+
|
|
11
|
+
class Config
|
|
12
|
+
ATTRIBUTES: Array[Symbol]
|
|
13
|
+
|
|
14
|
+
attr_accessor tries: Numeric
|
|
15
|
+
attr_accessor base_interval: Numeric
|
|
16
|
+
attr_accessor max_interval: Numeric
|
|
17
|
+
attr_accessor rand_factor: Numeric
|
|
18
|
+
attr_accessor multiplier: Numeric
|
|
19
|
+
attr_accessor sleep_disabled: bool
|
|
20
|
+
attr_accessor max_elapsed_time: Numeric?
|
|
21
|
+
attr_accessor intervals: Array[Numeric]?
|
|
22
|
+
attr_accessor on: untyped
|
|
23
|
+
attr_accessor retry_if: untyped
|
|
24
|
+
attr_accessor on_retry: untyped
|
|
25
|
+
attr_accessor on_give_up: untyped
|
|
26
|
+
attr_accessor contexts: Hash[Symbol, untyped]
|
|
27
|
+
|
|
28
|
+
def initialize: (?Hash[Symbol, untyped] opts) -> void
|
|
29
|
+
def to_h: () -> Hash[Symbol, untyped]
|
|
30
|
+
def validate!: () -> void
|
|
31
|
+
end
|
|
4
32
|
end
|
data/spec/config_spec.rb
CHANGED
|
@@ -163,4 +163,50 @@ describe Retriable::Config do
|
|
|
163
163
|
.to raise_error(ArgumentError, /on must be an Exception class/)
|
|
164
164
|
end
|
|
165
165
|
end
|
|
166
|
+
|
|
167
|
+
context "callable option validation" do
|
|
168
|
+
%i[retry_if on_retry on_give_up].each do |opt|
|
|
169
|
+
it "accepts a callable for #{opt}" do
|
|
170
|
+
expect { described_class.new(opt => ->(*) {}) }.not_to raise_error
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it "accepts nil and false for #{opt}" do
|
|
174
|
+
expect { described_class.new(opt => nil) }.not_to raise_error
|
|
175
|
+
expect { described_class.new(opt => false) }.not_to raise_error
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it "rejects a non-callable truthy value for #{opt}" do
|
|
179
|
+
expect { described_class.new(opt => 5) }.to raise_error(ArgumentError, /#{opt}.*#call/)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
context "context structure validation" do
|
|
185
|
+
it "rejects a context whose options contain a nested :contexts key" do
|
|
186
|
+
expect { described_class.new(contexts: { api: { contexts: {} } }) }
|
|
187
|
+
.to raise_error(ArgumentError, /contexts is not a valid option/)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it "rejects a context with an unknown option key" do
|
|
191
|
+
expect { described_class.new(contexts: { api: { does_not_exist: 1 } }) }
|
|
192
|
+
.to raise_error(ArgumentError, /does_not_exist is not a valid option/)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it "validates context structure even when intervals is provided" do
|
|
196
|
+
expect { described_class.new(intervals: [0.1], contexts: { api: { contexts: {} } }) }
|
|
197
|
+
.to raise_error(ArgumentError, /contexts is not a valid option/)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it "accepts a non-Hash context value (treated as empty options)" do
|
|
201
|
+
expect { described_class.new(contexts: { broken: nil }) }.not_to raise_error
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "accepts nil contexts" do
|
|
205
|
+
expect { described_class.new(contexts: nil) }.not_to raise_error
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "accepts a valid context" do
|
|
209
|
+
expect { described_class.new(contexts: { api: { tries: 3, base_interval: 1.0 } }) }.not_to raise_error
|
|
210
|
+
end
|
|
211
|
+
end
|
|
166
212
|
end
|
data/spec/retriable_spec.rb
CHANGED
|
@@ -52,9 +52,9 @@ describe Retriable do
|
|
|
52
52
|
|
|
53
53
|
# These two specs lock in the anonymous block forwarding (`&`) semantics
|
|
54
54
|
# across both delegation layers: Kernel#retriable_with_context ->
|
|
55
|
-
# Retriable.with_context. If the `&` is dropped at either layer, the
|
|
56
|
-
#
|
|
57
|
-
#
|
|
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
58
|
it "forwards a block through Kernel#retriable_with_context" do
|
|
59
59
|
require_relative "../lib/retriable/core_ext/kernel"
|
|
60
60
|
Retriable.configure { |c| c.contexts[:sql] = { tries: 1 } }
|
|
@@ -64,13 +64,23 @@ describe Retriable do
|
|
|
64
64
|
expect(@tries).to eq(1)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
it "
|
|
67
|
+
it "raises an ArgumentError when Kernel#retriable_with_context is called without a block" do
|
|
68
68
|
require_relative "../lib/retriable/core_ext/kernel"
|
|
69
69
|
Retriable.configure { |c| c.contexts[:sql] = { tries: 1 } }
|
|
70
70
|
|
|
71
|
-
expect
|
|
71
|
+
expect { retriable_with_context(:sql) }
|
|
72
|
+
.to raise_error(ArgumentError, /with_context requires a block/)
|
|
72
73
|
expect(@tries).to eq(0)
|
|
73
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
|
|
74
84
|
end
|
|
75
85
|
|
|
76
86
|
context "#retriable" do
|
|
@@ -1248,8 +1258,15 @@ describe Retriable do
|
|
|
1248
1258
|
expect(@tries).to eq(1)
|
|
1249
1259
|
end
|
|
1250
1260
|
|
|
1251
|
-
it "
|
|
1252
|
-
expect
|
|
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/)
|
|
1253
1270
|
expect(@tries).to eq(0)
|
|
1254
1271
|
end
|
|
1255
1272
|
|
|
@@ -1290,6 +1307,13 @@ describe Retriable do
|
|
|
1290
1307
|
expect(@tries).to eq(1)
|
|
1291
1308
|
end
|
|
1292
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
|
+
|
|
1293
1317
|
it "invokes on_give_up configured on a context" do
|
|
1294
1318
|
callback_called = false
|
|
1295
1319
|
described_class.configure do |c|
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: retriable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jack Chu
|
|
@@ -65,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
65
65
|
- !ruby/object:Gem::Version
|
|
66
66
|
version: '0'
|
|
67
67
|
requirements: []
|
|
68
|
-
rubygems_version:
|
|
68
|
+
rubygems_version: 3.6.9
|
|
69
69
|
specification_version: 4
|
|
70
70
|
summary: Retriable is a simple DSL to retry failed code blocks with randomized exponential
|
|
71
71
|
backoff
|