retry_block 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in retry_block.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task :test do
4
+ ruby "-I lib test/test_retry_block.rb"
5
+ end
@@ -0,0 +1,3 @@
1
+ module RetryBlock
2
+ VERSION = "1.0.1"
3
+ end
@@ -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
@@ -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