pester 0.2.0 → 1.0.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/.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: []
|