attempt 0.1.0

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