retry_block 1.0.1

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.
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