resque-retry 1.0.0.a → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.md CHANGED
@@ -1,6 +1,6 @@
1
- ## HEAD - will become v1.0.0
1
+ ## 1.0.0 (2012-09-07)
2
2
 
3
- **INCLUDES NON-BACKWARDS COMPATIBLE CHANGES**
3
+ ** !!! WARNING !!! INCLUDES NON-BACKWARDS COMPATIBLE CHANGES **
4
4
 
5
5
  * Fixed issues related to infinate job retries and v1.20.0 of resque.
6
6
  * Minimum gem dependency versions changed: resque >= 1.10.0, resque-scheduler >= 1.9.9
@@ -8,6 +8,7 @@
8
8
  * Web interface will work without needing to `require` your job code. (n.b. less details avaialble via web).
9
9
  * IMPORTANT: `#identifier` method has been namedspaced to `#retry_identifier`.
10
10
  * Bugfix: `Remove` button on retry web interface was not working.
11
+ * Feature: Allow `tagging` exceptions with a module instead of an exception class. (@tils - Tilmann Singer)
11
12
 
12
13
  ## 0.2.2 (2011-12-08)
13
14
 
data/README.md CHANGED
@@ -21,7 +21,7 @@ To install:
21
21
 
22
22
  $ gem install resque-retry
23
23
 
