pester 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +18 -3
- data/README.md +11 -11
- data/lib/pester.rb +21 -18
- data/lib/pester/version.rb +1 -1
- data/pester.gemspec +3 -3
- data/spec/pester_spec.rb +8 -5
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1f5ea594c429a04f50888d1869f4e829db2995a
|
4
|
+
data.tar.gz: 18eb22f587bcc68d708872c085d6f16ab746fc9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6150fbb4bf53a271945524e42b1672951e018a18a1505c61407dbed0f322862e4181260502009695d87e9d21aac3587c42e5b2ad6766b1f9a8622bcea62c5f7e
|
7
|
+
data.tar.gz: 4cb89d7f6d88e2591efbca5f57b513e5bb5111f902f33225c4b827174ef60e8902738e9be6750058f8d7581b16fa0b05bcb82a7e1fa16bb56c24ba8214094d3a
|
data/.rubocop.yml
CHANGED
@@ -1,14 +1,29 @@
|
|
1
1
|
Metrics/AbcSize:
|
2
|
-
Max:
|
2
|
+
Max: 27
|
3
|
+
|
4
|
+
Metrics/CyclomaticComplexity:
|
5
|
+
Max: 7
|
3
6
|
|
4
7
|
Metrics/LineLength:
|
5
8
|
Max: 140
|
6
9
|
|
10
|
+
Metrics/MethodLength:
|
11
|
+
Max: 23
|
12
|
+
|
13
|
+
Metrics/PerceivedComplexity:
|
14
|
+
Max: 9
|
15
|
+
|
7
16
|
Style/Documentation:
|
8
|
-
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Style/ModuleFunction:
|
20
|
+
Enabled: false
|
9
21
|
|
10
22
|
Style/RaiseArgs:
|
11
|
-
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Style/SpaceAroundOperators:
|
26
|
+
Enabled: false
|
12
27
|
|
13
28
|
Style/SignalException:
|
14
29
|
Enabled: false
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@ From the outset, the goal of Pester is to offer a simple interface. For example:
|
|
11
11
|
|
12
12
|
irb(main):001:0> require 'pester'
|
13
13
|
=> true
|
14
|
-
irb(main):002:0> Pester.
|
14
|
+
irb(main):002:0> Pester.retry_constant { fail 'derp' }
|
15
15
|
W, [2015-04-04T10:37:46.413158 #87600] WARN -- : Failure encountered: derp, backing off and trying again 3 more times. etc etc
|
16
16
|
|
17
17
|
will retry the block--which always fails--until Pester has exhausted its amount of retries. With no options provided, this will sleep for a constant number of seconds between attempts.
|
@@ -24,7 +24,7 @@ Pester's basic retry behaviors are defined by three options:
|
|
24
24
|
|
25
25
|
`delay_interval` is the unit, in seconds, that will be delayed between attempts. Normally, this is just the total number of seconds, but it can change with other `Behavior`s. `max_attempts` is the number of tries Pester will make, including the initial one. If this is set to 1, Pester will basically not retry; less than 1, it will not even bother executing the block:
|
26
26
|
|
27
|
-
irb(main):001:0> Pester.
|
27
|
+
irb(main):001:0> Pester.retry_constant(max_attempts: 0) { puts 'Trying...'; fail 'derp' }
|
28
28
|
=> nil
|
29
29
|
|
30
30
|
`on_retry` defines the behavior between retries, which can either be a custom block of code, or one of the predefined `Behavior`s, specifically in `Pester::Behaviors::Sleep`. If passed an empty lambda/block, Pester will immediately retry. When writing a custom behavior, `on_retry` expects a block that can be called with two parameters, `attempt_num`, and `delay_interval`, the idea being that these will mostly be used to define a function that determines just how long to sleep between attempts.
|
@@ -35,7 +35,7 @@ Three behaviors are provided out-of-the box:
|
|
35
35
|
* `Linear` simply multiplies `attempt_num` by `delay_interval` and sleeps for that many seconds
|
36
36
|
* `Exponential` sleeps for 2<sup>(`attempt_num` - 1)</sup> * `delay_interval` seconds
|
37
37
|
|
38
|
-
All three are available either by passing the behaviors to `on_retry`, or by calling the increasingly-verbosely-named `
|
38
|
+
All three are available either by passing the behaviors to `on_retry`, or by calling the increasingly-verbosely-named `retry_constant` (constant), `retry_with_backoff` (linear), or `retry_with_exponential_backoff` (exponential). `retry` by itself *will not* actually retry anything, unless provided with an `on_retry` function, either per-call or in the relevant environment.
|
39
39
|
|
40
40
|
Pester does log retry attempts (see below), however custom retry behavior that wraps existing `Behavior`s may be appropriate for logging custom information, incrementing statsd counters, etc. Also of note, different loggers can be passed per-call via the `logger` option.
|
41
41
|
|
@@ -51,7 +51,7 @@ Pester can be configured to be picky about what it chooses to retry and what it
|
|
51
51
|
|
52
52
|
The first two are mutually-exclusive whitelist and blacklists, both taking either a single error class or an array. Raising an error not covered by `retry_error_classes` (whitelist) causes it to immediately fail:
|
53
53
|
|
54
|
-
irb(main):002:0> Pester.
|
54
|
+
irb(main):002:0> Pester.retry_constant(retry_error_classes: NotImplementedError) do
|
55
55
|
puts 'Trying...'
|
56
56
|
fail 'derp'
|
57
57
|
end
|
@@ -60,7 +60,7 @@ The first two are mutually-exclusive whitelist and blacklists, both taking eithe
|
|
60
60
|
|
61
61
|
Raising an error covered by `reraise_error_classes` (blacklist) causes it to immediately fail:
|
62
62
|
|
63
|
-
irb(main):002:0> Pester.
|
63
|
+
irb(main):002:0> Pester.retry_constant(reraise_error_classes: NotImplementedError) do
|
64
64
|
puts 'Trying...'
|
65
65
|
raise NotImplementedError.new('derp')
|
66
66
|
end
|
@@ -69,7 +69,7 @@ Raising an error covered by `reraise_error_classes` (blacklist) causes it to imm
|
|
69
69
|
|
70
70
|
`retry_error_messages` also takes a single string or array, and calls `include?` on the error message. If it matches, the error's retried:
|
71
71
|
|
72
|
-
irb(main):002:0> Pester.
|
72
|
+
irb(main):002:0> Pester.retry_constant(retry_error_messages: 'please') do
|
73
73
|
puts 'Trying...'
|
74
74
|
fail 'retry this, please'
|
75
75
|
end
|
@@ -78,7 +78,7 @@ Raising an error covered by `reraise_error_classes` (blacklist) causes it to imm
|
|
78
78
|
|
79
79
|
Because it calls `include?`, this also works for regexes:
|
80
80
|
|
81
|
-
irb(main):002:0> Pester.
|
81
|
+
irb(main):002:0> Pester.retry_constant(retry_error_messages: /\d/) do
|
82
82
|
puts 'Trying...'
|
83
83
|
fail 'retry this 2'
|
84
84
|
end
|
@@ -92,8 +92,8 @@ Because it calls `include?`, this also works for regexes:
|
|
92
92
|
The easiest way to coordinate sets of Pester options across an app is via environments--these are basically option hashes configured in Pester by name:
|
93
93
|
|
94
94
|
Pester.configure do |c|
|
95
|
-
c.environments[:aws] = { max_attempts: 3, delay_interval: 5 }
|
96
|
-
c.environments[:internal] = { max_attempts: 2, delay_interval: 0 }
|
95
|
+
c.environments[:aws] = { max_attempts: 3, delay_interval: 5, on_retry: Pester::Behaviors::Sleep::Constant }
|
96
|
+
c.environments[:internal] = { max_attempts: 2, delay_interval: 0, on_retry: Pester::Behaviors::Sleep::Constant }
|
97
97
|
end
|
98
98
|
|
99
99
|
This will create two environments, `aws` and `internal`, which allow you to employ different backoff strategies, depending on the usage context. These are employed simply by calling `Pester.environment_name.retry` (where `retry` can also be another helper method):
|
@@ -112,7 +112,7 @@ This will create two environments, `aws` and `internal`, which allow you to empl
|
|
112
112
|
|
113
113
|
Environments can also be merged with retry helper methods:
|
114
114
|
|
115
|
-
Pester.aws.
|
115
|
+
Pester.aws.retry_constant # acts different from
|
116
116
|
Pester.aws.retry_with_exponential_backoff
|
117
117
|
|
118
118
|
where the helper method's `Behavior` will take precedence.
|
@@ -127,7 +127,7 @@ Pester will write retry and exhaustion information into your logs, by default us
|
|
127
127
|
|
128
128
|
And thus:
|
129
129
|
|
130
|
-
irb(main):002:0> Pester.
|
130
|
+
irb(main):002:0> Pester.retry_constant(delay_interval: 1) { puts 'Trying...'; fail 'derp' }
|
131
131
|
Trying...
|
132
132
|
Trying...
|
133
133
|
Trying...
|
data/lib/pester.rb
CHANGED
@@ -5,22 +5,27 @@ require 'pester/config'
|
|
5
5
|
require 'pester/version'
|
6
6
|
|
7
7
|
module Pester
|
8
|
-
|
8
|
+
extend self
|
9
|
+
attr_accessor :environments
|
10
|
+
|
11
|
+
def configure(&block)
|
9
12
|
Config.configure(&block)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
+
|
14
|
+
return if Config.environments.nil?
|
15
|
+
|
16
|
+
valid_environments = Config.environments.select { |_, e| e.is_a?(Hash) }
|
17
|
+
@environments = Hash[valid_environments.map { |k, e| [k.to_sym, Environment.new(e)] }]
|
13
18
|
end
|
14
19
|
|
15
|
-
def
|
20
|
+
def retry_constant(options = {}, &block)
|
16
21
|
retry_action(options.merge(on_retry: Behaviors::Sleep::Constant), &block)
|
17
22
|
end
|
18
23
|
|
19
|
-
def
|
24
|
+
def retry_with_backoff(options = {}, &block)
|
20
25
|
retry_action(options.merge(on_retry: Behaviors::Sleep::Linear), &block)
|
21
26
|
end
|
22
27
|
|
23
|
-
def
|
28
|
+
def retry_with_exponential_backoff(options = {}, &block)
|
24
29
|
retry_action({ delay_interval: 1 }.merge(options).merge(on_retry: Behaviors::Sleep::Exponential), &block)
|
25
30
|
end
|
26
31
|
|
@@ -49,7 +54,7 @@ module Pester
|
|
49
54
|
# FileUtils.rm_r(directory)
|
50
55
|
# end
|
51
56
|
|
52
|
-
def
|
57
|
+
def retry_action(opts = {}, &block)
|
53
58
|
merge_defaults(opts)
|
54
59
|
if opts[:retry_error_classes] && opts[:reraise_error_classes]
|
55
60
|
fail 'You can only have one of retry_error_classes or reraise_error_classes'
|
@@ -79,25 +84,21 @@ module Pester
|
|
79
84
|
nil
|
80
85
|
end
|
81
86
|
|
82
|
-
def respond_to?(method_sym)
|
87
|
+
def respond_to?(method_sym, options = {}, &block)
|
83
88
|
super || Config.environments.key?(method_sym)
|
84
89
|
end
|
85
90
|
|
86
|
-
def method_missing(method_sym)
|
87
|
-
if
|
88
|
-
|
91
|
+
def method_missing(method_sym, options = {}, &block)
|
92
|
+
if @environments.key?(method_sym)
|
93
|
+
@environments[method_sym]
|
89
94
|
else
|
90
95
|
super
|
91
96
|
end
|
92
97
|
end
|
93
98
|
|
94
|
-
class << self
|
95
|
-
attr_accessor :environments
|
96
|
-
end
|
97
|
-
|
98
99
|
private
|
99
100
|
|
100
|
-
def
|
101
|
+
def should_retry?(e, opts = {})
|
101
102
|
retry_error_classes = opts[:retry_error_classes]
|
102
103
|
retry_error_messages = opts[:retry_error_messages]
|
103
104
|
reraise_error_classes = opts[:reraise_error_classes]
|
@@ -117,7 +118,7 @@ module Pester
|
|
117
118
|
end
|
118
119
|
end
|
119
120
|
|
120
|
-
def
|
121
|
+
def merge_defaults(opts)
|
121
122
|
opts[:retry_error_classes] = opts[:retry_error_classes] ? Array(opts[:retry_error_classes]) : nil
|
122
123
|
opts[:retry_error_messages] = opts[:retry_error_messages] ? Array(opts[:retry_error_messages]) : nil
|
123
124
|
opts[:reraise_error_classes] = opts[:reraise_error_classes] ? Array(opts[:reraise_error_classes]) : nil
|
@@ -127,4 +128,6 @@ module Pester
|
|
127
128
|
opts[:on_max_attempts_exceeded] ||= Behaviors::WarnAndReraise
|
128
129
|
opts[:logger] ||= Config.logger
|
129
130
|
end
|
131
|
+
|
132
|
+
alias_method :retry, :retry_action
|
130
133
|
end
|
data/lib/pester/version.rb
CHANGED
data/pester.gemspec
CHANGED
@@ -10,9 +10,9 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ['marc@lumoslabs.com']
|
11
11
|
spec.summary = 'Common block-based retry for external calls.'
|
12
12
|
spec.description = <<-EOD
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
We found ourselves constantly wrapping network-facing calls with all kinds of bespoke,
|
14
|
+
copied, and rewritten retry logic. This gem is an attempt to unify common behaviors,
|
15
|
+
like simple retry, retry with linear backoff, and retry with exponential backoff.
|
16
16
|
EOD
|
17
17
|
spec.homepage = 'https://github.com/lumoslabs/pester'
|
18
18
|
spec.license = 'MIT'
|
data/spec/pester_spec.rb
CHANGED
@@ -275,22 +275,25 @@ describe '#environments' do
|
|
275
275
|
let(:environment_name) { :abc }
|
276
276
|
let(:options) { { option: 1234 } }
|
277
277
|
|
278
|
-
|
278
|
+
before do
|
279
279
|
Pester.configure do |config|
|
280
280
|
config.environments[environment_name] = options
|
281
281
|
end
|
282
|
+
end
|
282
283
|
|
284
|
+
it 'adds it to the Pester environment list' do
|
283
285
|
expect(Pester.environments.count).to eq(1)
|
284
286
|
end
|
285
287
|
|
286
288
|
it 'contains an Environment with the appropriate options' do
|
287
|
-
Pester.configure do |config|
|
288
|
-
config.environments[environment_name] = options
|
289
|
-
end
|
290
|
-
|
291
289
|
expect(Pester.environments[environment_name].class).to eq(Pester::Environment)
|
292
290
|
expect(Pester.environments[environment_name].options).to eq(options)
|
293
291
|
end
|
292
|
+
|
293
|
+
it 'contains an Environment addressable directly from Pester with the appropriate options' do
|
294
|
+
expect(Pester.send(environment_name).class).to eq(Pester::Environment)
|
295
|
+
expect(Pester.send(environment_name).options).to eq(options)
|
296
|
+
end
|
294
297
|
end
|
295
298
|
end
|
296
299
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pester
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc Bollinger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-05-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -53,9 +53,9 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.2'
|
55
55
|
description: |2
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
We found ourselves constantly wrapping network-facing calls with all kinds of bespoke,
|
57
|
+
copied, and rewritten retry logic. This gem is an attempt to unify common behaviors,
|
58
|
+
like simple retry, retry with linear backoff, and retry with exponential backoff.
|
59
59
|
email:
|
60
60
|
- marc@lumoslabs.com
|
61
61
|
executables: []
|