attempt_this 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,15 +1,15 @@
1
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
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
8
 
9
- # Calls the policy.
10
- def call
11
- Kernel.sleep(@delay)
12
- @delay *= 2
13
- end
14
- end
9
+ # Calls the policy.
10
+ def call
11
+ Kernel.sleep(@delay)
12
+ @delay *= 2
13
+ end
14
+ end
15
15
  end
@@ -1,14 +1,14 @@
1
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
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
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
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
14
  end
data/spec/attempt_spec.rb CHANGED
@@ -1,291 +1,296 @@
1
1
  require 'spec_helper.rb'
2
2
 
3
3
  describe AttemptThis do
4
- include AttemptThis
5
-
6
- context 'attempt' do
7
- it 'should reject nil enumerator' do
8
- ->{attempt(nil)}.should raise_error(ArgumentError)
9
- end
10
-
11
- it 'should allow execution without a code block' do
12
- ->{attempt(3.times)}.should_not raise_error
13
- end
14
-
15
- it 'should execute code block' do
16
- was_called = false
17
- attempt(1.times) {was_called = true}
18
- was_called.should be_true
19
- end
20
-
21
- it 'should execute the code block only once' do
22
- call_count = 0
23
- attempt(3.times) { call_count += 1 }
24
- call_count.should eql(1)
25
- end
26
-
27
- it 'should not execute the code' do
28
- call_count = 0
29
- attempt(0.times) { call_count += 1 }
30
-
31
- call_count.should eql(0)
32
- end
33
-
34
- it 'should re-throw the original exception' do
35
- ->{attempt(2.times){raise 'Test'}}.should raise_error('Test')
36
- end
37
-
38
- it 'should attempt 3 times' do
39
- call_count = 0
40
- ->{attempt(3.times) { call_count += 1; raise 'Test'}}.should raise_error('Test')
41
- call_count.should eql(3)
42
- end
43
-
44
- it 'should stop trying after a successful attempt' do
45
- attempt_count = 0
46
- attempt(3.times) do
47
- attempt_count += 1
48
- raise 'Test' if attempt_count < 2
49
- end
50
-
51
- attempt_count.should eql(2)
52
- end
53
- end
54
-
55
- context 'with_delay' do
56
- it 'should reject nil delay' do
57
- ->{attempt(3.times).with_delay(nil)}.should raise_error(ArgumentError)
58
- end
59
-
60
- it 'should reject negative delay' do
61
- ->{attempt(3.times).with_delay(-1)}.should raise_error(ArgumentError)
62
- end
63
-
64
- it 'should reject non-number delay' do
65
- ->{attempt(3.times).with_delay('foo')}.should raise_error(ArgumentError)
66
- end
67
-
68
- it 'should accept floating point delay' do
69
- ->{attempt(3.times).with_delay(1.5)}.should_not raise_error
70
- end
71
-
72
- it 'should accept zero delay' do
73
- ->{attempt(3.times).with_delay(0)}.should_not raise_error
74
- end
75
-
76
- it 'should accept calls without a code block' do
77
- ->{attempt(3.times).with_delay(3)}.should_not raise_error
78
- end
79
-
80
- it 'should call the code block' do
81
- was_called = false
82
- attempt(3.times).with_delay(1) do
83
- was_called = true
84
- end
85
- was_called.should be_true
86
- end
87
-
88
- it 'should not sleep on success' do
89
- Kernel.should_not_receive(:sleep)
90
- attempt(3.times).with_delay(3) {}
91
- end
92
-
93
- it 'should sleep for given number of seconds between failed attempts' do
94
- Kernel.should_receive(:sleep).with(5).exactly(2).times
95
- ->{attempt(3.times).with_delay(5) {raise 'Test'}}.should raise_error('Test')
96
- end
97
-
98
- it 'should not fail on zero delay' do
99
- ->{attempt(3.times).with_delay(0) { raise 'Test' }}.should raise_error('Test')
100
- end
101
-
102
- it 'should reject negative start' do
103
- ->{attempt(3.times).with_delay(-1..1)}.should raise_error(ArgumentError)
104
- end
105
-
106
- it 'should reject negative end' do
107
- ->{attempt(3.times).with_delay(1..-1)}.should raise_error(ArgumentError)
108
- end
109
-
110
- it 'should reject non-number range' do
111
- ->{attempt(3.times).with_delay('x'..'y')}.should raise_error(ArgumentError)
112
- end
113
-
114
- it 'should accept floating point range' do
115
- ->{attempt(3.times).with_delay(1.5..3)}.should_not raise_error
116
- end
117
-
118
- it 'should reject inverse range' do
119
- ->{attempt(2.times).with_delay(3..1)}.should raise_error(ArgumentError)
120
- end
121
-
122
- it 'should accept zero seconds interval' do
123
- ->{attempt(3.times).with_delay(0..0)}.should_not raise_error
124
- end
125
-
126
- it 'should wait for specified number of seconds' do
127
- Kernel.should_receive(:sleep).with(5).exactly(2).times
128
- ->{attempt(3.times).with_delay(5..5){raise 'Test'}}.should raise_error('Test')
129
- end
130
-
131
- it 'should reject multiple delay policies' do
132
- ->{attempt(3.times).with_delay(1).with_delay(1)}.should raise_error(ArgumentError)
133
- end
134
- end
135
-
136
- context 'with_reset' do
137
- it 'should reject nil reset proc' do
138
- ->{attempt(3.times).with_reset(nil)}.should raise_error(ArgumentError)
139
- end
140
-
141
- it 'should accept calls without a code block' do
142
- ->{attempt(3.times).with_reset(->{})}.should_not raise_error
143
- end
144
-
145
- it 'should call the code block' do
146
- was_called = false
147
- attempt(1.times).with_reset(->{}) { was_called = true }
148
-
149
- was_called.should be_true
150
- end
151
-
152
- it 'should reject multiple reset procs' do
153
- ->{attempt(3.times).with_reset(->{}).with_reset(->{})}.should raise_error(ArgumentError)
154
- end
155
-
156
- it 'should not be called on successful calls' do
157
- was_called = false
158
-
159
- attempt(1.times).with_reset(->{ was_called = true }) {}
160
- was_called.should be_false
161
- end
162
-
163
- it 'should be called on each failure' do
164
- reset_count = 0
165
-
166
- ->{attempt(3.times).with_reset(->{ reset_count += 1 }) { raise 'Test' }}.should raise_error('Test')
167
- reset_count.should eql(3)
168
- end
169
- end
170
-
171
- context 'and_default_to' do
172
- it 'should reject nil default method' do
173
- ->{attempt(3.times).and_default_to(nil)}.should raise_error(ArgumentError)
174
- end
175
-
176
- it 'should reject duplicate default methods' do
177
- ->{attempt(3.times).and_default_to(->{}).and_default_to(->{})}.should raise_error(ArgumentError)
178
- end
179
-
180
- it 'should allow calls without a code block' do
181
- ->{attempt(3.times).and_default_to(->{})}.should_not raise_error
182
- end
183
-
184
- it 'should call the code block' do
185
- was_called = false
186
- attempt(3.times).and_default_to(->{}){ was_called = true }
187
-
188
- was_called.should be_true
189
- end
190
-
191
- it 'should not be called on success' do
192
- was_called = false
193
- attempt(3.times).and_default_to(->{ was_called = true }) {}
194
- was_called.should be_false
195
- end
196
-
197
- it 'should be called once on the failure' do
198
- call_count = 0
199
- attempt(3.times).and_default_to(->{ call_count += 1 }){ raise 'Test'}
200
-
201
- call_count.should eql(1)
202
- end
203
-
204
- it 'should not be called if code block stopped failing' do
205
- call_count = 0
206
- was_called = false
207
-
208
- attempt(3.times).and_default_to(->{ was_called = true }) { call_count += 1; raise 'Test' if call_count < 2 }
209
- was_called.should be_false
210
- end
211
- end
212
-
213
- context 'with_binary_backoff' do
214
- it 'should reject nil initial delay' do
215
- ->{attempt(3.times).with_binary_backoff(nil)}.should raise_error(ArgumentError)
216
- end
217
-
218
- it 'should reject non-integer initial delay' do
219
- ->{attempt(3.times).with_binary_backoff('foo')}.should raise_error(ArgumentError)
220
- end
221
-
222
- it 'should reject zero initial delay' do
223
- ->{attempt(3.times).with_binary_backoff(0)}.should raise_error(ArgumentError)
224
- end
225
-
226
- it 'should reject negative initial delay' do
227
- ->{attempt(3.times).with_binary_backoff(-1)}.should raise_error(ArgumentError)
228
- end
229
-
230
- it 'should reject multiple policies' do
231
- ->{attempt(3.times).with_binary_backoff(1).with_binary_backoff(2)}.should raise_error(ArgumentError)
232
- end
233
-
234
- it 'should accept calls without a code block' do
235
- ->{attempt(3.times).with_binary_backoff(1)}.should_not raise_error
236
- end
237
-
238
- it 'should call the code block' do
239
- was_called = false
240
-
241
- attempt(3.times).with_binary_backoff(1) { was_called = true }
242
- was_called.should be_true
243
- end
244
-
245
- it 'should double delay on each failure' do
246
- Kernel.should_receive(:sleep).ordered.with(1)
247
- Kernel.should_receive(:sleep).ordered.with(2)
248
- Kernel.should_receive(:sleep).ordered.with(4)
249
-
250
- attempt(4.times).with_binary_backoff(1).and_default_to(->{}) { raise 'Test' }
251
- end
252
- end
253
-
254
- context 'with_filter' do
255
- it 'should reject empty exceptions list' do
256
- ->{attempt.with_filter}.should raise_error(ArgumentError)
257
- end
258
-
259
- it 'should reject non-exceptions' do
260
- ->{attempt.with_filter(1)}.should raise_error(ArgumentError)
261
- end
262
-
263
- it 'should accept calls without a block' do
264
- ->{attempt(2.times).with_filter(Exception)}.should_not raise_error
265
- end
266
-
267
- it 'should call code within the block' do
268
- was_called = false
269
- attempt(2.times).with_filter(Exception){ was_called = true }
270
- was_called.should be_true
271
- end
272
-
273
- it 'should ignore other exceptions' do
274
- count = 0
275
- ->{attempt(3.times).with_filter(StandardError){ count += 1; raise(Exception, 'Test')}}.should raise_error(Exception)
276
- count.should eql(1)
277
- end
278
-
279
- it 'should not ignore specified exceptions' do
280
- count = 0
281
- ->{attempt(3.times).with_filter(RuntimeError){ count += 1; raise 'Test'}}.should raise_error(RuntimeError)
282
- count.should eql(3)
283
- end
284
-
285
- it 'should not ignore derived exceptions' do
286
- count = 0
287
- ->{attempt(3.times).with_filter(Exception){ count += 1; raise(StandardError, 'Test')}}.should raise_error(StandardError)
288
- count.should eql(3)
289
- end
290
- end
4
+ include AttemptThis
5
+
6
+ context 'attempt' do
7
+ it 'should reject nil enumerator' do
8
+ ->{attempt(nil)}.should raise_error(ArgumentError)
9
+ end
10
+
11
+ it 'should allow execution without a code block' do
12
+ ->{attempt(3.times)}.should_not raise_error
13
+ end
14
+
15
+ it 'should execute code block' do
16
+ was_called = false
17
+ attempt(1.times) {was_called = true}
18
+ was_called.should be_true
19
+ end
20
+
21
+ it 'should execute the code block only once' do
22
+ call_count = 0
23
+ attempt(3.times) { call_count += 1 }
24
+ call_count.should eql(1)
25
+ end
26
+
27
+ it 'should not execute the code' do
28
+ call_count = 0
29
+ attempt(0.times) { call_count += 1 }
30
+
31
+ call_count.should eql(0)
32
+ end
33
+
34
+ it 'should re-throw the original exception' do
35
+ ->{attempt(2.times){raise 'Test'}}.should raise_error('Test')
36
+ end
37
+
38
+ it 'should attempt 3 times' do
39
+ call_count = 0
40
+ ->{attempt(3.times) { call_count += 1; raise 'Test'}}.should raise_error('Test')
41
+ call_count.should eql(3)
42
+ end
43
+
44
+ it 'should stop trying after a successful attempt' do
45
+ attempt_count = 0
46
+ attempt(3.times) do
47
+ attempt_count += 1
48
+ raise 'Test' if attempt_count < 2
49
+ end
50
+
51
+ attempt_count.should eql(2)
52
+ end
53
+
54
+ it 'should return block\'s value' do
55
+ expected = UUID.generate.to_s
56
+ attempt(3.times) { expected }.should eql(expected)
57
+ end
58
+ end
59
+
60
+ context 'with_delay' do
61
+ it 'should reject nil delay' do
62
+ ->{attempt(3.times).with_delay(nil)}.should raise_error(ArgumentError)
63
+ end
64
+
65
+ it 'should reject negative delay' do
66
+ ->{attempt(3.times).with_delay(-1)}.should raise_error(ArgumentError)
67
+ end
68
+
69
+ it 'should reject non-number delay' do
70
+ ->{attempt(3.times).with_delay('foo')}.should raise_error(ArgumentError)
71
+ end
72
+
73
+ it 'should accept floating point delay' do
74
+ ->{attempt(3.times).with_delay(1.5)}.should_not raise_error
75
+ end
76
+
77
+ it 'should accept zero delay' do
78
+ ->{attempt(3.times).with_delay(0)}.should_not raise_error
79
+ end
80
+
81
+ it 'should accept calls without a code block' do
82
+ ->{attempt(3.times).with_delay(3)}.should_not raise_error
83
+ end
84
+
85
+ it 'should call the code block' do
86
+ was_called = false
87
+ attempt(3.times).with_delay(1) do
88
+ was_called = true
89
+ end
90
+ was_called.should be_true
91
+ end
92
+
93
+ it 'should not sleep on success' do
94
+ Kernel.should_not_receive(:sleep)
95
+ attempt(3.times).with_delay(3) {}
96
+ end
97
+
98
+ it 'should sleep for given number of seconds between failed attempts' do
99
+ Kernel.should_receive(:sleep).with(5).exactly(2).times
100
+ ->{attempt(3.times).with_delay(5) {raise 'Test'}}.should raise_error('Test')
101
+ end
102
+
103
+ it 'should not fail on zero delay' do
104
+ ->{attempt(3.times).with_delay(0) { raise 'Test' }}.should raise_error('Test')
105
+ end
106
+
107
+ it 'should reject negative start' do
108
+ ->{attempt(3.times).with_delay(-1..1)}.should raise_error(ArgumentError)
109
+ end
110
+
111
+ it 'should reject negative end' do
112
+ ->{attempt(3.times).with_delay(1..-1)}.should raise_error(ArgumentError)
113
+ end
114
+
115
+ it 'should reject non-number range' do
116
+ ->{attempt(3.times).with_delay('x'..'y')}.should raise_error(ArgumentError)
117
+ end
118
+
119
+ it 'should accept floating point range' do
120
+ ->{attempt(3.times).with_delay(1.5..3)}.should_not raise_error
121
+ end
122
+
123
+ it 'should reject inverse range' do
124
+ ->{attempt(2.times).with_delay(3..1)}.should raise_error(ArgumentError)
125
+ end
126
+
127
+ it 'should accept zero seconds interval' do
128
+ ->{attempt(3.times).with_delay(0..0)}.should_not raise_error
129
+ end
130
+
131
+ it 'should wait for specified number of seconds' do
132
+ Kernel.should_receive(:sleep).with(5).exactly(2).times
133
+ ->{attempt(3.times).with_delay(5..5){raise 'Test'}}.should raise_error('Test')
134
+ end
135
+
136
+ it 'should reject multiple delay policies' do
137
+ ->{attempt(3.times).with_delay(1).with_delay(1)}.should raise_error(ArgumentError)
138
+ end
139
+ end
140
+
141
+ context 'with_reset' do
142
+ it 'should reject nil reset proc' do
143
+ ->{attempt(3.times).with_reset(nil)}.should raise_error(ArgumentError)
144
+ end
145
+
146
+ it 'should accept calls without a code block' do
147
+ ->{attempt(3.times).with_reset(->{})}.should_not raise_error
148
+ end
149
+
150
+ it 'should call the code block' do
151
+ was_called = false
152
+ attempt(1.times).with_reset(->{}) { was_called = true }
153
+
154
+ was_called.should be_true
155
+ end
156
+
157
+ it 'should reject multiple reset procs' do
158
+ ->{attempt(3.times).with_reset(->{}).with_reset(->{})}.should raise_error(ArgumentError)
159
+ end
160
+
161
+ it 'should not be called on successful calls' do
162
+ was_called = false
163
+
164
+ attempt(1.times).with_reset(->{ was_called = true }) {}
165
+ was_called.should be_false
166
+ end
167
+
168
+ it 'should be called on each failure' do
169
+ reset_count = 0
170
+
171
+ ->{attempt(3.times).with_reset(->{ reset_count += 1 }) { raise 'Test' }}.should raise_error('Test')
172
+ reset_count.should eql(3)
173
+ end
174
+ end
175
+
176
+ context 'and_default_to' do
177
+ it 'should reject nil default method' do
178
+ ->{attempt(3.times).and_default_to(nil)}.should raise_error(ArgumentError)
179
+ end
180
+
181
+ it 'should reject duplicate default methods' do
182
+ ->{attempt(3.times).and_default_to(->{}).and_default_to(->{})}.should raise_error(ArgumentError)
183
+ end
184
+
185
+ it 'should allow calls without a code block' do
186
+ ->{attempt(3.times).and_default_to(->{})}.should_not raise_error
187
+ end
188
+
189
+ it 'should call the code block' do
190
+ was_called = false
191
+ attempt(3.times).and_default_to(->{}){ was_called = true }
192
+
193
+ was_called.should be_true
194
+ end
195
+
196
+ it 'should not be called on success' do
197
+ was_called = false
198
+ attempt(3.times).and_default_to(->{ was_called = true }) {}
199
+ was_called.should be_false
200
+ end
201
+
202
+ it 'should be called once on the failure' do
203
+ call_count = 0
204
+ attempt(3.times).and_default_to(->{ call_count += 1 }){ raise 'Test'}
205
+
206
+ call_count.should eql(1)
207
+ end
208
+
209
+ it 'should not be called if code block stopped failing' do
210
+ call_count = 0
211
+ was_called = false
212
+
213
+ attempt(3.times).and_default_to(->{ was_called = true }) { call_count += 1; raise 'Test' if call_count < 2 }
214
+ was_called.should be_false
215
+ end
216
+ end
217
+
218
+ context 'with_binary_backoff' do
219
+ it 'should reject nil initial delay' do
220
+ ->{attempt(3.times).with_binary_backoff(nil)}.should raise_error(ArgumentError)
221
+ end
222
+
223
+ it 'should reject non-integer initial delay' do
224
+ ->{attempt(3.times).with_binary_backoff('foo')}.should raise_error(ArgumentError)
225
+ end
226
+
227
+ it 'should reject zero initial delay' do
228
+ ->{attempt(3.times).with_binary_backoff(0)}.should raise_error(ArgumentError)
229
+ end
230
+
231
+ it 'should reject negative initial delay' do
232
+ ->{attempt(3.times).with_binary_backoff(-1)}.should raise_error(ArgumentError)
233
+ end
234
+
235
+ it 'should reject multiple policies' do
236
+ ->{attempt(3.times).with_binary_backoff(1).with_binary_backoff(2)}.should raise_error(ArgumentError)
237
+ end
238
+
239
+ it 'should accept calls without a code block' do
240
+ ->{attempt(3.times).with_binary_backoff(1)}.should_not raise_error
241
+ end
242
+
243
+ it 'should call the code block' do
244
+ was_called = false
245
+
246
+ attempt(3.times).with_binary_backoff(1) { was_called = true }
247
+ was_called.should be_true
248
+ end
249
+
250
+ it 'should double delay on each failure' do
251
+ Kernel.should_receive(:sleep).ordered.with(1)
252
+ Kernel.should_receive(:sleep).ordered.with(2)
253
+ Kernel.should_receive(:sleep).ordered.with(4)
254
+
255
+ attempt(4.times).with_binary_backoff(1).and_default_to(->{}) { raise 'Test' }
256
+ end
257
+ end
258
+
259
+ context 'with_filter' do
260
+ it 'should reject empty exceptions list' do
261
+ ->{attempt.with_filter}.should raise_error(ArgumentError)
262
+ end
263
+
264
+ it 'should reject non-exceptions' do
265
+ ->{attempt.with_filter(1)}.should raise_error(ArgumentError)
266
+ end
267
+
268
+ it 'should accept calls without a block' do
269
+ ->{attempt(2.times).with_filter(Exception)}.should_not raise_error
270
+ end
271
+
272
+ it 'should call code within the block' do
273
+ was_called = false
274
+ attempt(2.times).with_filter(Exception){ was_called = true }
275
+ was_called.should be_true
276
+ end
277
+
278
+ it 'should ignore other exceptions' do
279
+ count = 0
280
+ ->{attempt(3.times).with_filter(StandardError){ count += 1; raise(Exception, 'Test')}}.should raise_error(Exception)
281
+ count.should eql(1)
282
+ end
283
+
284
+ it 'should not ignore specified exceptions' do
285
+ count = 0
286
+ ->{attempt(3.times).with_filter(RuntimeError){ count += 1; raise 'Test'}}.should raise_error(RuntimeError)
287
+ count.should eql(3)
288
+ end
289
+
290
+ it 'should not ignore derived exceptions' do
291
+ count = 0
292
+ ->{attempt(3.times).with_filter(Exception){ count += 1; raise(StandardError, 'Test')}}.should raise_error(StandardError)
293
+ count.should eql(3)
294
+ end
295
+ end
291
296
  end