attempt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/CHANGES +2 -0
  2. data/MANIFEST +9 -0
  3. data/README +64 -0
  4. data/lib/attempt.rb +99 -0
  5. data/test/tc_attempt.rb +42 -0
  6. metadata +52 -0
data/CHANGES ADDED
@@ -0,0 +1,2 @@
1
+ == 0.1.0 - 9-Jun-2006
2
+ * Initial commit
data/MANIFEST ADDED
@@ -0,0 +1,9 @@
1
+ CHANGES
2
+ MANIFEST
3
+ README
4
+ install.rb
5
+ attempt.gemspec
6
+
7
+ lib/attempt.rb
8
+
9
+ test/tc_attempt.rb
data/README ADDED
@@ -0,0 +1,64 @@
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
+ === Standard Installation
9
+ ruby test/tc_attempt.rb
10
+ ruby install.rb
11
+
12
+ === Gem Installation
13
+ ruby test/tc_attempt.rb
14
+ ruby attempt.gemspec
15
+ gem install attempt-x-y-z.gem # Where x-y-z is a version
16
+
17
+ == Synopsis
18
+ require 'attempt'
19
+
20
+ # Attempt to ftp to some host, trying 3 times with 30 seconds between
21
+ # attempts before finally raising an error.
22
+ #
23
+ attempt(3, 30){
24
+ Net::FTP.open(host, user, passwd){ ... }
25
+ }
26
+
27
+ # Or, do things the long way...
28
+ code = Attempt.new{ |a|
29
+ a.tries = 3
30
+ a.interval = 30
31
+ }
32
+
33
+ code.attempt{
34
+ Net::FTP.open(host, user, passwd){ ... }
35
+ }
36
+
37
+ == Future Plans
38
+ Add the ability to set an absolute maximum number of seconds to prevent
39
+ nested sleep/retry from delaying attempts longer than expected.
40
+
41
+ == Known Bugs
42
+ None that I'm aware of. If you find any bugs, please log them on the
43
+ project page at http://www.rubyforge.org/projects/shards.
44
+
45
+ == Notes From the Author
46
+ Use with caution. Specifically, make sure you aren't inadvertantly
47
+ wrapping code that already performs sleep + retry. Otherwise, you'll
48
+ end up with a series of nested retry's that could take much longer to
49
+ work than you expect.
50
+
51
+ == Warranty
52
+ This package is provided "as is" and without any express or
53
+ implied warranties, including, without limitation, the implied
54
+ warranties of merchantability and fitness for a particular purpose.
55
+
56
+ == License
57
+ Ruby's
58
+
59
+ == Copyright
60
+ (C) 2006, Daniel J. Berger
61
+ All Rights Reserved
62
+
63
+ == Author
64
+ Daniel J. Berger
data/lib/attempt.rb ADDED
@@ -0,0 +1,99 @@
1
+ require 'timeout'
2
+
3
+ class Attempt
4
+ VERSION = '0.1.0'
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
+ # attempt(tries = 3, interval = 60, timeout = nil){ # some op }
75
+ #
76
+ # Attempt to perform the operation in the provided block up to +tries+
77
+ # times, sleeping +interval+ between each try. By default the number
78
+ # of tries defaults to 3, the interval defaults to 60 seconds, and there
79
+ # is no timeout specified.
80
+ #
81
+ # If +timeout+ is provided then the operation is wrapped in a Timeout
82
+ # block as well. This is handy for those rare occasions when an IO
83
+ # connection could hang indefinitely, for example.
84
+ #
85
+ # If the operation still fails the (last) error is then re-raised.
86
+ #
87
+ # This is really just a wrapper for Attempt.new where the simple case is
88
+ # good enough i.e. you don't care about warnings, increments or logging,
89
+ # and you want a little added convenience.
90
+ #
91
+ def attempt(tries = 3, interval = 60, timeout = nil, &block)
92
+ raise 'no block given' unless block_given?
93
+ Attempt.new{ |a|
94
+ a.tries = tries
95
+ a.interval = interval
96
+ a.timeout = timeout if timeout
97
+ }.attempt(&block)
98
+ end
99
+ end
@@ -0,0 +1,42 @@
1
+ ###########################################################
2
+ # tc_attempt.rb
3
+ #
4
+ # Test case for the attempt package.
5
+ ###########################################################
6
+ Dir.chdir('..') if File.basename(Dir.pwd) == 'test'
7
+ $LOAD_PATH.unshift(Dir.pwd + '/lib')
8
+
9
+ require 'attempt'
10
+ require 'test/unit'
11
+
12
+ print "\n== You will see a warning or two when running these tests ==\n\n"
13
+
14
+ class TC_Attempt < Test::Unit::TestCase
15
+ def setup
16
+ @tries = 2
17
+ @interval = 0.1
18
+ @timeout = 0.1
19
+ end
20
+
21
+ def test_version
22
+ assert_equal('0.1.0', Attempt::VERSION)
23
+ end
24
+
25
+ def test_attempt_basic
26
+ assert_nothing_raised{ attempt{ 2 + 2 } }
27
+ assert_nothing_raised{ attempt(@tries){ 2 + 2 } }
28
+ assert_nothing_raised{ attempt(@tries, @interval){ 2 + 2 } }
29
+ assert_nothing_raised{ attempt(@tries, @interval, @timeout){ 2 + 2 } }
30
+ end
31
+
32
+ def test_attempt_expected_errors
33
+ assert_raises(Timeout::Error){ attempt(1, 1, @timeout){ sleep 5 } }
34
+ assert_raises(RuntimeError){ attempt(2,2){ raise } }
35
+ end
36
+
37
+ def teardown
38
+ @tries = nil
39
+ @interval = nil
40
+ @timeout = nil
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: attempt
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2006-06-09 00:00:00 -05:00
8
+ summary: A thin wrapper for begin + rescue + sleep + retry
9
+ require_paths:
10
+ - lib
11
+ email: djberg96@gmail.com
12
+ homepage: http://www.rubyforge.org/projects/shards
13
+ rubyforge_project:
14
+ description: A thin wrapper for begin + rescue + sleep + retry
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - Daniel J. Berger
30
+ files:
31
+ - lib/attempt.rb
32
+ - CHANGES
33
+ - CVS
34
+ - MANIFEST
35
+ - README
36
+ - test/CVS
37
+ - test/tc_attempt.rb
38
+ test_files:
39
+ - test/tc_attempt.rb
40
+ rdoc_options: []
41
+
42
+ extra_rdoc_files:
43
+ - README
44
+ - CHANGES
45
+ executables: []
46
+
47
+ extensions: []
48
+
49
+ requirements: []
50
+
51
+ dependencies: []
52
+