resque-retry 1.0.0.a → 1.0.0

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