24
- If your using [Bundler][bundler] to manage your dependencies, you should add `gem
24
+ If you're using [Bundler][bundler] to manage your dependencies, you should add `gem
25
25
  'resque-retry'` to your projects `Gemfile`.
26
26
 
27
27
  Add this to your `Rakefile`:
@@ -63,7 +63,7 @@ For more explanation and examples, please see the remaining documentation.
63
63
  Failure Backend & Resque Web Additions
64
64
  --------------------------------------
65
65
 
66
- Lets say your using the Redis failure backend of resque (the default).
66
+ Lets say you're using the Redis failure backend of resque (the default).
67
67
  Every time a job fails, the failure queue is populated with the job and
68
68
  exception details.
69
69
 
@@ -95,7 +95,7 @@ the Redis failed queue, like a normal failure *(without retry)* would.
95
95
 
96
96
  ### Resque Web Additions
97
97
 
98
- If your using the `MultipleWithRetrySuppression` failure backend, you should
98
+ If you're using the `MultipleWithRetrySuppression` failure backend, you should
99
99
  also checkout the resque-web additions!
100
100
 
101
101
  The new Retry tab displays delayed jobs with retry information; the number of
data/Rakefile CHANGED
@@ -10,16 +10,17 @@ task :default => :test
10
10
  ##
11
11
  # Test task.
12
12
  Rake::TestTask.new(:test) do |task|
13
+ task.libs << 'test'
13
14
  task.test_files = FileList['test/*_test.rb']
14
15
  task.verbose = true
15
16
  end
16
17
 
17
18
  ##
18
19
  # docs task.
19
- YARD::Rake::YardocTask.new :yardoc do |t|
20
- t.files = ['lib/**/*.rb']
21
- t.options = ['--output-dir', "doc/",
22
- '--files', 'LICENSE',
23
- '--readme', 'README.md',
24
- '--title', 'resque-retry documentation']
20
+ YARD::Rake::YardocTask.new :yardoc do |task|
21
+ task.files = ['lib/**/*.rb']
22
+ task.options = ['--output-dir', 'doc/',
23
+ '--files', 'LICENSE HISTORY.md',
24
+ '--readme', 'README.md',
25
+ '--title', 'resque-retry documentation']
25
26
  end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Perform some loose sanity checking that were in the correct directory first.
4
+ if [ ! -f "Gemfile" ] || [ ! -f "resque-retry.gemspec" ]; then
5
+ echo " ERROR: You may only run this script from the ROOT directory of the gem. Aborting."
6
+ exit 1
7
+ fi
8
+
9
+ # exit if anything fails.
10
+ set -e
11
+ # echo the commands we execute
12
+ set -o verbose
13
+ # ruby versions to test.
14
+ TEST_RUBIES="ruby-1.8.7-p358,ruby-1.9.2-p320,ruby-1.9.3-p0,jruby-1.6.7.2,rbx-2.0.testing"
15
+ # install dependencies.
16
+ rvm $TEST_RUBIES do bundle
17
+ # run unit tests
18
+ rvm $TEST_RUBIES do bundle exec rake
@@ -3,7 +3,7 @@ require 'resque/failure/multiple'
3
3
  module Resque
4
4
  module Failure
5
5
 
6
- # A multiple failure backend, with retry suppression.
6
+ # A multiple failure backend, with retry suppression
7
7
  #
8
8
  # For example: if you had a job that could retry 5 times, your failure
9
9
  # backends are not notified unless the _final_ retry attempt also fails.
@@ -19,11 +19,13 @@ module Resque
19
19
  class MultipleWithRetrySuppression < Multiple
20
20
  include Resque::Helpers
21
21
 
22
- # Called when the job fails.
22
+ # Called when the job fails
23
23
  #
24
24
  # If the job will retry, suppress the failure from the other backends.
25
25
  # Store the lastest failure information in redis, used by the web
26
26
  # interface.
27
+ #
28
+ # @api private
27
29
  def save
28
30
  if !(retryable? && retrying?)
29
31
  cleanup_retry_failure_log!
@@ -43,12 +45,16 @@ module Resque
43
45
  end
44
46
  end
45
47
 
46
- # Expose this for the hook's use.
48
+ # Expose this for the hook's use
49
+ #
50
+ # @api public
47
51
  def self.failure_key(retry_key)
48
52
  'failure-' + retry_key
49
53
  end
50
54
 
51
55
  protected
56
+
57
+ # Return the class/module of the failed job.
52
58
  def klass
53
59
  constantize(payload['class'])
54
60
  end
@@ -37,24 +37,30 @@ module Resque
37
37
  module ExponentialBackoff
38
38
  include Resque::Plugins::Retry
39
39
 
40
- # Defaults to the number of delays in the backoff strategy.
40
+ # Defaults to the number of delays in the backoff strategy
41
41
  #
42
42
  # @return [Number] maximum number of retries
43
+ #
44
+ # @api private
43
45
  def retry_limit
44
46
  @retry_limit ||= backoff_strategy.length
45
47
  end
46
48
 
47
- # Selects the delay from the backoff strategy.
49
+ # Selects the delay from the backoff strategy
48
50
  #
49
51
  # @return [Number] seconds to delay until the next retry.
52
+ #
53
+ # @api private
50
54
  def retry_delay
51
55
  backoff_strategy[retry_attempt] || backoff_strategy.last
52
56
  end
53
57
 
54
58
  # @abstract
55
- # The backoff strategy is used to vary the delay between retry attempts.
59
+ # The backoff strategy is used to vary the delay between retry attempts
56
60
  #
57
61
  # @return [Array] array of delays. index = retry attempt
62
+ #
63
+ # @api public
58
64
  def backoff_strategy
59
65
  @backoff_strategy ||= [0, 60, 600, 3600, 10_800, 21_600]
60
66
  end
@@ -35,6 +35,8 @@ module Resque
35
35
  module Retry
36
36
 
37
37
  # Copy retry criteria checks on inheritance.
38
+ #
39
+ # @api private
38
40
  def inherited(subclass)
39
41
  super(subclass)
40
42
  subclass.instance_variable_set("@retry_criteria_checks", retry_criteria_checks.dup)
@@ -45,10 +47,12 @@ module Resque
45
47
  # are many/long or may not cleanly convert to strings.
46
48
  #
47
49
  # Builds a retry identifier using the job arguments. This identifier
48
- # is used as part of the redis key.
50
+ # is used as part of the redis key
49
51
  #
50
52
  # @param [Array] args job arguments
51
53
  # @return [String] job identifier
54
+ #
55
+ # @api public
52
56
  def retry_identifier(*args)
53
57
  args_string = args.join('-')
54
58
  args_string.empty? ? nil : args_string
@@ -58,34 +62,42 @@ module Resque
58
62
  # attempts.
59
63
  #
60
64
  # @return [String] redis key
65
+ #
66
+ # @api public
61
67
  def redis_retry_key(*args)
62
68
  ['resque-retry', name, retry_identifier(*args)].compact.join(":").gsub(/\s/, '')
63
69
  end
64
70
 
65
- # Maximum number of retrys we can attempt to successfully perform the job.
71
+ # Maximum number of retrys we can attempt to successfully perform the job
66
72
  #
67
73
  # A retry limit of 0 will *never* retry.
68
74
  # A retry limit of -1 or below will retry forever.
69
75
  #
70
76
  # @return [Fixnum]
77
+ #
78
+ # @api public
71
79
  def retry_limit
72
80
  @retry_limit ||= 1
73
81
  end
74
82
 
75
- # Number of retry attempts used to try and perform the job.
83
+ # Number of retry attempts used to try and perform the job
76
84
  #
77
85
  # The real value is kept in Redis, it is accessed and incremented using
78
86
  # a before_perform hook.
79
87
  #
80
88
  # @return [Fixnum] number of attempts
89
+ #
90
+ # @api public
81
91
  def retry_attempt
82
92
  @retry_attempt ||= 0
83
93
  end
84
94
 
85
95
  # @abstract
86
- # Number of seconds to delay until the job is retried.
87
- #
96
+ # Number of seconds to delay until the job is retried
97
+ #
88
98
  # @return [Number] number of seconds to delay
99
+ #
100
+ # @api public
89
101
  def retry_delay(exception_class = nil)
90
102
  if @retry_exceptions.is_a?(Hash)
91
103
  delay = @retry_exceptions[exception_class] || 0
@@ -100,37 +112,62 @@ module Resque
100
112
  # Number of seconds to sleep after job is requeued
101
113
  #
102
114
  # @return [Number] number of seconds to sleep
115
+ #
116
+ # @api public
103
117
  def sleep_after_requeue
104
118
  @sleep_after_requeue ||= 0
105
119
  end
106
120
 
121
+ # @abstract
122
+ # Specify another resque job (module or class) to delegate retry duties
123
+ # to upon failure
124
+ #
125
+ # @return [Object, nil] class or module if delegate on failure, otherwise nil
126
+ #
127
+ # @api public
107
128
  def retry_job_delegate
108
129
  @retry_job_delegate ||= nil
109
130
  end
110
131
 
111
132
  # @abstract
112
133
  # Modify the arguments used to retry the job. Use this to do something
113
- # other than try the exact same job again.
134
+ # other than try the exact same job again
114
135
  #
115
136
  # @return [Array] new job arguments
137
+ #
138
+ # @api public
116
139
  def args_for_retry(*args)
117
140
  args
118
141
  end
119
142
 
120
- # Convenience method to test whether you may retry on a given exception.
143
+ # Convenience method to test whether you may retry on a given
144
+ # exception
145
+ #
146
+ # @param [Exception] an instance of Exception. Deprecated: can
147
+ # also be a Class
121
148
  #
122
149
  # @return [Boolean]
150
+ #
151
+ # @api public
123
152
  def retry_exception?(exception)
124
153
  return true if retry_exceptions.nil?
125
- !! retry_exceptions.any? { |ex| ex >= exception }
154
+ !! retry_exceptions.any? do |ex|
155
+ if exception.is_a?(Class)
156
+ ex >= exception
157
+ else
158
+ ex === exception
159
+ end
160
+ end
126
161
  end
127
162
 
128
163
  # @abstract
129
- # Controls what exceptions may be retried.
164
+ # Controls what exceptions may be retried
130
165
  #
131
166
  # Default: `nil` - this will retry all exceptions.
132
- #
167
+ #
133
168
  # @return [Array, nil]
169
+ #
170
+ # @api public
134
171
  def retry_exceptions
135
172
  if @retry_exceptions.is_a?(Hash)
136
173
  @retry_exceptions.keys
@@ -139,17 +176,19 @@ module Resque
139
176
  end
140
177
  end
141
178
 
142
- # Test if the retry criteria is valid.
179
+ # Test if the retry criteria is valid
143
180
  #
144
181
  # @param [Exception] exception
145
182
  # @param [Array] args job arguments
146
183
  # @return [Boolean]
184
+ #
185
+ # @api public
147
186
  def retry_criteria_valid?(exception, *args)
148
187
  # if the retry limit was reached, dont bother checking anything else.
149
188
  return false if retry_limit_reached?
150
189
 
151
190
  # We always want to retry if the exception matches.
152
- should_retry = retry_exception?(exception.class)
191
+ should_retry = retry_exception?(exception)
153
192
 
154
193
  # call user retry criteria check blocks.
155
194
  retry_criteria_checks.each do |criteria_check|
@@ -159,17 +198,20 @@ module Resque
159
198
  should_retry
160
199
  end
161
200
 
162
- # Retry criteria checks.
201
+ # Retry criteria checks
163
202
  #
164
203
  # @return [Array]
204
+ #
205
+ # @api public
165
206
  def retry_criteria_checks
166
207
  @retry_criteria_checks ||= []
167
- @retry_criteria_checks
168
208
  end
169
209
 
170
- # Test if the retry limit has been reached.
210
+ # Test if the retry limit has been reached
171
211
  #
172
212
  # @return [Boolean]
213
+ #
214
+ # @api public
173
215
  def retry_limit_reached?
174
216
  if retry_limit == 0
175
217
  true
@@ -181,7 +223,7 @@ module Resque
181
223
  end
182
224
 
183
225
  # Register a retry criteria check callback to be run before retrying
184
- # the job again.
226
+ # the job again
185
227
  #
186
228
  # If any callback returns `true`, the job will be retried.
187
229
  #
@@ -200,11 +242,15 @@ module Resque
200
242
  # @yieldparam exception [Exception] the exception that was raised
201
243
  # @yieldparam args [Array] job arguments
202
244
  # @yieldreturn [Boolean] false == dont retry, true = can retry
245
+ #
246
+ # @api public
203
247
  def retry_criteria_check(&block)
204
248
  retry_criteria_checks << block
205
249
  end
206
250
 
207
- # Retries the job.
251
+ # Retries the job
252
+ #
253
+ # @api private
208
254
  def try_again(exception, *args)
209
255
  # some plugins define retry_delay and have it take no arguments, so rather than break those,
210
256
  # we'll just check here to see whether it takes the additional exception class argument or not
@@ -225,9 +271,11 @@ module Resque
225
271
  sleep(sleep_after_requeue) if sleep_after_requeue > 0
226
272
  end
227
273
 
228
- # Resque before_perform hook.
274
+ # Resque before_perform hook
229
275
  #
230
276
  # Increments and sets the `@retry_attempt` count.
277
+ #
278
+ # @api private
231
279
  def before_perform_retry(*args)
232
280
  @on_failure_retry_hook_already_called = false
233
281
 
@@ -237,21 +285,25 @@ module Resque
237
285
  @retry_attempt = Resque.redis.incr(retry_key) # increment by 1.
238
286
  end
239
287
 
240
- # Resque after_perform hook.
288
+ # Resque after_perform hook
241
289
  #
242
290
  # Deletes retry attempt count from Redis.
291
+ #
292
+ # @api private
243
293
  def after_perform_retry(*args)
244
294
  clean_retry_key(*args)
245
295
  end
246
296
 
247
- # Resque on_failure hook.
297
+ # Resque on_failure hook
248
298
  #
249
299
  # Checks if our retry criteria is valid, if it is we try again.
250
300
  # Otherwise the retry attempt count is deleted from Redis.
251
301
  #
252
- # NOTE: This hook will only allow execution once per job perform attempt.
302
+ # @note This hook will only allow execution once per job perform attempt.
253
303
  # This was added because Resque v1.20.0 calls the hook twice.
254
304
  # IMO; this isn't something resque-retry should have to worry about!
305
+ #
306
+ # @api private
255
307
  def on_failure_retry(exception, *args)
256
308
  return if @on_failure_retry_hook_already_called
257
309
 
@@ -264,6 +316,11 @@ module Resque
264
316
  @on_failure_retry_hook_already_called = true
265
317
  end
266
318
 
319
+ # Used to perform retry criteria check blocks under the job instance's context
320
+ #
321
+ # @return [Object] return value of the criteria check
322
+ #
323
+ # @api private
267
324
  def instance_exec(*args, &block)
268
325
  mname = "__instance_exec_#{Thread.current.object_id.abs}"
269
326
  class << self; self end.class_eval{ define_method(mname, &block) }
@@ -275,6 +332,9 @@ module Resque
275
332
  ret
276
333
  end
277
334
 
335
+ # Clean up retry state from redis once done
336
+ #
337
+ # @api private
278
338
  def clean_retry_key(*args)
279
339
  Resque.redis.del(redis_retry_key(*args))
280
340
  end
@@ -1,51 +1,16 @@
1
1
  require 'cgi'
2
+ require 'resque/server'
3
+ require 'resque_scheduler/server'
2
4
 
3
5
  # Extend Resque::Server to add tabs.
4
6
  module ResqueRetry
5
7
  module Server
6
8
 
9
+ # Adds `resque-retry` web interface elements to `resque-web`
10
+ #
11
+ # @api private
7
12
  def self.included(base)
8
13
  base.class_eval {
9
- helpers do
10
- # builds a retry key for the specified job.
11
- def retry_key_for_job(job)
12
- begin
13
- klass = Resque.constantize(job['class'])
14
- if klass.respond_to?(:redis_retry_key)
15
- klass.redis_retry_key(job['args'])
16
- else
17
- nil
18
- end
19
- rescue NameError
20
- nil
21
- end
22
- end
23
-
24
- # gets the number of retry attempts for a job.
25
- def retry_attempts_for_job(job)
26
- Resque.redis.get(retry_key_for_job(job))
27
- end
28
-
29
- # gets the failure details hash for a job.
30
- def retry_failure_details(retry_key)
31
- Resque.decode(Resque.redis.get("failure_#{retry_key}"))
32
- end
33
-
34
- # reads a 'local' template file.
35
- def local_template(path)
36
- # Is there a better way to specify alternate template locations with sinatra?
37
- File.read(File.join(File.dirname(__FILE__), "server/views/#{path}"))
38
- end
39
-
40
- # cancels job retry
41
- def cancel_retry(job)
42
- klass = Resque.constantize(job['class'])
43
- retry_key = retry_key_for_job(job)
44
- Resque.remove_delayed(klass, *job['args'])
45
- Resque.redis.del("failure_#{retry_key}")
46
- Resque.redis.del(retry_key)
47
- end
48
- end
49
14
 
50
15
  get '/retry' do
51
16
  erb local_template('retry.erb')
@@ -70,10 +35,53 @@ module ResqueRetry
70
35
  }
71
36
  end
72
37
 
38
+ # Helper methods used by retry tab.
39
+ module Helpers
40
+ # builds a retry key for the specified job.
41
+ def retry_key_for_job(job)
42
+ begin
43
+ klass = Resque.constantize(job['class'])
44
+ if klass.respond_to?(:redis_retry_key)
45
+ klass.redis_retry_key(job['args'])
46
+ else
47
+ nil
48
+ end
49
+ rescue NameError
50
+ nil
51
+ end
52
+ end
53
+
54
+ # gets the number of retry attempts for a job.
55
+ def retry_attempts_for_job(job)
56
+ Resque.redis.get(retry_key_for_job(job))
57
+ end
58
+
59
+ # gets the failure details hash for a job.
60
+ def retry_failure_details(retry_key)
61
+ Resque.decode(Resque.redis.get("failure-#{retry_key}"))
62
+ end
63
+
64
+ # reads a 'local' template file.
65
+ def local_template(path)
66
+ # Is there a better way to specify alternate template locations with sinatra?
67
+ File.read(File.join(File.dirname(__FILE__), "server/views/#{path}"))
68
+ end
69
+
70
+ # cancels job retry
71
+ def cancel_retry(job)
72
+ klass = Resque.constantize(job['class'])
73
+ retry_key = retry_key_for_job(job)
74
+ Resque.remove_delayed(klass, *job['args'])
75
+ Resque.redis.del("failure-#{retry_key}")
76
+ Resque.redis.del(retry_key)
77
+ end
78
+ end
79
+
73
80
  end
74
81
  end
75
82
 
76
83
  Resque::Server.tabs << 'Retry'
77
84
  Resque::Server.class_eval do
78
85
  include ResqueRetry::Server
86
+ helpers ResqueRetry::Server::Helpers
79
87
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/test_helper')
1
+ require 'test_helper'
2
2
 
3
3
  class ExponentialBackoffTest < MiniTest::Unit::TestCase
4
4
  def setup
@@ -1,4 +1,17 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/test_helper')
1
+ require 'test_helper'
2
+
3
+ # Mock failure backend for testing MultipleWithRetrySuppression
4
+ class MockFailureBackend < Resque::Failure::Base
5
+ class << self
6
+ attr_accessor :errors
7
+ end
8
+
9
+ def save
10
+ self.class.errors << exception.to_s
11
+ end
12
+
13
+ self.errors = []
14
+ end
2
15
 
3
16
  class MultipleFailureTest < MiniTest::Unit::TestCase
4
17
 
@@ -19,7 +32,7 @@ class MultipleFailureTest < MiniTest::Unit::TestCase
19
32
  end
20
33
 
21
34
  def test_failure_is_passed_on_when_job_class_not_found
22
- skip 'commit 7113b0df to `resque` gem means the failure backend is never called'
35
+ skip 'commit 7113b0df to `resque` gem means the failure backend is never called. effects resque v1.20.0'
23
36
  new_job_class = Class.new(LimitThreeJob).tap { |klass| klass.send(:instance_variable_set, :@queue, LimitThreeJob.instance_variable_get(:@queue)) }
24
37
  Object.send(:const_set, 'LimitThreeJobTemp', new_job_class)
25
38
  Resque.enqueue(LimitThreeJobTemp)
data/test/redis-test.conf CHANGED
@@ -1,4 +1,4 @@
1
- # Redis configuration file example
1
+ # Redis configuration file
2
2
 
3
3
  # By default Redis does not run as a daemon. Use 'yes' if you need it.
4
4
  # Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
@@ -6,7 +6,7 @@ daemonize yes
6
6
 
7
7
  # When run as a daemon, Redis write a pid file in /var/run/redis.pid by default.
8
8
  # You can specify a custom pid file location here.
9
- pidfile ./test/redis-test.pid
9
+ #pidfile ./test/redis-test.pid
10
10
 
11
11
  # Accept connections on the specified port, default is 6379
12
12
  port 9736
@@ -30,16 +30,16 @@ timeout 300
30
30
  # after 900 sec (15 min) if at least 1 key changed
31
31
  # after 300 sec (5 min) if at least 10 keys changed
32
32
  # after 60 sec if at least 10000 keys changed
33
- save 900 1
34
- save 300 10
35
- save 60 10000
33
+ #save 900 1
34
+ #save 300 10
35
+ #save 60 10000
36
36
 
37
37
  # The filename where to dump the DB
38
- dbfilename dump.rdb
38
+ #dbfilename dump.rdb
39
39
 
40
40
  # For default save/load DB in/from the working directory
41
41
  # Note that you must specify a directory not a file name.
42
- dir ./test/
42
+ #dir ./test/
43
43
 
44
44
  # Set server verbosity to 'debug'
45
45
  # it can be one of:
data/test/resque_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/test_helper')
1
+ require 'test_helper'
2
2
 
3
3
  # make sure the worlds not fallen from beneith us.
4
4
  class ResqueTest < MiniTest::Unit::TestCase
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/test_helper')
1
+ require 'test_helper'
2
2
 
3
3
  class RetryCriteriaTest < MiniTest::Unit::TestCase
4
4
  def setup
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/test_helper')
1
+ require 'test_helper'
2
2
 
3
3
  class RetryTest < MiniTest::Unit::TestCase
4
4
  def setup
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/test_helper')
1
+ require 'test_helper'
2
2
 
3
3
  class RetryInheritingChecksTest < MiniTest::Unit::TestCase
4
4
  def setup
data/test/retry_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/test_helper')
1
+ require 'test_helper'
2
2
 
3
3
  class RetryTest < MiniTest::Unit::TestCase
4
4
  def setup
@@ -108,13 +108,17 @@ class RetryTest < MiniTest::Unit::TestCase
108
108
  assert_equal 0, Resque.info[:pending], 'pending jobs'
109
109
  end
110
110
 
111
- def test_can_determine_if_exception_may_be_retried
111
+ def test_can_determine_if_exception_may_be_retried_with_defaults
112
112
  assert_equal true, RetryDefaultsJob.retry_exception?(StandardError), 'StandardError may retry'
113
113
  assert_equal true, RetryDefaultsJob.retry_exception?(CustomException), 'CustomException may retry'
114
114
  assert_equal true, RetryDefaultsJob.retry_exception?(HierarchyCustomException), 'HierarchyCustomException may retry'
115
+ end
115
116
 
117
+ def test_can_determine_if_exception_may_be_retried_with_custom_retry_exceptions
118
+ assert_equal false, RetryCustomExceptionsJob.retry_exception?(StandardError), 'StandardError may not retry'
116
119
  assert_equal true, RetryCustomExceptionsJob.retry_exception?(CustomException), 'CustomException may retry'
117
120
  assert_equal true, RetryCustomExceptionsJob.retry_exception?(HierarchyCustomException), 'HierarchyCustomException may retry'
121
+ assert_equal true, RetryCustomExceptionsJob.retry_exception?(RuntimeError.new.extend(CustomExceptionModule)), 'Extended with CustomExceptionModule may retry'
118
122
  assert_equal false, RetryCustomExceptionsJob.retry_exception?(AnotherCustomException), 'AnotherCustomException may not retry'
119
123
  end
120
124
 
@@ -130,6 +134,17 @@ class RetryTest < MiniTest::Unit::TestCase
130
134
  assert_equal 2, Resque.info[:pending], 'pending jobs'
131
135
  end
132
136
 
137
+ def test_retry_if_failed_and_exception_may_retry_because_of_included_module
138
+ Resque.enqueue(RetryCustomExceptionsJob, 'tagged CustomException')
139
+ 4.times do
140
+ perform_next_job(@worker)
141
+ end
142
+
143
+ assert_equal 4, Resque.info[:failed], 'failed jobs'
144
+ assert_equal 4, Resque.info[:processed], 'processed job'
145
+ assert_equal 1, Resque.info[:pending], 'pending jobs'
146
+ end
147
+
133
148
  def test_do_not_retry_if_failed_and_exception_does_not_allow_retry
134
149
  Resque.enqueue(RetryCustomExceptionsJob, AnotherCustomException)
135
150
  Resque.enqueue(RetryCustomExceptionsJob, RuntimeError)
data/test/server_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/test_helper')
1
+ require 'test_helper'
2
2
 
3
3
  require 'resque-retry/server'
4
4
  ENV['RACK_ENV'] = 'test'
data/test/test_helper.rb CHANGED
@@ -3,12 +3,14 @@ $LOAD_PATH.unshift dir + '/../lib'
3
3
  $TESTING = true
4
4
 
5
5
  require 'rubygems'
6
+ require 'timeout'
6
7
  require 'minitest/unit'
7
8
  require 'minitest/pride'
8
9
  require 'rack/test'
9
10
  require 'mocha'
10
11
 
11
- if RUBY_ENGINE == 'ruby' && RUBY_VERSION >= '1.9'
12
+ # Run code coverage in MRI 1.9 only.
13
+ if RUBY_VERSION >= '1.9' && RUBY_ENGINE == 'ruby'
12
14
  require 'simplecov'
13
15
  SimpleCov.start do
14
16
  add_filter '/test/'
@@ -18,45 +20,31 @@ end
18
20
  require 'resque-retry'
19
21
  require dir + '/test_jobs'
20
22
 
21
- # make sure we can run redis
22
- if !system("which redis-server")
23
- puts '', "** can't find `redis-server` in your path"
24
- puts "** try running `sudo rake install`"
23
+ # make sure we can run redis-server
24
+ if !system('which redis-server')
25
+ puts '', "** `redis-server` was not found in your PATH"
25
26
  abort ''
26
27
  end
27
28
 
29
+ # make sure we can shutdown the server using cli.
30
+ if !system('which redis-cli')
31
+ puts '', "** `redis-cli` was not found in your PATH"
32
+ abort ''
33
+ end
28
34
 
29
- # start our own redis when the tests start,
30
- # kill it when they end
35
+ # This code is run `at_exit` to setup everything before running the tests.
36
+ # Redis server is started before this code block runs.
31
37
  at_exit do
32
38
  next if $!
33
39
 
34
40
  exit_code = MiniTest::Unit.new.run(ARGV)
35
-
36
- pid = `ps -e -o pid,command | grep [r]edis-test`.split(" ")[0]
37
- puts "Killing test redis server..."
38
- `rm -f #{dir}/dump.rdb`
39
- `kill -9 #{pid}`
40
- exit exit_code
41
+ `redis-cli -p 9736 shutdown nosave`
41
42
  end
