attempt 0.1.0 → 0.1.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/CHANGES +7 -2
- data/MANIFEST +7 -9
- data/README +61 -64
- data/Rakefile +20 -0
- data/lib/attempt.rb +105 -99
- data/test/tc_attempt.rb +40 -42
- metadata +6 -5
data/CHANGES
CHANGED
@@ -1,2 +1,7 @@
|
|
1
|
-
== 0.1.
|
2
|
-
*
|
1
|
+
== 0.1.1 - 31-Jul-2007
|
2
|
+
* Added a Rakefile with tasks for testing and installation.
|
3
|
+
* Removed the install.rb file, since installation is now handled by the Rakefile.
|
4
|
+
* Some minor doc updates.
|
5
|
+
|
6
|
+
== 0.1.0 - 9-Jun-2006
|
7
|
+
* Initial commit
|
data/MANIFEST
CHANGED
data/README
CHANGED
@@ -1,64 +1,61 @@
|
|
1
|
-
== Description
|
2
|
-
A thin wrapper for begin + rescue + sleep + retry.
|
3
|
-
|
4
|
-
== Prerequisites
|
5
|
-
Ruby 1.8.0 or later.
|
6
|
-
|
7
|
-
== Installation
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
}
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
==
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
==
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
== Author
|
64
|
-
Daniel J. Berger
|
1
|
+
== Description
|
2
|
+
A thin wrapper for begin + rescue + sleep + retry.
|
3
|
+
|
4
|
+
== Prerequisites
|
5
|
+
Ruby 1.8.0 or later.
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
rake test (optional)
|
9
|
+
rake install (non-gem) or rake install_gem (gem)
|
10
|
+
|
11
|
+
== Synopsis
|
12
|
+
require 'attempt'
|
13
|
+
|
14
|
+
# Attempt to ftp to some host, trying 3 times with 30 seconds between
|
15
|
+
# attempts before finally raising an error.
|
16
|
+
#
|
17
|
+
attempt(3, 30){
|
18
|
+
Net::FTP.open(host, user, passwd){ ... }
|
19
|
+
}
|
20
|
+
|
21
|
+
# Or, do things the long way...
|
22
|
+
code = Attempt.new{ |a|
|
23
|
+
a.tries = 3
|
24
|
+
a.interval = 30
|
25
|
+
}
|
26
|
+
|
27
|
+
code.attempt{
|
28
|
+
Net::FTP.open(host, user, passwd){ ... }
|
29
|
+
}
|
30
|
+
|
31
|
+
== Future Plans
|
32
|
+
Add the ability to set an absolute maximum number of seconds to prevent
|
33
|
+
nested sleep/retry from delaying attempts longer than expected.
|
34
|
+
|
35
|
+
== Known Bugs
|
36
|
+
None that I'm aware of. If you find any bugs, please log them on the
|
37
|
+
project page at http://www.rubyforge.org/projects/shards.
|
38
|
+
|
39
|
+
== Notes From the Author
|
40
|
+
Use with caution. Specifically, make sure you aren't inadvertantly
|
41
|
+
wrapping code that already performs sleep + retry. Otherwise, you'll
|
42
|
+
end up with a series of nested retry's that could take much longer to
|
43
|
+
work than you expect.
|
44
|
+
|
45
|
+
== Acknowledgements
|
46
|
+
This library is partially based on Mark Fowler's 'Attempt' Perl module.
|
47
|
+
|
48
|
+
== Warranty
|
49
|
+
This package is provided "as is" and without any express or
|
50
|
+
implied warranties, including, without limitation, the implied
|
51
|
+
warranties of merchantability and fitness for a particular purpose.
|
52
|
+
|
53
|
+
== License
|
54
|
+
Ruby's
|
55
|
+
|
56
|
+
== Copyright
|
57
|
+
(C) 2006-2007, Daniel J. Berger
|
58
|
+
All Rights Reserved
|
59
|
+
|
60
|
+
== Author
|
61
|
+
Daniel J. Berger
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
desc "Install the attempt package (non-gem)"
|
5
|
+
task :install do
|
6
|
+
cp 'lib/attempt.rb', Config::CONFIG['sitelibdir'], :verbose => true
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Install the attempt package as a gem"
|
10
|
+
task :install_gem do
|
11
|
+
ruby 'attempt.gemspec'
|
12
|
+
file = Dir["*.gem"].first
|
13
|
+
sh "gem install #{file}"
|
14
|
+
end
|
15
|
+
|
16
|
+
Rake::TestTask.new do |t|
|
17
|
+
t.libs << 'lib'
|
18
|
+
t.warning = true
|
19
|
+
t.test_files = FileList['test/tc*']
|
20
|
+
end
|
data/lib/attempt.rb
CHANGED
@@ -1,99 +1,105 @@
|
|
1
|
-
require 'timeout'
|
2
|
-
|
3
|
-
class Attempt
|
4
|
-
VERSION = '0.1.
|
5
|
-
|
6
|
-
# Number of attempts to make before failing. The default is 3.
|
7
|
-
attr_accessor :tries
|
8
|
-
|
9
|
-
# Number of seconds to wait between attempts. The default is 60.
|
10
|
-
attr_accessor :interval
|
11
|
-
|
12
|
-
# A boolean value that determines whether errors that would have been
|
13
|
-
# raised should be sent to STDERR as warnings. The default is true.
|
14
|
-
attr_accessor :warnings
|
15
|
-
|
16
|
-
# If you provide an IO handle to this option then errors that would
|
17
|
-
# have been raised are sent to that handle.
|
18
|
-
attr_accessor :log
|
19
|
-
|
20
|
-
# If set, this increments the interval with each failed attempt by that
|
21
|
-
# number of seconds.
|
22
|
-
attr_accessor :increment
|
23
|
-
|
24
|
-
# If set, the code block is further wrapped in a timeout block.
|
25
|
-
attr_accessor :timeout
|
26
|
-
|
27
|
-
# Determines which exception level to check when looking for errors to
|
28
|
-
# retry. The default is 'Exception' (i.e. all errors).
|
29
|
-
attr_accessor :level
|
30
|
-
|
31
|
-
# :call-seq:
|
32
|
-
# Attempt.new{ |a| ... }
|
33
|
-
#
|
34
|
-
# Creates and returns a new +Attempt+ object. Use a block to set the
|
35
|
-
# accessors.
|
36
|
-
#
|
37
|
-
def initialize
|
38
|
-
@tries = 3 # Reasonable default
|
39
|
-
@interval = 60 # Reasonable default
|
40
|
-
@log = nil # Should be an IO handle, if provided
|
41
|
-
@increment = nil # Should be an int, if provided
|
42
|
-
@timeout = nil # Wrap the code in a timeout block if provided
|
43
|
-
@level = Exception # Level of exception to be caught
|
44
|
-
@warnings = true # Errors sent to STDERR as warnings if true
|
45
|
-
|
46
|
-
yield self if block_given?
|
47
|
-
end
|
48
|
-
|
49
|
-
def attempt
|
50
|
-
count = 1
|
51
|
-
begin
|
52
|
-
if @timeout
|
53
|
-
Timeout.timeout(@timeout){ yield }
|
54
|
-
else
|
55
|
-
yield
|
56
|
-
end
|
57
|
-
rescue @level => error
|
58
|
-
@tries -= 1
|
59
|
-
if @tries > 0
|
60
|
-
msg = "Error on attempt # #{count}: #{error}; retrying"
|
61
|
-
count += 1
|
62
|
-
warn msg if @warnings
|
63
|
-
@log.puts msg if @log
|
64
|
-
@interval += @increment if @increment
|
65
|
-
sleep @interval
|
66
|
-
retry
|
67
|
-
end
|
68
|
-
raise
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
module Kernel
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
class Attempt
|
4
|
+
VERSION = '0.1.1'
|
5
|
+
|
6
|
+
# Number of attempts to make before failing. The default is 3.
|
7
|
+
attr_accessor :tries
|
8
|
+
|
9
|
+
# Number of seconds to wait between attempts. The default is 60.
|
10
|
+
attr_accessor :interval
|
11
|
+
|
12
|
+
# A boolean value that determines whether errors that would have been
|
13
|
+
# raised should be sent to STDERR as warnings. The default is true.
|
14
|
+
attr_accessor :warnings
|
15
|
+
|
16
|
+
# If you provide an IO handle to this option then errors that would
|
17
|
+
# have been raised are sent to that handle.
|
18
|
+
attr_accessor :log
|
19
|
+
|
20
|
+
# If set, this increments the interval with each failed attempt by that
|
21
|
+
# number of seconds.
|
22
|
+
attr_accessor :increment
|
23
|
+
|
24
|
+
# If set, the code block is further wrapped in a timeout block.
|
25
|
+
attr_accessor :timeout
|
26
|
+
|
27
|
+
# Determines which exception level to check when looking for errors to
|
28
|
+
# retry. The default is 'Exception' (i.e. all errors).
|
29
|
+
attr_accessor :level
|
30
|
+
|
31
|
+
# :call-seq:
|
32
|
+
# Attempt.new{ |a| ... }
|
33
|
+
#
|
34
|
+
# Creates and returns a new +Attempt+ object. Use a block to set the
|
35
|
+
# accessors.
|
36
|
+
#
|
37
|
+
def initialize
|
38
|
+
@tries = 3 # Reasonable default
|
39
|
+
@interval = 60 # Reasonable default
|
40
|
+
@log = nil # Should be an IO handle, if provided
|
41
|
+
@increment = nil # Should be an int, if provided
|
42
|
+
@timeout = nil # Wrap the code in a timeout block if provided
|
43
|
+
@level = Exception # Level of exception to be caught
|
44
|
+
@warnings = true # Errors sent to STDERR as warnings if true
|
45
|
+
|
46
|
+
yield self if block_given?
|
47
|
+
end
|
48
|
+
|
49
|
+
def attempt
|
50
|
+
count = 1
|
51
|
+
begin
|
52
|
+
if @timeout
|
53
|
+
Timeout.timeout(@timeout){ yield }
|
54
|
+
else
|
55
|
+
yield
|
56
|
+
end
|
57
|
+
rescue @level => error
|
58
|
+
@tries -= 1
|
59
|
+
if @tries > 0
|
60
|
+
msg = "Error on attempt # #{count}: #{error}; retrying"
|
61
|
+
count += 1
|
62
|
+
warn msg if @warnings
|
63
|
+
@log.puts msg if @log
|
64
|
+
@interval += @increment if @increment
|
65
|
+
sleep @interval
|
66
|
+
retry
|
67
|
+
end
|
68
|
+
raise
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
module Kernel
|
74
|
+
# :call-seq:
|
75
|
+
# attempt(tries = 3, interval = 60, timeout = nil){ # some op }
|
76
|
+
#
|
77
|
+
# Attempt to perform the operation in the provided block up to +tries+
|
78
|
+
# times, sleeping +interval+ between each try. By default the number
|
79
|
+
# of tries defaults to 3, the interval defaults to 60 seconds, and there
|
80
|
+
# is no timeout specified.
|
81
|
+
#
|
82
|
+
# If +timeout+ is provided then the operation is wrapped in a Timeout
|
83
|
+
# block as well. This is handy for those rare occasions when an IO
|
84
|
+
# connection could hang indefinitely, for example.
|
85
|
+
#
|
86
|
+
# If the operation still fails the (last) error is then re-raised.
|
87
|
+
#
|
88
|
+
# This is really just a wrapper for Attempt.new where the simple case is
|
89
|
+
# good enough i.e. you don't care about warnings, increments or logging,
|
90
|
+
# and you want a little added convenience.
|
91
|
+
#
|
92
|
+
# Example:
|
93
|
+
#
|
94
|
+
# # Make 3 attempts to connect to the database, 60 seconds apart.
|
95
|
+
# attempt{ DBI.connect(dsn, user, passwd) }
|
96
|
+
#
|
97
|
+
def attempt(tries = 3, interval = 60, timeout = nil, &block)
|
98
|
+
raise 'no block given' unless block_given?
|
99
|
+
Attempt.new{ |a|
|
100
|
+
a.tries = tries
|
101
|
+
a.interval = interval
|
102
|
+
a.timeout = timeout if timeout
|
103
|
+
}.attempt(&block)
|
104
|
+
end
|
105
|
+
end
|
data/test/tc_attempt.rb
CHANGED
@@ -1,42 +1,40 @@
|
|
1
|
-
|
2
|
-
# tc_attempt.rb
|
3
|
-
#
|
4
|
-
# Test case for the attempt package.
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
assert_nothing_raised{ attempt{ 2 + 2 } }
|
27
|
-
assert_nothing_raised{ attempt(@tries){ 2 + 2 } }
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
@
|
39
|
-
|
40
|
-
|
41
|
-
end
|
42
|
-
end
|
1
|
+
#####################################################################
|
2
|
+
# tc_attempt.rb
|
3
|
+
#
|
4
|
+
# Test case for the attempt package. You should run this test case
|
5
|
+
# via the 'rake test' Rakefile task.
|
6
|
+
#####################################################################
|
7
|
+
require 'attempt'
|
8
|
+
require 'test/unit'
|
9
|
+
|
10
|
+
print "\n== You will see a warning or two when running these tests ==\n\n"
|
11
|
+
|
12
|
+
class TC_Attempt < Test::Unit::TestCase
|
13
|
+
def setup
|
14
|
+
@tries = 2
|
15
|
+
@interval = 0.1
|
16
|
+
@timeout = 0.1
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_version
|
20
|
+
assert_equal('0.1.1', Attempt::VERSION)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_attempt_basic
|
24
|
+
assert_nothing_raised{ attempt{ 2 + 2 } }
|
25
|
+
assert_nothing_raised{ attempt(@tries){ 2 + 2 } }
|
26
|
+
assert_nothing_raised{ attempt(@tries, @interval){ 2 + 2 } }
|
27
|
+
assert_nothing_raised{ attempt(@tries, @interval, @timeout){ 2 + 2 } }
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_attempt_expected_errors
|
31
|
+
assert_raises(Timeout::Error){ attempt(1, 1, @timeout){ sleep 5 } }
|
32
|
+
assert_raises(RuntimeError){ attempt(2,2){ raise } }
|
33
|
+
end
|
34
|
+
|
35
|
+
def teardown
|
36
|
+
@tries = nil
|
37
|
+
@interval = nil
|
38
|
+
@timeout = nil
|
39
|
+
end
|
40
|
+
end
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.
|
2
|
+
rubygems_version: 0.9.4
|
3
3
|
specification_version: 1
|
4
4
|
name: attempt
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
7
|
-
date:
|
6
|
+
version: 0.1.1
|
7
|
+
date: 2007-07-31 00:00:00 -06:00
|
8
8
|
summary: A thin wrapper for begin + rescue + sleep + retry
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -25,15 +25,15 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
|
|
25
25
|
platform: ruby
|
26
26
|
signing_key:
|
27
27
|
cert_chain:
|
28
|
+
post_install_message:
|
28
29
|
authors:
|
29
30
|
- Daniel J. Berger
|
30
31
|
files:
|
31
32
|
- lib/attempt.rb
|
32
33
|
- CHANGES
|
33
|
-
- CVS
|
34
34
|
- MANIFEST
|
35
35
|
- README
|
36
|
-
-
|
36
|
+
- Rakefile
|
37
37
|
- test/tc_attempt.rb
|
38
38
|
test_files:
|
39
39
|
- test/tc_attempt.rb
|
@@ -42,6 +42,7 @@ rdoc_options: []
|
|
42
42
|
extra_rdoc_files:
|
43
43
|
- README
|
44
44
|
- CHANGES
|
45
|
+
- MANIFEST
|
45
46
|
executables: []
|
46
47
|
|
47
48
|
extensions: []
|