wait 0.2.2 → 0.3.2

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 (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