42
43
 
43
44
  puts "Starting redis for testing at localhost:9736..."
44
45
  `redis-server #{dir}/redis-test.conf`
45
46
  Resque.redis = '127.0.0.1:9736'
46
47
 
47
- # Mock failure backend for testing MultipleWithRetrySuppression
48
- class MockFailureBackend < Resque::Failure::Base
49
- class << self
50
- attr_accessor :errors
51
- end
52
-
53
- def save
54
- self.class.errors << exception.to_s
55
- end
56
-
57
- self.errors = []
58
- end
59
-
60
48
  # Test helpers
61
49
  class MiniTest::Unit::TestCase
62
50
  def perform_next_job(worker, &block)
data/test/test_jobs.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  CustomException = Class.new(StandardError)
2
+ CustomExceptionModule = Module.new
2
3
  HierarchyCustomException = Class.new(CustomException)
3
4
  AnotherCustomException = Class.new(StandardError)
4
5
 
@@ -155,12 +156,13 @@ class RetryCustomExceptionsJob < RetryDefaultsJob
155
156
  @queue = :testing
156
157
 
157
158
  @retry_limit = 5
158
- @retry_exceptions = [CustomException, HierarchyCustomException]
159
+ @retry_exceptions = [CustomException, CustomExceptionModule, HierarchyCustomException]
159
160
 
