wait 0.2.2 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/wait.rb +68 -150
  2. metadata +2 -2
data/lib/wait.rb CHANGED
@@ -1,62 +1,59 @@
1
- require "timeout"
2
- require "logger"
1
+ require File.expand_path("../initialize", __FILE__)
3
2
 
4
3
  class Wait
4
+ DEFAULT = {
5
+ :timeout => 15,
6
+ :logger => BaseLogger,
7
+ :attempts => 5,
8
+ :counter => BaseCounter,
9
+ :delay => 1,
10
+ :delayer => RegularDelayer,
11
+ :tester => TruthyTester,
12
+ :rescuer => PassiveRescuer
13
+ }
14
+
5
15
  # Creates a new Wait instance.
6
16
  #
7
17
  # == Options
8
18
  #
9
19
  # [:attempts]
10
- # Number of times to attempt the block. Default is +5+.
20
+ # Number of times to attempt the block (passed to +counter+). Default is
21
+ # +5+.
22
+ # [:counter]
23
+ # Strategy used to count attempts. Default is Wait::BaseCounter.
11
24
  # [:timeout]
12
25
  # Seconds until the block times out. Default is +15+.
26
+ # [:delay]
27
+ # Seconds to delay in between attempts (passed to +delayer+). Default is
28
+ # +1+.
13
29
  # [:delayer]
14
- # Delay strategy to use to sleep in between attempts. Default is
15
- # +Wait::RegularDelayer.new+.
30
+ # Strategy used to delay in between attempts. Default is
31
+ # Wait::RegularDelayer.
16
32
  # [:rescue]
17
- # One or an array of exceptions to rescue. Default is +nil+.
33
+ # One or an array of exceptions to rescue (passed to +rescuer+). Default
34
+ # is +nil+.
35
+ # [:rescuer]
36
+ # Strategy used to handle exceptions. Default is Wait::PassiveRescuer.
18
37
  # [:tester]
19
- # Strategy to use to test the result. Default is +Wait::TruthyTester+.
38
+ # Strategy used to test the result. Default is Wait::TruthyTester.
20
39
  # [:logger]
21
- # Ruby logger to use. Default is +Wait#logger+.
40
+ # Ruby logger used. Default is Wait::BaseLogger.
22
41
  #
23
42
  def initialize(options = {})
24
- @attempts = options[:attempts] || 5
25
- @timeout = options[:timeout] || 15
26
- @delayer = options[:delayer] || RegularDelayer.new
27
- @exceptions = Array(options[:rescue])
28
- @tester = options[:tester] || TruthyTester
29
- @logger = options[:logger] || logger
30
-
31
- @counter = AttemptCounter.new(@attempts)
32
-
33
- validate_strategies
43
+ @timeout = options[:timeout] || DEFAULT[:timeout]
44
+ debug = options[:debug]
45
+ @logger = (options[:logger] || (debug ? DebugLogger : DEFAULT[:logger])).new
46
+ attempts = options[:attempts] || DEFAULT[:attempts]
47
+ @counter = (options[:counter] || DEFAULT[:counter]).new(@logger, attempts)
48
+ delay = options[:delay] || DEFAULT[:delay]
49
+ @delayer = (options[:delayer] || DEFAULT[:delayer]).new(@logger, delay)
50
+ @tester = (options[:tester] || DEFAULT[:tester]).new(@logger)
51
+ exceptions = Array(options[:rescue])
52
+ @rescuer = (options[:rescuer] || DEFAULT[:rescuer]).new(@logger, @tester.exceptions, exceptions)
34
53
  end
35
54
 
36
- # Validates all of the assigned strategy objects.
37
- def validate_strategies
38
- unless @delayer.respond_to?(:sleep)
39
- raise(ArgumentError, "delay strategy does not respond to sleep message: #{@delayer.inspect}")
40
- end
41
-
42
- unless @tester.respond_to?(:new)
43
- raise(ArgumentError, "tester strategy does not respond to new message: #{@tester.inspect}")
44
- end
45
-
46
- unless @tester.new.respond_to?(:valid?)
47
- raise(ArgumentError, "tester strategy does not respond to valid? message: #{@tester.inspect}")
48
- end
49
- end
50
-
51
- # Returns a new (or existing) logger instance.
52
- def logger
53
- if @logger.nil?
54
- @logger = Logger.new(STDOUT)
55
- @logger.level = Logger::WARN
56
- end
57
-
58
- @logger
59
- end
55
+ # Raised when a block times out.
56
+ class TimeoutError < Timeout::Error; end
60
57
 
61
58
  # == Description
62
59
  #
@@ -64,7 +61,7 @@ class Wait
64
61
  # result. Useful for blocking script execution until:
65
62
  # * an HTTP request was successful
66
63
  # * a port has opened
67
- # * an external process has started
64
+ # * a process has started
68
65
  # * etc.
69
66
  #
70
67
  # == Examples
@@ -72,8 +69,11 @@ class Wait
72
69
  # wait = Wait.new
73
70
  # # => #<Wait>
74
71
  # wait.until { Time.now.sec.even? }
