attempt_this 0.8.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a25ae3dbdf326fbbfd6ac7f4f87576495d4aa2ff
4
+ data.tar.gz: 570ce5129322b14d1b0c61a463daeb6b3c335962
5
+ SHA512:
6
+ metadata.gz: e97953cc5b47eedbe6f6398ec1e1ad528aadf2cd36829dc8e5533c80846b0344fd1f5efc95a7bf4fc8481b866175f1a732dd1a4de88d57ddfa66f44f2cc96f68
7
+ data.tar.gz: 135cd4ad6336d9c63b9a6d58c3858706dd5445d73cd882e9e94ea6ea0dbbbb9db40df5a1dabfd66fea82b480c8ec9ca565fefcfd8bd4b7506601917879851c98
@@ -0,0 +1,112 @@
1
+ require_relative 'binary_backoff_policy.rb'
2
+ require_relative 'exception_type_filter.rb'
3
+
4
+ module AttemptThis
5
+ # Retry policy implementation.
6
+ # This class is internal and is not supposed to be used outside of the module.
7
+ class AttemptObject
8
+ # Initializes object with enumerator.
9
+ def initialize(enumerator)
10
+ @enumerator = enumerator
11
+ end
12
+
13
+ # Executes the code block.
14
+ def attempt(block)
15
+ if (block)
16
+ last_exception = nil
17
+ first_time = true
18
+
19
+ @delay_policy = ->{} unless @delay_policy
20
+ @reset_method = ->{} unless @reset_method
21
+ @exception_filter = ExceptionTypeFilter.new([StandardError]) unless @exception_filter
22
+
23
+ @enumerator.each do
24
+ @delay_policy.call unless first_time
25
+ last_exception = nil
26
+ begin
27
+ block.call
28
+ break
29
+ rescue => ex
30
+ raise unless @exception_filter.include?(ex)
31
+ last_exception = ex
32
+ @reset_method.call
33
+ end
34
+ first_time = false
35
+ end
36
+
37
+ # Re-raise the last exception
38
+ if (last_exception)
39
+ if (@default_method)
40
+ @default_method.call
41
+ else
42
+ raise last_exception
43
+ end
44
+ end
45
+ end
46
+
47
+ # Return self to allow chaining calls.
48
+ self
49
+ end
50
+
51
+ # Specifies delay in seconds between failed attempts.
52
+ def with_delay(delay, &block)
53
+ # Delay should be either an integer or a range of integers.
54
+ if (delay.is_a?(Numeric))
55
+ raise(ArgumentError, "Delay should be a non-negative number; got #{delay}!") unless delay >= 0
56
+ delay = delay..delay
57
+ elsif delay.is_a?(Range)
58
+ raise(ArgumentError, "Range members should be numbers; got #{delay}!") unless delay.first.is_a?(Numeric) && delay.last.is_a?(Numeric)
59
+ raise(ArgumentError, "Range members should be non-negative; got #{delay}!") unless delay.first >= 0 && delay.last >= 0
60
+ raise(ArgumentError, "Range's end should be greater than or equal to range's start; got #{delay}!") unless delay.first <= delay.last
61
+ else
62
+ raise(ArgumentError, "Delay should be either an number or a range of numbers; got #{delay}!")
63
+ end
64
+ raise(ArgumentError, 'Delay policy has already been specified!') if @delay_policy
65
+ @delay_policy = ->{Kernel.sleep(rand(delay))}
66
+
67
+ attempt(block)
68
+ end
69
+
70
+ # Specifies reset method that will be called after each failed attempt.
71
+ def with_reset(reset_method, &block)
72
+ raise(ArgumentError, 'Reset method is nil!') unless reset_method
73
+ raise(ArgumentError, 'Reset method has already been speicifed!') if @reset_method
74
+
75
+ @reset_method = reset_method
76
+ attempt(block)
77
+ end
78
+
79
+ # Specifies default method that should be called after all attempts have failed.
80
+ def and_default_to(default_method, &block)
81
+ raise(ArgumentError, 'Default method is nil!') unless default_method
82
+ raise(ArgumentError, 'Default method has already been specified!') if @default_method
83
+
84
+ @default_method = default_method
85
+ attempt(block)
86
+ end
87
+
88
+ # Specifies delay which doubles between failed attempts.
89
+ def with_binary_backoff(initial_delay, &block)
90
+ raise(ArgumentError, "Delay should be a number; got ${initial_delay}!") unless initial_delay.is_a?(Numeric)
91
+ raise(ArgumentError, "Delay should be a positive number; got #{initial_delay}!") unless initial_delay > 0
92
+ raise(ArgumentError, "Delay policy has already been specified!") if @delay_policy
93
+
94
+ @delay_policy = BinaryBackoffPolicy.new(initial_delay)
95
+ attempt(block)
96
+ end
97
+
98
+ # Specifies exceptions
99
+ def with_filter(*exceptions, &block)
100
+ raise(ArgumentError, "Empty exceptions list!") unless exceptions.size > 0
101
+ # Everything must be an exception.
102
+ exceptions.each do |e|
103
+ raise(ArgumentError, "Not an exception: #{e}!") unless e <= Exception
104
+ end
105
+
106
+ raise(ArgumentError, "Exception filter has already been specified!") if @exception_filter
107
+
108
+ @exception_filter = ExceptionTypeFilter.new(exceptions)
109
+ attempt(block)
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,15 @@
1
+ module AttemptThis
2
+ # Implementation of binary backoff policy. Internal use only.
3
+ class BinaryBackoffPolicy
4
+ # Initializer.
5
+ def initialize(initial_delay)
6
+ @delay = initial_delay
7
+ end
8
+
9
+ # Calls the policy.
10
+ def call
11
+ Kernel.sleep(@delay)
12
+ @delay *= 2
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module AttemptThis
2
+ # Type-based exception filter.
3
+ class ExceptionTypeFilter
4
+ # Initializer.
5
+ def initialize(exception_classes)
6
+ @exception_classes = Array.new(exception_classes)
7
+ end
8
+
9
+ # Tells whether the given exception satisfies the filter.
10
+ def include?(exception)
11
+ @exception_classes.any?{|klass| exception.is_a?(klass)}
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ require 'attempt_this/attempt_object.rb'
2
+
3
+ module AttemptThis
4
+ # Attempts code block until it doesn't throw an exception or the end of enumerator has been reached.
5
+ def attempt(enumerator, &block)
6
+ raise(ArgumentError, 'Nil enumerator!') if enumerator.nil?
7
+
8
+ impl = AttemptObject.new(enumerator)
9
+ impl.attempt(block)
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: attempt_this
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.0
5
+ platform: ruby
6
+ authors:
7
+ - Aliaksei Baturytski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-05 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Retry on exception with configurable number of times, delays, exception
14
+ filters, and fall back strategies
15
+ email: abaturytski@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/attempt_this.rb
21
+ - lib/attempt_this/attempt_object.rb
22
+ - lib/attempt_this/binary_backoff_policy.rb
23
+ - lib/attempt_this/exception_type_filter.rb
24
+ homepage: https://github.com/aliakb/attempt_this
25
+ licenses: []
26
+ metadata: {}
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 2.0.0
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: Retry policy mix-in
47
+ test_files: []