160
161
  def self.perform(exception)
161
162
  case exception
162
163
  when 'CustomException' then raise CustomException
163
164
  when 'HierarchyCustomException' then raise HierarchyCustomException
165
+ when 'tagged CustomException' then raise AnotherCustomException.new.extend(CustomExceptionModule)
164
166
  when 'AnotherCustomException' then raise AnotherCustomException
165
167
  else raise StandardError
166
168
  end
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-retry
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.a
5
- prerelease: 6
4
+ version: 1.0.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Luke Antins
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-03-11 00:00:00.000000000Z
13
+ date: 2012-09-07 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: resque
17
- requirement: &2158569780 !ruby/object:Gem::Requirement
17
+ requirement: !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,15 @@ dependencies:
22
22
  version: 1.10.0
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *2158569780
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: 1.10.0
26
31
  - !ruby/object:Gem::Dependency
27
32
  name: resque-scheduler
28
- requirement: &2158568800 !ruby/object:Gem::Requirement
33
+ requirement: !ruby/object:Gem::Requirement
29
34
  none: false
30
35
  requirements:
31
36
  - - ! '>='
@@ -33,10 +38,15 @@ dependencies:
33
38
  version: 1.9.9
34
39
  type: :runtime
35
40
  prerelease: false
36
- version_requirements: *2158568800
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: 1.9.9
37
47
  - !ruby/object:Gem::Dependency
