retry_block 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +5 -0
- data/lib/retry_block/version.rb +3 -0
- data/lib/retry_block.rb +38 -0
- data/retry_block.gemspec +20 -0
- data/test/test_retry_block.rb +174 -0
- metadata +72 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/lib/retry_block.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "retry_block/version"
|
2
|
+
|
3
|
+
module Kernel
|
4
|
+
def retry_block (opts={}, &block)
|
5
|
+
opts = {
|
6
|
+
:attempts => 1, # Number of times to try block. Nil means to retry forever until success
|
7
|
+
:sleep => 0, # Seconds to sleep between attempts
|
8
|
+
:catch => Exception, # An exception or array of exceptions to listen for
|
9
|
+
:fail_callback => nil # Proc/lambda that gets executed between attempts
|
10
|
+
}.merge(opts)
|
11
|
+
|
12
|
+
opts[:catch] = [ opts[:catch] ].flatten
|
13
|
+
attempts = 1
|
14
|
+
|
15
|
+
if (not opts[:attempts].nil?) and (not opts[:attempts].is_a? Integer or opts[:attempts] <= 0)
|
16
|
+
raise ArgumentError, "retry_block: :attempts must be an integer >= 0 or nil"
|
17
|
+
end
|
18
|
+
|
19
|
+
begin
|
20
|
+
return yield attempts
|
21
|
+
rescue *opts[:catch] => exception
|
22
|
+
|
23
|
+
# If a callable object was given for :fail_callback then call it
|
24
|
+
if opts[:fail_callback].respond_to? :call
|
25
|
+
callback_opts = [attempts, exception].slice(0, opts[:fail_callback].arity)
|
26
|
+
opts[:fail_callback].call *callback_opts
|
27
|
+
end
|
28
|
+
|
29
|
+
# If we've maxed out our attempts, raise the exception to the calling code
|
30
|
+
raise if (not opts[:attempts].nil?) and (attempts += 1) > opts[:attempts]
|
31
|
+
|
32
|
+
# Sleep before the next retry if the option was given
|
33
|
+
sleep opts[:sleep] if opts[:sleep].is_a? Numeric and opts[:sleep] > 0
|
34
|
+
|
35
|
+
retry
|
36
|
+
end #rescue
|
37
|
+
end # def
|
38
|
+
end # module
|
data/retry_block.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "retry_block/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "retry_block"
|
7
|
+
s.version = RetryBlock::VERSION
|
8
|
+
s.authors = ["Alfred J. Fazio"]
|
9
|
+
s.email = ["alfred.fazio@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Take control of unstable or indeterminate code with retry_block}
|
12
|
+
s.description = %q{Take control of unstable or indeterminate code with retry_block}
|
13
|
+
|
14
|
+
s.rubyforge_project = "retry_block"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'lib/retry_block'
|
3
|
+
|
4
|
+
class TestRetryBlock < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@fail_count = 0
|
7
|
+
@run_count = 0
|
8
|
+
@fail_callback = lambda do
|
9
|
+
@fail_count += 1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_runs_block_once
|
14
|
+
assert_nothing_raised do
|
15
|
+
retry_block(:fail_callback => @fail_callback) do
|
16
|
+
@run_count += 1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
assert_equal 1, @run_count
|
20
|
+
assert_equal 0, @fail_count
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_runs_block_twice
|
24
|
+
assert_nothing_raised do
|
25
|
+
retry_block(:attempts => 2, :fail_callback => @fail_callback) do
|
26
|
+
@run_count += 1
|
27
|
+
raise TestException if @run_count == 1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
assert_equal 2, @run_count
|
31
|
+
assert_equal 1, @fail_count
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_runs_block_twice_and_fails
|
35
|
+
assert_raises(TestException) do
|
36
|
+
retry_block(:attempts => 2, :fail_callback => @fail_callback) do
|
37
|
+
@run_count += 1
|
38
|
+
raise TestException
|
39
|
+
end
|
40
|
+
end
|
41
|
+
assert_equal 2, @run_count
|
42
|
+
assert_equal 2, @fail_count
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_raises_unexpected_exception1
|
46
|
+
assert_raises(RuntimeError) do
|
47
|
+
retry_block(:attempts => 2, :catch => TestException, :fail_callback => @fail_callback) do
|
48
|
+
@run_count += 1
|
49
|
+
raise RuntimeError
|
50
|
+
end
|
51
|
+
end
|
52
|
+
assert_equal 1, @run_count
|
53
|
+
assert_equal 0, @fail_count
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_raises_unexpected_exception2
|
57
|
+
assert_raises(RuntimeError) do
|
58
|
+
retry_block(:attempts => 2, :catch => [TestException], :fail_callback => @fail_callback) do
|
59
|
+
@run_count += 1
|
60
|
+
raise RuntimeError
|
61
|
+
end
|
62
|
+
end
|
63
|
+
assert_equal 1, @run_count
|
64
|
+
assert_equal 0, @fail_count
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_catches_exception1
|
68
|
+
assert_nothing_raised do
|
69
|
+
retry_block(:attempts => 2, :catch => TestException, :fail_callback => @fail_callback) do
|
70
|
+
@run_count += 1
|
71
|
+
raise TestException unless @run_count == 2
|
72
|
+
end
|
73
|
+
end
|
74
|
+
assert_equal 2, @run_count
|
75
|
+
assert_equal 1, @fail_count
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_catches_multiple_exception
|
79
|
+
assert_nothing_raised do
|
80
|
+
retry_block(:attempts => 3, :catch => [TestException, RuntimeError], :fail_callback => @fail_callback) do
|
81
|
+
@run_count += 1
|
82
|
+
raise TestException if @run_count == 1
|
83
|
+
raise RuntimeError if @run_count == 2
|
84
|
+
end
|
85
|
+
end
|
86
|
+
assert_equal 3, @run_count
|
87
|
+
assert_equal 2, @fail_count
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_doesnt_catch_unexpected_exception_from_list
|
91
|
+
assert_raises(Exception) do
|
92
|
+
retry_block(:attempts => 4, :catch => [TestException, RuntimeError], :fail_callback => @fail_callback) do
|
93
|
+
@run_count += 1
|
94
|
+
raise TestException if @run_count == 1
|
95
|
+
raise RuntimeError if @run_count == 2
|
96
|
+
raise Exception if @run_count == 3
|
97
|
+
end
|
98
|
+
end
|
99
|
+
assert_equal 3, @run_count
|
100
|
+
assert_equal 2, @fail_count
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_fails_on_bad_argument1
|
104
|
+
assert_raises(ArgumentError) do
|
105
|
+
retry_block(:attempts => -1, :fail_callback => @fail_callback) do
|
106
|
+
@run_count += 1
|
107
|
+
end
|
108
|
+
end
|
109
|
+
assert_equal 0, @run_count
|
110
|
+
assert_equal 0, @fail_count
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_fails_on_bad_argument2
|
114
|
+
assert_raises(ArgumentError) do
|
115
|
+
retry_block(:attempts => 0.4, :fail_callback => @fail_callback) do
|
116
|
+
@run_count += 1
|
117
|
+
end
|
118
|
+
end
|
119
|
+
assert_equal 0, @run_count
|
120
|
+
assert_equal 0, @fail_count
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_sleeps
|
124
|
+
sleep_time = 0.1 # Sleep for 1/10 of a second
|
125
|
+
attempts = 3
|
126
|
+
# Doesn't sleep after last failure
|
127
|
+
total_sleep_time = (attempts-1) * sleep_time
|
128
|
+
begin_time = Time.new
|
129
|
+
assert_raises(TestException) do
|
130
|
+
retry_block(:attempts => attempts, :fail_callback => @fail_callback, :sleep => sleep_time) do
|
131
|
+
@run_count += 1
|
132
|
+
raise TestException
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end_time = Time.new
|
136
|
+
time_elapsed = end_time - begin_time
|
137
|
+
assert_equal attempts, @run_count
|
138
|
+
assert_equal attempts, @fail_count
|
139
|
+
# Things are never exact. Expect that time_elapsed in seconds to be
|
140
|
+
# equal to total_sleep_time plus or minus 5%
|
141
|
+
assert_in_delta total_sleep_time, time_elapsed, total_sleep_time*0.05
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_doesnt_sleep
|
145
|
+
begin_time = Time.new
|
146
|
+
assert_raises(TestException) do
|
147
|
+
retry_block(:attempts => 1, :fail_callback => @fail_callback, :sleep => 0.1) do
|
148
|
+
@run_count += 1
|
149
|
+
raise TestException
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end_time = Time.new
|
153
|
+
time_elapsed = end_time - begin_time
|
154
|
+
assert_equal 1, @run_count
|
155
|
+
assert_equal 1, @fail_count
|
156
|
+
# plus or minus 1/100 of a second ;)
|
157
|
+
assert_in_delta 0.0, time_elapsed, 0.01
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_runs_forever
|
161
|
+
assert_raises(RuntimeError) do
|
162
|
+
retry_block(:attempts => nil, :catch => TestException, :fail_callback => @fail_callback) do
|
163
|
+
@run_count += 1
|
164
|
+
raise TestException unless @run_count == 100
|
165
|
+
raise RuntimeError
|
166
|
+
end
|
167
|
+
end
|
168
|
+
assert_equal 100, @run_count
|
169
|
+
assert_equal 99, @fail_count
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class TestException < Exception
|
174
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: retry_block
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 1.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Alfred J. Fazio
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-11-04 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Take control of unstable or indeterminate code with retry_block
|
22
|
+
email:
|
23
|
+
- alfred.fazio@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- .gitignore
|
32
|
+
- Gemfile
|
33
|
+
- Rakefile
|
34
|
+
- lib/retry_block.rb
|
35
|
+
- lib/retry_block/version.rb
|
36
|
+
- retry_block.gemspec
|
37
|
+
- test/test_retry_block.rb
|
38
|
+
homepage: ""
|
39
|
+
licenses: []
|
40
|
+
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
hash: 3
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project: retry_block
|
67
|
+
rubygems_version: 1.8.5
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: Take control of unstable or indeterminate code with retry_block
|
71
|
+
test_files:
|
72
|
+
- test/test_retry_block.rb
|