retry_upto 1.1 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +20 -20
- data/lib/retry_upto.rb +9 -10
- data/test/retry_upto_test.rb +23 -30
- metadata +8 -8
data/README.md
CHANGED
@@ -1,12 +1,18 @@
|
|
1
1
|
# retry_upto: retry with steroids
|
2
2
|
|
3
3
|
If you need to use `retry` in ruby, you probably want to retry some code only a **maximum number of times**,
|
4
|
-
and sometimes **wait
|
4
|
+
and sometimes **wait an interval of time** between the attempts to avoid external resources hiccups or usage limits.
|
5
5
|
|
6
6
|
Therefore **Your Important Code** ends surrounded by counters, conditions and calls to `sleep`.
|
7
7
|
|
8
8
|
This gem deals elegantly with this common scenario:
|
9
9
|
|
10
|
+
retry_upto(5) do
|
11
|
+
# Your Important Code
|
12
|
+
end
|
13
|
+
|
14
|
+
Under Ruby 1.9 it also allows:
|
15
|
+
|
10
16
|
5.times.retry do
|
11
17
|
# Your Important Code
|
12
18
|
end
|
@@ -21,28 +27,18 @@ Retries up to 5 times catching any exception, doesn't wait between attempts:
|
|
21
27
|
|
22
28
|
retry_upto(5)
|
23
29
|
|
24
|
-
###
|
30
|
+
### Time intervals between attempts
|
25
31
|
|
26
|
-
|
32
|
+
For fixed intervals, an `:interval` parameter can be passed indicating the time in seconds.
|
33
|
+
The following example will sleep two seconds between attempts:
|
27
34
|
|
28
35
|
retry_upto(5, :interval => 2)
|
29
36
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
the time between the following attempts (2, 4, 8, ...):
|
34
|
-
|
35
|
-
retry_upto(5, :interval => 1, :growth => 2)
|
37
|
+
For customized intervals, the `:interval` parameter can be a lambda, which will be applied
|
38
|
+
to the number of each attempt. For instance, the following code will sleep 3 seconds after
|
39
|
+
the first attempt, 6 after the second, 9 after the third...
|
36
40
|
|
37
|
-
|
38
|
-
the time between the following attempts (0.5, 0.25, 0.125, ...):
|
39
|
-
|
40
|
-
retry_upto(5, :interval => 1, :growth => 0.5)
|
41
|
-
|
42
|
-
Retries up to 5 times, waits 1 second after the first attempt and increases
|
43
|
-
randomly the time between the following attempts:
|
44
|
-
|
45
|
-
retry_upto(5, :interval => 1, :growth => lambda{ |x| x + rand(3) } )
|
41
|
+
retry_upto(5, :interval => lambda{ |attempt| attempt * 3 })
|
46
42
|
|
47
43
|
### Retrying only when certain Exceptions get raised
|
48
44
|
|
@@ -50,6 +46,10 @@ Retries up to 5 times only after a ZeroDivisionError, raising any other Exceptio
|
|
50
46
|
|
51
47
|
retry_upto(5, :rescue => ZeroDivisionError)
|
52
48
|
|
49
|
+
Retries up to 5 times only after a ZeroDivisionError or a NameError, raising any other Exception:
|
50
|
+
|
51
|
+
retry_upto(5, :rescue => [ZeroDivisionError, NameError])
|
52
|
+
|
53
53
|
All the options described above can be combined together.
|
54
54
|
|
55
55
|
### More sugar!
|
@@ -58,7 +58,7 @@ In ruby 1.9, the `Enumerator` class gets enhanced to use `retry_upto` this way:
|
|
58
58
|
|
59
59
|
5.times.retry
|
60
60
|
|
61
|
-
|
61
|
+
This syntax accepts the same options:
|
62
62
|
|
63
63
|
5.times.retry(:interval => 10)
|
64
64
|
|
@@ -68,7 +68,7 @@ See the LICENSE file included in the distribution.
|
|
68
68
|
|
69
69
|
## Authors
|
70
70
|
|
71
|
-
This gem was born from gists by Raul Murciano, Glenn Gillen, Pedro Belo, Jaime Iniesta, Lleïr Borras and ideas taken from Aitor García Rey.
|
71
|
+
This gem was born from gists by Raul Murciano, Glenn Gillen, Pedro Belo, Jaime Iniesta, Lleïr Borras and ideas taken from Aitor García Rey and Jim Remsik.
|
72
72
|
|
73
73
|
Yes, so many brain cells and so few lines of code. Great, isn't it?
|
74
74
|
|
data/lib/retry_upto.rb
CHANGED
@@ -1,20 +1,19 @@
|
|
1
1
|
# See README.md for usage explanations
|
2
2
|
|
3
|
-
def retry_upto(max_retries = 1,
|
3
|
+
def retry_upto(max_retries = 1, opts = {})
|
4
4
|
yield
|
5
|
-
rescue *(
|
6
|
-
|
7
|
-
|
8
|
-
if
|
9
|
-
|
10
|
-
|
11
|
-
options[:interval] = options[:growth].call(options[:interval])
|
5
|
+
rescue *(opts[:rescue] || Exception)
|
6
|
+
attempt = attempt ? attempt+1 : 1
|
7
|
+
raise if (attempt == max_retries)
|
8
|
+
if interval = opts[:interval]
|
9
|
+
secs = interval.respond_to?(:call) ? interval.call(attempt) : interval
|
10
|
+
sleep(secs)
|
12
11
|
end
|
13
12
|
retry
|
14
13
|
end
|
15
14
|
|
16
15
|
class Enumerator
|
17
|
-
def retry(
|
18
|
-
retry_upto(self.count,
|
16
|
+
def retry(opts = {}, &blk)
|
17
|
+
retry_upto(self.count, opts, &blk)
|
19
18
|
end
|
20
19
|
end
|
data/test/retry_upto_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require './test/test_helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class RetryUptoTest < MiniTest::Unit::TestCase
|
4
4
|
|
5
5
|
class FooError < Exception; end
|
6
6
|
class BarError < Exception; end
|
@@ -12,16 +12,16 @@ class Retry_uptoTest < MiniTest::Unit::TestCase
|
|
12
12
|
@bars = 0
|
13
13
|
end
|
14
14
|
|
15
|
-
# raises FooError only in the
|
15
|
+
# raises FooError only in the 3 first calls
|
16
16
|
def foo!
|
17
17
|
@foos += 1
|
18
|
-
raise FooError if @foos <
|
18
|
+
raise FooError if @foos < 4
|
19
19
|
end
|
20
20
|
|
21
|
-
# raises BarError only in the
|
21
|
+
# raises BarError only in the 3 first calls
|
22
22
|
def bar!
|
23
23
|
@bars += 1
|
24
|
-
raise BarError if @bars <
|
24
|
+
raise BarError if @bars < 4
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -39,69 +39,62 @@ class Retry_uptoTest < MiniTest::Unit::TestCase
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def test_retries_the_desired_number_of_attempts
|
42
|
-
retry_upto(
|
43
|
-
assert_equal
|
42
|
+
retry_upto(4){ @target.foo! }
|
43
|
+
assert_equal 4, @target.foos
|
44
44
|
end
|
45
45
|
|
46
46
|
# interval between attempts
|
47
47
|
|
48
48
|
def test_there_is_no_interval_between_attempts_by_default
|
49
|
-
self.expects(:sleep).
|
50
|
-
retry_upto(
|
49
|
+
self.expects(:sleep).never
|
50
|
+
retry_upto(4){ @target.foo! }
|
51
51
|
end
|
52
52
|
|
53
53
|
def test_interval_between_attempts_can_be_customized
|
54
|
-
self.expects(:sleep).times(
|
55
|
-
retry_upto(
|
54
|
+
self.expects(:sleep).times(3).with(5).returns(nil)
|
55
|
+
retry_upto(4, :interval => 5){ @target.foo! }
|
56
56
|
end
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
self.expects(:sleep).times(1).with(
|
62
|
-
|
63
|
-
retry_upto(3, :interval => 5, :growth => 3){ @target.foo! }
|
64
|
-
end
|
65
|
-
|
66
|
-
def test_grow_for_inverval_between_attempts_can_be_defined_with_a_lambda
|
67
|
-
self.expects(:sleep).times(1).with(5)
|
68
|
-
self.expects(:sleep).times(1).with(7)
|
69
|
-
retry_upto(3, :interval => 5, :growth => lambda{ |t| t + 2 }){ @target.foo! }
|
58
|
+
def test_different_intervals_can_be_defined_with_a_lambda
|
59
|
+
self.expects(:sleep).times(1).with(3)
|
60
|
+
self.expects(:sleep).times(1).with(6)
|
61
|
+
self.expects(:sleep).times(1).with(9)
|
62
|
+
retry_upto(4, :interval => lambda{ |attempt| attempt * 3 }){ @target.foo! }
|
70
63
|
end
|
71
64
|
|
72
65
|
# exceptions
|
73
66
|
|
74
67
|
def test_by_default_any_exception_gets_captured_if_there_are_attempts_left
|
75
|
-
retry_upto(
|
68
|
+
retry_upto(4){ @target.foo! }
|
76
69
|
assert true # if we reach this no exception was raised out of retry_upto
|
77
70
|
end
|
78
71
|
|
79
72
|
def test_the_last_attempt_does_not_capture_the_exception
|
80
73
|
assert_raises(FooError) do
|
81
|
-
retry_upto(
|
74
|
+
retry_upto(3){ @target.foo! }
|
82
75
|
end
|
83
76
|
end
|
84
77
|
|
85
78
|
def test_a_specified_exception_will_be_captured_between_attempts
|
86
|
-
retry_upto(
|
79
|
+
retry_upto(4, :rescue => FooError){ @target.foo! }
|
87
80
|
assert true # if we reach this no exception was raised out of retry_upto
|
88
81
|
end
|
89
82
|
|
90
83
|
def test_several_specified_exceptions_will_be_captured_between_attempts
|
91
|
-
retry_upto(
|
92
|
-
retry_upto(
|
84
|
+
retry_upto(4, :rescue => [FooError, BarError]){ @target.foo! }
|
85
|
+
retry_upto(4, :rescue => [FooError, BarError]){ @target.bar! }
|
93
86
|
assert true # if we reach this no exception was raised out of retry_upto
|
94
87
|
end
|
95
88
|
|
96
89
|
def test_the_last_attempt_does_not_capture_the_specified_exception
|
97
90
|
assert_raises(FooError) do
|
98
|
-
retry_upto(
|
91
|
+
retry_upto(3, :rescue => FooError){ @target.foo! }
|
99
92
|
end
|
100
93
|
end
|
101
94
|
|
102
95
|
def test_a_exception_different_from_the_specified_one_will_not_be_captured_between_attempts
|
103
96
|
assert_raises(FooError) do
|
104
|
-
retry_upto(
|
97
|
+
retry_upto(4, :rescue => ZeroDivisionError){ @target.foo! }
|
105
98
|
end
|
106
99
|
end
|
107
100
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: retry_upto
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.2'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -14,11 +14,11 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date: 2011-10-
|
17
|
+
date: 2011-10-10 00:00:00.000000000Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: minitest
|
21
|
-
requirement: &
|
21
|
+
requirement: &2153491260 !ruby/object:Gem::Requirement
|
22
22
|
none: false
|
23
23
|
requirements:
|
24
24
|
- - ! '>'
|
@@ -26,10 +26,10 @@ dependencies:
|
|
26
26
|
version: '2.0'
|
27
27
|
type: :development
|
28
28
|
prerelease: false
|
29
|
-
version_requirements: *
|
29
|
+
version_requirements: *2153491260
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: mocha
|
32
|
-
requirement: &
|
32
|
+
requirement: &2153490760 !ruby/object:Gem::Requirement
|
33
33
|
none: false
|
34
34
|
requirements:
|
35
35
|
- - ! '>='
|
@@ -37,10 +37,10 @@ dependencies:
|
|
37
37
|
version: 0.10.0
|
38
38
|
type: :development
|
39
39
|
prerelease: false
|
40
|
-
version_requirements: *
|
40
|
+
version_requirements: *2153490760
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
|
-
requirement: &
|
43
|
+
requirement: &2153490260 !ruby/object:Gem::Requirement
|
44
44
|
none: false
|
45
45
|
requirements:
|
46
46
|
- - ! '>='
|
@@ -48,7 +48,7 @@ dependencies:
|
|
48
48
|
version: 0.9.2
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
|
-
version_requirements: *
|
51
|
+
version_requirements: *2153490260
|
52
52
|
description: adds some useful options to retry code blocks
|
53
53
|
email:
|
54
54
|
- raul@murciano.net
|