38
48
  name: rake
39
- requirement: &2158568180 !ruby/object:Gem::Requirement
49
+ requirement: !ruby/object:Gem::Requirement
40
50
  none: false
41
51
  requirements:
42
52
  - - ! '>='
@@ -44,10 +54,15 @@ dependencies:
44
54
  version: '0'
45
55
  type: :development
46
56
  prerelease: false
47
- version_requirements: *2158568180
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
48
63
  - !ruby/object:Gem::Dependency
49
64
  name: minitest
50
- requirement: &2158567580 !ruby/object:Gem::Requirement
65
+ requirement: !ruby/object:Gem::Requirement
51
66
  none: false
52
67
  requirements:
53
68
  - - ! '>='
@@ -55,10 +70,15 @@ dependencies:
55
70
  version: '0'
56
71
  type: :development
57
72
  prerelease: false
58
- version_requirements: *2158567580
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
59
79
  - !ruby/object:Gem::Dependency
60
80
  name: rack-test
61
- requirement: &2158567120 !ruby/object:Gem::Requirement
81
+ requirement: !ruby/object:Gem::Requirement
62
82
  none: false
63
83
  requirements:
64
84
  - - ! '>='
@@ -66,10 +86,31 @@ dependencies:
66
86
  version: '0'
67
87
  type: :development
