retryable 2.0.4 → 3.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 +7 -0
- data/CHANGELOG.md +35 -1
- data/Gemfile +20 -0
- data/README.md +136 -66
- data/Rakefile +2 -2
- data/lib/retryable/configuration.rb +18 -22
- data/lib/retryable/version.rb +33 -9
- data/lib/retryable.rb +48 -27
- data/retryable.gemspec +29 -18
- data/spec/retryable/configuration_spec.rb +55 -0
- data/spec/retryable/version_spec.rb +32 -0
- data/spec/retryable_logging_spec.rb +34 -0
- data/spec/retryable_spec.rb +192 -0
- data/spec/retryable_with_context_spec.rb +56 -0
- data/spec/spec_helper.rb +10 -17
- data/spec/support/counter.rb +58 -0
- metadata +53 -66
- data/spec/lib/configuration_spec.rb +0 -39
- data/spec/lib/retryable_spec.rb +0 -151
data/retryable.gemspec
CHANGED
@@ -1,20 +1,31 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# rubocop:disable Style/ExpandPathArguments
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
# rubocop:enable Style/ExpandPathArguments
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
5
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
+
require 'retryable/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'retryable'
|
10
|
+
spec.version = Retryable::Version
|
11
|
+
spec.authors = ['Nikita Fedyashev', 'Carlo Zottmann', 'Chu Yeow']
|
12
|
+
spec.email = ['nfedyashev@gmail.com']
|
13
|
+
|
14
|
+
spec.summary = 'Retrying code blocks in Ruby'
|
15
|
+
spec.description = spec.summary
|
16
|
+
spec.homepage = 'http://github.com/nfedyashev/retryable'
|
17
|
+
spec.metadata = {
|
18
|
+
'changelog_uri' => 'https://github.com/nfedyashev/retryable/blob/master/CHANGELOG.md',
|
19
|
+
'source_code_uri' => 'https://github.com/nfedyashev/retryable/tree/master'
|
20
|
+
}
|
21
|
+
spec.licenses = ['MIT']
|
22
|
+
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
spec.files = Dir['{config,lib,spec}/**/*', '*.md', '*.gemspec', 'Gemfile', 'Rakefile']
|
25
|
+
spec.test_files = spec.files.grep(%r{^spec/})
|
26
|
+
|
27
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 1.9.3')
|
28
|
+
spec.required_rubygems_version = Gem::Requirement.new('>= 1.3.6')
|
29
|
+
|
30
|
+
spec.add_development_dependency 'bundler'
|
20
31
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Retryable do
|
4
|
+
it 'is enabled by default' do
|
5
|
+
expect(described_class).to be_enabled
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'could be disabled' do
|
9
|
+
described_class.disable
|
10
|
+
expect(described_class).not_to be_enabled
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when disabled' do
|
14
|
+
before do
|
15
|
+
described_class.disable
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'could be re-enabled' do
|
19
|
+
described_class.enable
|
20
|
+
expect(described_class).to be_enabled
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when configured locally' do
|
25
|
+
it 'does not affect the original global config' do
|
26
|
+
new_sleep = 2
|
27
|
+
original_sleep = described_class.configuration.send(:sleep)
|
28
|
+
|
29
|
+
expect(original_sleep).not_to eq(new_sleep)
|
30
|
+
|
31
|
+
counter(tries: 2, sleep: new_sleep) do |tries, ex|
|
32
|
+
raise StandardError if tries < 1
|
33
|
+
end
|
34
|
+
|
35
|
+
actual = described_class.configuration.send(:sleep)
|
36
|
+
expect(actual).to eq(original_sleep)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when configured globally with custom sleep parameter' do
|
41
|
+
it 'passes retry count and exception on retry' do
|
42
|
+
expect(Kernel).to receive(:sleep).once.with(3)
|
43
|
+
|
44
|
+
described_class.configure do |config|
|
45
|
+
config.sleep = 3
|
46
|
+
end
|
47
|
+
|
48
|
+
counter(tries: 2) do |tries, ex|
|
49
|
+
expect(ex.class).to eq(StandardError) if tries > 0
|
50
|
+
raise StandardError if tries < 1
|
51
|
+
end
|
52
|
+
expect(counter.count).to eq(2)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Retryable::Version do
|
4
|
+
before do
|
5
|
+
allow(described_class).to receive(:major).and_return(2)
|
6
|
+
allow(described_class).to receive(:minor).and_return(0)
|
7
|
+
allow(described_class).to receive(:patch).and_return(4)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '.to_h' do
|
11
|
+
it 'returns a hash with the right values' do
|
12
|
+
expect(described_class.to_h).to be_a Hash
|
13
|
+
expect(described_class.to_h[:major]).to eq(2)
|
14
|
+
expect(described_class.to_h[:minor]).to eq(0)
|
15
|
+
expect(described_class.to_h[:patch]).to eq(4)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.to_a' do
|
20
|
+
it 'returns an array with the right values' do
|
21
|
+
expect(described_class.to_a).to be_an Array
|
22
|
+
expect(described_class.to_a).to eq([2, 0, 4])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '.to_s' do
|
27
|
+
it 'returns a string with the right value' do
|
28
|
+
expect(described_class.to_s).to be_a String
|
29
|
+
expect(described_class.to_s).to eq('2.0.4')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
RSpec.describe Retryable do
|
5
|
+
describe '.retryable' do
|
6
|
+
before do
|
7
|
+
described_class.enable
|
8
|
+
expect(Kernel).to receive(:sleep)
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:retryable) do
|
12
|
+
-> { Retryable.retryable(tries: 2) { |tries| raise StandardError, "because foo" if tries < 1 } }
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'given default configuration' do
|
16
|
+
it 'does not output anything' do
|
17
|
+
expect { retryable.call }.not_to output.to_stdout_from_any_process
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'given custom STDOUT logger config option' do
|
22
|
+
it 'does not output anything' do
|
23
|
+
described_class.configure do |config|
|
24
|
+
config.log_method = lambda do |retries, exception|
|
25
|
+
Logger.new(STDOUT).debug("[Attempt ##{retries}] Retrying because [#{exception.class} - #{exception.message}]: #{exception.backtrace.first(5).join(' | ')}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
expect { retryable.call }.to output(/\[Attempt #1\] Retrying because \[StandardError - because foo\]/).to_stdout_from_any_process
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,192 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
RSpec.describe Retryable do
|
5
|
+
describe '.retryable' do
|
6
|
+
before do
|
7
|
+
described_class.enable
|
8
|
+
@attempt = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'catch StandardError only by default' do
|
12
|
+
expect do
|
13
|
+
counter(tries: 2) { |tries| raise Exception if tries < 1 }
|
14
|
+
end.to raise_error Exception
|
15
|
+
expect(counter.count).to eq(1)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'retries on default exception' do
|
19
|
+
expect(Kernel).to receive(:sleep).once.with(1)
|
20
|
+
|
21
|
+
counter(tries: 2) { |tries| raise StandardError if tries < 1 }
|
22
|
+
expect(counter.count).to eq(2)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'does not retry if disabled' do
|
26
|
+
described_class.disable
|
27
|
+
|
28
|
+
expect do
|
29
|
+
counter(tries: 2) { raise }
|
30
|
+
end.to raise_error RuntimeError
|
31
|
+
expect(counter.count).to eq(1)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'executes *ensure* clause' do
|
35
|
+
ensure_cb = proc do |retries|
|
36
|
+
expect(retries).to eq(0)
|
37
|
+
end
|
38
|
+
|
39
|
+
described_class.retryable(ensure: ensure_cb) {}
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'passes retry count and exception on retry' do
|
43
|
+
expect(Kernel).to receive(:sleep).once.with(1)
|
44
|
+
|
45
|
+
counter(tries: 2) do |tries, ex|
|
46
|
+
expect(ex.class).to eq(StandardError) if tries > 0
|
47
|
+
raise StandardError if tries < 1
|
48
|
+
end
|
49
|
+
expect(counter.count).to eq(2)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'makes another try if exception is covered by :on' do
|
53
|
+
allow(Kernel).to receive(:sleep)
|
54
|
+
counter(on: [StandardError, ArgumentError, RuntimeError]) do |tries|
|
55
|
+
raise ArgumentError if tries < 1
|
56
|
+
end
|
57
|
+
expect(counter.count).to eq(2)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'does not retry on :not exception which is covered by Array' do
|
61
|
+
expect do
|
62
|
+
counter(not: [RuntimeError, IndexError]) { |tries| raise RuntimeError if tries < 1 }
|
63
|
+
end.to raise_error RuntimeError
|
64
|
+
expect(counter.count).to eq(1)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'does not try on unexpected exception' do
|
68
|
+
allow(Kernel).to receive(:sleep)
|
69
|
+
expect do
|
70
|
+
counter(on: RuntimeError) { |tries| raise StandardError if tries < 1 }
|
71
|
+
end.to raise_error StandardError
|
72
|
+
expect(counter.count).to eq(1)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'retries three times' do
|
76
|
+
allow(Kernel).to receive(:sleep)
|
77
|
+
counter(tries: 3) { |tries| raise StandardError if tries < 2 }
|
78
|
+
expect(counter.count).to eq(3)
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'infinite retries' do
|
82
|
+
example 'with magic constant' do
|
83
|
+
expect do
|
84
|
+
Timeout.timeout(3) do
|
85
|
+
counter(tries: :infinite, sleep: 0.1) { raise StandardError }
|
86
|
+
end
|
87
|
+
end.to raise_error Timeout::Error
|
88
|
+
|
89
|
+
expect(counter.count).to be > 10
|
90
|
+
end
|
91
|
+
|
92
|
+
example 'with native infinity data type' do
|
93
|
+
expect do
|
94
|
+
require 'bigdecimal'
|
95
|
+
|
96
|
+
tries = [Float::INFINITY, BigDecimal::INFINITY, BigDecimal("1.0") / BigDecimal("0.0")]
|
97
|
+
Timeout.timeout(3) do
|
98
|
+
counter(tries: tries.sample, sleep: 0.1) { raise StandardError }
|
99
|
+
end
|
100
|
+
end.to raise_error Timeout::Error
|
101
|
+
|
102
|
+
expect(counter.count).to be > 10
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'executes exponential backoff scheme for :sleep option' do
|
107
|
+
[1, 4, 16, 64].each { |i| expect(Kernel).to receive(:sleep).once.ordered.with(i) }
|
108
|
+
expect do
|
109
|
+
described_class.retryable(tries: 5, sleep: ->(n) { 4**n }) { raise RangeError }
|
110
|
+
end.to raise_error RangeError
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'calls :sleep_method option' do
|
114
|
+
sleep_method = double
|
115
|
+
expect(sleep_method).to receive(:call).twice
|
116
|
+
expect do
|
117
|
+
described_class.retryable(tries: 3, sleep_method: sleep_method) { |tries| raise RangeError if tries < 9 }
|
118
|
+
end.to raise_error RangeError
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'does not retry any exception if :on is empty list' do
|
122
|
+
expect do
|
123
|
+
counter(on: []) { raise }
|
124
|
+
end.to raise_error RuntimeError
|
125
|
+
expect(counter.count).to eq(1)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'catches an exception that matches the regex' do
|
129
|
+
expect(Kernel).to receive(:sleep).once.with(1)
|
130
|
+
counter(matching: /IO timeout/) { |c, _e| raise 'yo, IO timeout!' if c == 0 }
|
131
|
+
expect(counter.count).to eq(2)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'does not catch an exception that does not match the regex' do
|
135
|
+
expect(Kernel).not_to receive(:sleep)
|
136
|
+
expect do
|
137
|
+
counter(matching: /TimeError/) { raise 'yo, IO timeout!' }
|
138
|
+
end.to raise_error RuntimeError
|
139
|
+
expect(counter.count).to eq(1)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'catches an exception in the list of matches' do
|
143
|
+
expect(Kernel).to receive(:sleep).once.with(1)
|
144
|
+
counter(matching: [/IO timeout/, 'IO tymeout']) { |c, _e| raise 'yo, IO timeout!' if c == 0 }
|
145
|
+
expect(counter.count).to eq(2)
|
146
|
+
|
147
|
+
expect(Kernel).to receive(:sleep).once.with(1)
|
148
|
+
counter(matching: [/IO timeout/, 'IO tymeout']) { |c, _e| raise 'yo, IO tymeout!' if c == 0 }
|
149
|
+
expect(counter.count).to eq(4)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'does not allow invalid type of matching option' do
|
153
|
+
expect do
|
154
|
+
described_class.retryable(matching: 1) { raise 'this is invaid type of matching iotion' }
|
155
|
+
end.to raise_error ArgumentError, ':matching must be a string or regex'
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'does not allow invalid options' do
|
159
|
+
expect do
|
160
|
+
described_class.retryable(bad_option: 2) { raise 'this is bad' }
|
161
|
+
end.to raise_error ArgumentError, '[Retryable] Invalid options: bad_option'
|
162
|
+
end
|
163
|
+
|
164
|
+
# rubocop:disable Rspec/InstanceVariable
|
165
|
+
it 'accepts a callback to run after an exception is rescued' do
|
166
|
+
expect do
|
167
|
+
described_class.retryable(sleep: 0, exception_cb: proc { |e| @raised = e.to_s }) do |tries|
|
168
|
+
raise StandardError, 'this is fun!' if tries < 1
|
169
|
+
end
|
170
|
+
end.not_to raise_error
|
171
|
+
|
172
|
+
expect(@raised).to eq('this is fun!')
|
173
|
+
end
|
174
|
+
# rubocop:enable Rspec/InstanceVariable
|
175
|
+
|
176
|
+
it 'does not retry on :not exception' do
|
177
|
+
expect do
|
178
|
+
counter(not: RuntimeError) { |tries| raise RuntimeError if tries < 1 }
|
179
|
+
end.to raise_error RuntimeError
|
180
|
+
expect(counter.count).to eq(1)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'gives precidence for :not over :on' do
|
184
|
+
expect do
|
185
|
+
counter(sleep: 0, tries: 3, on: StandardError, not: IndexError) do |tries|
|
186
|
+
raise tries >= 1 ? IndexError : StandardError
|
187
|
+
end
|
188
|
+
end.to raise_error IndexError
|
189
|
+
expect(counter.count).to eq(2)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Retryable do
|
4
|
+
describe '.with_context' do
|
5
|
+
before do
|
6
|
+
described_class.enable
|
7
|
+
@attempt = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'properly checks context configuration' do
|
11
|
+
expect do
|
12
|
+
described_class.with_context(:foo) {}
|
13
|
+
end.to raise_error ArgumentError, 'foo not found in Retryable.configuration.contexts. Available contexts: []'
|
14
|
+
|
15
|
+
expect do
|
16
|
+
described_class.retryable_with_context(:bar) {}
|
17
|
+
end.to raise_error ArgumentError, 'bar not found in Retryable.configuration.contexts. Available contexts: []'
|
18
|
+
|
19
|
+
expect do
|
20
|
+
described_class.configure do |config|
|
21
|
+
config.contexts[:faulty_service] = {
|
22
|
+
sleep: 3
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
described_class.retryable_with_context(:baz) {}
|
27
|
+
end.to raise_error ArgumentError, 'baz not found in Retryable.configuration.contexts. Available contexts: [:faulty_service]'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'properly fetches context options' do
|
31
|
+
allow(Kernel).to receive(:sleep)
|
32
|
+
|
33
|
+
described_class.configure do |config|
|
34
|
+
config.contexts[:faulty_service] = {
|
35
|
+
tries: 3
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
c = counter_with_context(:faulty_service) { |tries| raise StandardError if tries < 2 }
|
40
|
+
expect(c.count).to eq(3)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'properly overrides context options with local arguments' do
|
44
|
+
allow(Kernel).to receive(:sleep)
|
45
|
+
|
46
|
+
described_class.configure do |config|
|
47
|
+
config.contexts[:faulty_service] = {
|
48
|
+
tries: 1
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
c = counter_with_context(:faulty_service, tries: 3) { |tries| raise StandardError if tries < 2 }
|
53
|
+
expect(c.count).to eq(3)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,25 +1,18 @@
|
|
1
|
-
require
|
2
|
-
require '
|
3
|
-
require 'pry'
|
1
|
+
require 'retryable'
|
2
|
+
require 'simplecov'
|
4
3
|
|
5
|
-
|
6
|
-
|
4
|
+
# rubocop:disable Style/ExpandPathArguments
|
5
|
+
Dir.glob(File.expand_path('../support/**/*.rb', __FILE__), &method(:require))
|
6
|
+
# rubocop:enable Style/ExpandPathArguments
|
7
7
|
|
8
|
-
|
9
|
-
reset_config
|
10
|
-
end
|
8
|
+
SimpleCov.start
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
return Retryable.retryable(*opts) do |*args|
|
15
|
-
@try_count += 1
|
16
|
-
yield *args
|
17
|
-
end
|
18
|
-
end
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.disable_monkey_patching!
|
19
12
|
|
20
|
-
|
13
|
+
config.include(Counter)
|
21
14
|
|
22
|
-
|
15
|
+
config.before do
|
23
16
|
Retryable.configuration = nil
|
24
17
|
end
|
25
18
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Counter
|
2
|
+
class PlainGenerator
|
3
|
+
attr_reader :count
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
@options = options
|
7
|
+
@count = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def around
|
11
|
+
Retryable.retryable(@options) do |*arguments|
|
12
|
+
increment
|
13
|
+
yield(*arguments)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def increment
|
20
|
+
@count += 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class GeneratorWithContext
|
25
|
+
attr_reader :count
|
26
|
+
|
27
|
+
def initialize(context_key, options)
|
28
|
+
@context_key = context_key
|
29
|
+
@count = 0
|
30
|
+
@options = options
|
31
|
+
end
|
32
|
+
|
33
|
+
def around
|
34
|
+
Retryable.with_context(@context_key, @options) do |*arguments|
|
35
|
+
increment
|
36
|
+
yield(*arguments)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def increment
|
43
|
+
@count += 1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def counter(options = {}, &block)
|
48
|
+
@counter ||= PlainGenerator.new(options)
|
49
|
+
@counter.around(&block) if block_given?
|
50
|
+
@counter
|
51
|
+
end
|
52
|
+
|
53
|
+
def counter_with_context(context_key, options = {}, &block)
|
54
|
+
@counter_with_context ||= GeneratorWithContext.new(context_key, options)
|
55
|
+
@counter_with_context.around(&block) if block_given?
|
56
|
+
@counter_with_context
|
57
|
+
end
|
58
|
+
end
|
metadata
CHANGED
@@ -1,97 +1,84 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: retryable
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 2
|
8
|
-
- 0
|
9
|
-
- 4
|
10
|
-
version: 2.0.4
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 3.0.5
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- Nikita Fedyashev
|
14
8
|
- Carlo Zottmann
|
15
9
|
- Chu Yeow
|
16
10
|
autorequire:
|
17
11
|
bindir: bin
|
18
12
|
cert_chain: []
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
dependencies:
|
23
|
-
- !ruby/object:Gem::Dependency
|
13
|
+
date: 2019-11-11 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
24
16
|
name: bundler
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
- !ruby/object:Gem::Version
|
31
|
-
hash: 15
|
32
|
-
segments:
|
33
|
-
- 1
|
34
|
-
- 0
|
35
|
-
version: "1.0"
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ">="
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
36
22
|
type: :development
|
37
|
-
|
38
|
-
|
39
|
-
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '0'
|
29
|
+
description: Retrying code blocks in Ruby
|
30
|
+
email:
|
31
|
+
- nfedyashev@gmail.com
|
40
32
|
executables: []
|
41
|
-
|
42
33
|
extensions: []
|
43
|
-
|
44
34
|
extra_rdoc_files: []
|
45
|
-
|
46
|
-
files:
|
35
|
+
files:
|
47
36
|
- CHANGELOG.md
|
37
|
+
- Gemfile
|
48
38
|
- LICENSE.md
|
49
39
|
- README.md
|
50
40
|
- Rakefile
|
51
|
-
- retryable.
|
41
|
+
- lib/retryable.rb
|
52
42
|
- lib/retryable/configuration.rb
|
53
43
|
- lib/retryable/version.rb
|
54
|
-
-
|
55
|
-
- spec/
|
56
|
-
- spec/
|
44
|
+
- retryable.gemspec
|
45
|
+
- spec/retryable/configuration_spec.rb
|
46
|
+
- spec/retryable/version_spec.rb
|
47
|
+
- spec/retryable_logging_spec.rb
|
48
|
+
- spec/retryable_spec.rb
|
49
|
+
- spec/retryable_with_context_spec.rb
|
57
50
|
- spec/spec_helper.rb
|
58
|
-
|
51
|
+
- spec/support/counter.rb
|
59
52
|
homepage: http://github.com/nfedyashev/retryable
|
60
|
-
licenses:
|
53
|
+
licenses:
|
61
54
|
- MIT
|
55
|
+
metadata:
|
56
|
+
changelog_uri: https://github.com/nfedyashev/retryable/blob/master/CHANGELOG.md
|
57
|
+
source_code_uri: https://github.com/nfedyashev/retryable/tree/master
|
62
58
|
post_install_message:
|
63
59
|
rdoc_options: []
|
64
|
-
|
65
|
-
require_paths:
|
60
|
+
require_paths:
|
66
61
|
- lib
|
67
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
-
|
69
|
-
requirements:
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
70
64
|
- - ">="
|
71
|
-
- !ruby/object:Gem::Version
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
version: "0"
|
76
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
-
none: false
|
78
|
-
requirements:
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 1.9.3
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
79
69
|
- - ">="
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
hash: 23
|
82
|
-
segments:
|
83
|
-
- 1
|
84
|
-
- 3
|
85
|
-
- 6
|
70
|
+
- !ruby/object:Gem::Version
|
86
71
|
version: 1.3.6
|
87
72
|
requirements: []
|
88
|
-
|
89
|
-
rubyforge_project:
|
90
|
-
rubygems_version: 1.6.2
|
73
|
+
rubygems_version: 3.0.3
|
91
74
|
signing_key:
|
92
|
-
specification_version:
|
93
|
-
summary:
|
94
|
-
test_files:
|
95
|
-
- spec/
|
96
|
-
- spec/
|
75
|
+
specification_version: 4
|
76
|
+
summary: Retrying code blocks in Ruby
|
77
|
+
test_files:
|
78
|
+
- spec/retryable/configuration_spec.rb
|
79
|
+
- spec/retryable/version_spec.rb
|
80
|
+
- spec/retryable_logging_spec.rb
|
81
|
+
- spec/retryable_spec.rb
|
82
|
+
- spec/retryable_with_context_spec.rb
|
97
83
|
- spec/spec_helper.rb
|
84
|
+
- spec/support/counter.rb
|