attempt_this 0.9.0 → 0.9.1

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