68
88
  prerelease: false
69
- version_requirements: *2158567120
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
70
95
  - !ruby/object:Gem::Dependency
71
96
  name: yard
72
- requirement: &2158566580 !ruby/object:Gem::Requirement
97
+ requirement: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rdiscount
113
+ requirement: !ruby/object:Gem::Requirement
73
114
  none: false
74
115
  requirements:
75
116
  - - ! '>='
@@ -77,10 +118,15 @@ dependencies:
77
118
  version: '0'
78
119
  type: :development
79
120
  prerelease: false
80
- version_requirements: *2158566580
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
81
127
  - !ruby/object:Gem::Dependency
82
128
  name: json
83
- requirement: &2158566080 !ruby/object:Gem::Requirement
129
+ requirement: !ruby/object:Gem::Requirement
84
130
  none: false
85
131
  requirements:
86
132
  - - ! '>='
@@ -88,10 +134,15 @@ dependencies:
88
134
  version: '0'
89
135
  type: :development
90
136
  prerelease: false
91
- version_requirements: *2158566080
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
92
143
  - !ruby/object:Gem::Dependency
93
144
  name: simplecov
94
- requirement: &2158565300 !ruby/object:Gem::Requirement
145
+ requirement: !ruby/object:Gem::Requirement
95
146
  none: false