75
- # # Rescued exception while waiting: Wait::TruthyTester::ResultNotTruthy: false
76
- # # Attempt 1/5 failed, delaying for 1s
72
+ # # [Tester] result: false
73
+ # # [Rescuer] rescued: Wait::TruthyTester::ResultNotTruthy: false
74
+ # # [Counter] attempt 1/5 failed
75
+ # # [Delayer] delaying for 1s
76
+ # # [Tester] result: true
77
77
  # # => true
78
78
  #
79
79
  # If you wish to handle an exception by attempting the block again, pass one
@@ -88,10 +88,14 @@ class Wait
88
88
  # when 3 then "foo"
89
89
  # end
90
90
  # end
91
- # # Rescued exception while waiting: Wait::TruthyTester::ResultNotTruthy: nil
92
- # # Attempt 1/5 failed, delaying for 1s
93
- # # Rescued exception while waiting: RuntimeError: RuntimeError
94
- # # Attempt 2/5 failed, delaying for 2s
91
+ # # [Tester] result: nil
92
+ # # [Rescuer] rescued: Wait::TruthyTester::ResultNotTruthy: nil
93
+ # # [Counter] attempt 1/5 failed
94
+ # # [Delayer] delaying for 1s
95
+ # # [Rescuer] rescued: RuntimeError: RuntimeError
96
+ # # [Counter] attempt 2/5 failed
97
+ # # [Delayer] delaying for 1s
98
+ # # [Tester] result: "foo"
95
99
  # # => "foo"
96
100
  #
97
101
  # == Returns
@@ -105,111 +109,25 @@ class Wait
105
109
  def until(&block)
106
110
  # Reset the attempt counter.
107
111
  @counter.reset
108
-
109
112
  begin
113
+ # Increment the attempt counter.
110
114
  @counter.increment
111
-
112
- result = Timeout.timeout(@timeout, Wait::TimeoutError) do
113
- # Execute the block and pass the attempt count to it.
115
+ # Wrap the given block in a timeout.
116
+ result = Timeout.timeout(@timeout, TimeoutError) do
117
+ # Execute the block and pass the attempt count (an +Integer+) to it.
114
118
  yield(@counter.attempt)
115
119
  end
116
-
117
- tester = @tester.new(result)
118
- tester.raise_unless_valid
119
- rescue Wait::TimeoutError, *(@tester.exceptions + @exceptions) => exception
120
- logger.debug "Rescued exception while waiting: #{exception.class.name}: #{exception.message}"
121
- logger.debug exception.backtrace.join("\n")
122
-
123
- # If this was the last attempt, raise the exception from the last
124
- # attempt.
125
- if @counter.last_attempt?
126
- raise(exception)
127
- else
128
- logger.debug "Attempt #{@counter} failed, delaying for #{@delayer}"
129
- @delayer.sleep
130
- retry
131
- end
132
- end
133
- end
134
-
135
- # Raised when a block doesn't return a result (+nil+ or +false+).
136
- class NoResultError < StandardError; end
137
-
138
- # Raised when a block times out.
139
- class TimeoutError < Timeout::Error; end
140
-
141
- class RegularDelayer
142
- def initialize(initial_delay = 1)
143
- @delay = initial_delay
144
- end
145
-
146
- def sleep
147
- Kernel.sleep(@delay)
148
- end
149
-
150
- def to_s
151
- "#{@delay}s"
152
- end
153
- end # RegularDelayer
154
-
155
- class ExponentialDelayer < RegularDelayer
156
- def sleep
157
- super
158
- increment
159
- end
160
-
161
- def increment
162
- @delay *= 2
163
- end
164
- end # ExponentialDelayer
165
-
166
- class AttemptCounter
167
- attr_reader :attempt
168
-
169
- def initialize(total)
170
- # Prevent accidentally causing an infinite loop.
171
- unless total.is_a?(Fixnum) and total > 0
172
- raise(ArgumentError, "invalid number of attempts: #{total.inspect}")
173
- end
174
-
175
- @total = total
176
- reset
177
- end
178
-
179
- def reset
180
- @attempt = 0
181
- end
182
-
183
- def increment
184
- @attempt += 1
185
- end
186
-
187
- def last_attempt?
188
- @attempt == @total
189
- end
190
-
191
- def to_s
192
- [@attempt, @total].join("/")
193
- end
194
- end # AttemptCounter
195
-
196
- class TruthyTester
197
- class ResultNotTruthy < RuntimeError; end
198
-
199
- def self.exceptions
200
- [ResultNotTruthy]
201
- end
202
-
203
- def initialize(result = nil)
204
- @result = result
205
- end
206
-
207
- def raise_unless_valid
208
- valid? ? @result : raise(ResultNotTruthy, @result.inspect)
209
- end
210
-
211
- def valid?
212
- not (@result.nil? or @result == false)
120
+ # Raise an exception unless the result is valid.
121
+ @tester.raise_unless_valid(result)
122
+ rescue *@rescuer.exceptions => exception
123
+ # Raise the exception unless it can be ignored.
124
+ @rescuer.raise_unless_ignore(exception)
125
+ # Raise the exception if this was the last attempt.
126
+ @counter.raise_if_last_attempt(exception)
127
+ # Sleep before the next attempt.
128
+ @delayer.sleep
129
+ # Try the block again.
130
+ retry
213
131
  end
214
132
  end
215
133
  end #Wait
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wait
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-21 00:00:00.000000000 Z
12
+ date: 2013-01-06 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description:
15
15
  email: todd@paperlesspost.com