96
147
  requirements:
97
148
  - - ! '>='
@@ -99,10 +150,15 @@ dependencies:
99
150
  version: 0.3.0
100
151
  type: :development
101
152
  prerelease: false
102
- version_requirements: *2158565300
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ none: false
155
+ requirements:
156
+ - - ! '>='
157
+ - !ruby/object:Gem::Version
158
+ version: 0.3.0
103
159
  - !ruby/object:Gem::Dependency
104
160
  name: mocha
105
- requirement: &2158564740 !ruby/object:Gem::Requirement
161
+ requirement: !ruby/object:Gem::Requirement
106
162
  none: false
107
163
  requirements:
108
164
  - - ! '>='
@@ -110,7 +166,12 @@ dependencies:
110
166
  version: '0'
111
167
  type: :development
112
168
  prerelease: false
113
- version_requirements: *2158564740
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ none: false
171
+ requirements:
172
+ - - ! '>='
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
114
175
  description: ! " resque-retry provides retry, delay and exponential backoff support
115
176
  for\n resque jobs.\n\n Features:\n\n * Redis backed retry count/limit.\n * Retry
116
177
  on all or specific exceptions.\n * Exponential backoff (varying the delay between
@@ -125,6 +186,7 @@ files:
125
186
  - Rakefile
126
187
  - README.md
127
188
  - HISTORY.md
189
+ - bin/test-rubies.sh
128
190
  - test/exponential_backoff_test.rb
129
191
  - test/multiple_failure_test.rb
130
192
  - test/redis-test.conf
@@ -158,12 +220,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
158
220
  required_rubygems_version: !ruby/object:Gem::Requirement
159
221
  none: false
160
222
  requirements:
161
- - - ! '>'
223
+ - - ! '>='
162
224
  - !ruby/object:Gem::Version
163
- version: 1.3.1
225
+ version: '0'
164
226
  requirements: []
165
227
  rubyforge_project:
166
- rubygems_version: 1.8.10
228
+ rubygems_version: 1.8.24
167
229
  signing_key:
168
230
  specification_version: 3
169
231
  summary: A resque plugin; provides retry, delay and exponential backoff support for