activejob-retry 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f76c2188de3c777b923674f887df0b8e743f6568
4
- data.tar.gz: 2bef53ab8bf06175091c59c3fe44de6b5c8281a0
3
+ metadata.gz: d4ed8260c84b5c95a405c8cadb2a2b0e93ba5070
4
+ data.tar.gz: 1b7d2e89e1843ee73c6461374397a5e0450637c5
5
5
  SHA512:
6
- metadata.gz: 5bee51402193953fa3da7aca0dc44d1259ada8d7e2cc2552f16f6dc8f4ae1e38e4240d964dafd2d969fd87544e433a352f6651be760b18628fdbda1499b268a8
7
- data.tar.gz: bba231f4a031a3e34ec605727b68c9bce910bcff8755f2e0ab1838cd7b005df59f761765901fd0257def15c6e47579c8d77f5d94a65c994e1d2771b488e9c54a
6
+ metadata.gz: 1c03c64268ed4f9a40a0d35b178e96550d24ad6b03b7cc65a1e43440845d6ff39f78fda571e5e06dcc6b83b6a736d97c9449768abca698d2bfd20830cd03d99a
7
+ data.tar.gz: 64233f0b5a15269e040f5f9b306ab58cb27f437dd93fe610dc68f880b28f1758e878674aaa547479b9faac12c74820bec73714891afd743f5690f1c6db78ce5f
@@ -9,10 +9,9 @@ matrix:
9
9
  - rvm: rbx-2
10
10
 
11
11
  rvm:
12
- - 1.9.3
13
- - 2.0.0
14
12
  - 2.1
15
13
  - 2.2
14
+ - 2.3.1
16
15
  - jruby
17
16
  - rbx-2
18
17
 
@@ -20,7 +19,6 @@ script:
20
19
  - bundle exec rubocop
21
20
  - bundle exec rspec spec
22
21
  - bundle exec rake test
23
- - bundle exec rake test:integration
24
22
 
25
23
  services:
26
24
  - redis
@@ -1,3 +1,7 @@
1
+ ## 0.6.0 - May 18, 2016
2
+
3
+ - Change API usage to make improper use harder (by [@isaacseymour](https://github.com/isaacseymour))
4
+
1
5
  ## 0.5.1 - October 28, 2015
2
6
 
3
7
  - Stop warning about QueueClassic - it supports delayed execution from 3.1+ (by [@senny](https://github.com/senny))
@@ -1,189 +1,179 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activejob-retry (0.5.1)
4
+ activejob-retry (0.6.0)
5
5
  activejob (>= 4.2)
6
6
  activesupport (>= 4.2)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- actionmailer (4.2.0)
12
- actionpack (= 4.2.0)
13
- actionview (= 4.2.0)
14
- activejob (= 4.2.0)
11
+ actionmailer (4.2.6)
12
+ actionpack (= 4.2.6)
13
+ actionview (= 4.2.6)
14
+ activejob (= 4.2.6)
15
15
  mail (~> 2.5, >= 2.5.4)
16
16
  rails-dom-testing (~> 1.0, >= 1.0.5)
17
- actionpack (4.2.0)
18
- actionview (= 4.2.0)
19
- activesupport (= 4.2.0)
20
- rack (~> 1.6.0)
17
+ actionpack (4.2.6)
18
+ actionview (= 4.2.6)
19
+ activesupport (= 4.2.6)
20
+ rack (~> 1.6)
21
21
  rack-test (~> 0.6.2)
22
22
  rails-dom-testing (~> 1.0, >= 1.0.5)
23
- rails-html-sanitizer (~> 1.0, >= 1.0.1)
24
- actionview (4.2.0)
25
- activesupport (= 4.2.0)
23
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
24
+ actionview (4.2.6)
25
+ activesupport (= 4.2.6)
26
26
  builder (~> 3.1)
27
27
  erubis (~> 2.7.0)
28
28
  rails-dom-testing (~> 1.0, >= 1.0.5)
29
- rails-html-sanitizer (~> 1.0, >= 1.0.1)
30
- activejob (4.2.0)
31
- activesupport (= 4.2.0)
29
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
30
+ activejob (4.2.6)
31
+ activesupport (= 4.2.6)
32
32
  globalid (>= 0.3.0)
33
- activemodel (4.2.0)
34
- activesupport (= 4.2.0)
33
+ activemodel (4.2.6)
34
+ activesupport (= 4.2.6)
35
35
  builder (~> 3.1)
36
- activerecord (4.2.0)
37
- activemodel (= 4.2.0)
38
- activesupport (= 4.2.0)
36
+ activerecord (4.2.6)
37
+ activemodel (= 4.2.6)
38
+ activesupport (= 4.2.6)
39
39
  arel (~> 6.0)
40
- activesupport (4.2.0)
40
+ activesupport (4.2.6)
41
41
  i18n (~> 0.7)
42
42
  json (~> 1.7, >= 1.7.7)
43
43
  minitest (~> 5.1)
44
44
  thread_safe (~> 0.3, >= 0.3.4)
45
45
  tzinfo (~> 1.1)
46
- arel (6.0.0)
47
- ast (2.0.0)
48
- astrolabe (1.3.0)
49
- parser (>= 2.2.0.pre.3, < 3.0)
50
- backburner (0.4.6)
51
- beaneater (~> 0.3.1)
52
- dante (~> 0.1.5)
53
- beaneater (0.3.3)
46
+ arel (6.0.3)
47
+ ast (2.2.0)
48
+ backburner (1.3.0)
49
+ beaneater (~> 1.0)
50
+ dante (> 0.1.5)
51
+ beaneater (1.0.0)
54
52
  builder (3.2.2)
55
- celluloid (0.16.0)
56
- timers (~> 4.0.0)
57
- connection_pool (2.1.0)
58
- dante (0.1.5)
59
- delayed_job (4.0.6)
60
- activesupport (>= 3.0, < 5.0)
61
- delayed_job_active_record (4.0.3)
62
- activerecord (>= 3.0, < 5.0)
63
- delayed_job (>= 3.0, < 4.1)
53
+ concurrent-ruby (1.0.2)
54
+ connection_pool (2.2.0)
55
+ dante (0.2.0)
56
+ delayed_job (4.1.2)
57
+ activesupport (>= 3.0, < 5.1)
58
+ delayed_job_active_record (4.1.1)
59
+ activerecord (>= 3.0, < 5.1)
60
+ delayed_job (>= 3.0, < 5)
64
61
  diff-lcs (1.2.5)
65
62
  erubis (2.7.0)
66
- globalid (0.3.0)
63
+ globalid (0.3.6)
67
64
  activesupport (>= 4.1.0)
68
- hike (1.2.3)
69
- hitimes (1.2.2)
70
65
  i18n (0.7.0)
71
- json (1.8.1)
72
- loofah (2.0.1)
66
+ json (1.8.3)
67
+ loofah (2.0.3)
73
68
  nokogiri (>= 1.5.9)
74
- mail (2.6.3)
75
- mime-types (>= 1.16, < 3)
76
- mime-types (2.4.3)
77
- mini_portile (0.6.2)
78
- minitest (5.5.0)
69
+ mail (2.6.4)
70
+ mime-types (>= 1.16, < 4)
71
+ mime-types (3.0)
72
+ mime-types-data (~> 3.2015)
73
+ mime-types-data (3.2016.0221)
74
+ mini_portile2 (2.0.0)
75
+ minitest (5.9.0)
79
76
  mono_logger (1.1.0)
80
- multi_json (1.10.1)
81
- nokogiri (1.6.5)
82
- mini_portile (~> 0.6.0)
83
- parser (2.2.0.1)
84
- ast (>= 1.1, < 3.0)
85
- slop (~> 3.4, >= 3.4.5)
86
- pg (0.17.1)
87
- powerpack (0.0.9)
88
- que (0.9.0)
89
- rack (1.6.0)
77
+ multi_json (1.12.0)
78
+ nokogiri (1.6.7.2)
79
+ mini_portile2 (~> 2.0.0.rc2)
80
+ parser (2.3.1.0)
81
+ ast (~> 2.2)
82
+ pg (0.18.4)
83
+ powerpack (0.1.1)
84
+ que (0.11.5)
85
+ rack (1.6.4)
90
86
  rack-protection (1.5.3)
91
87
  rack
92
- rack-test (0.6.2)
88
+ rack-test (0.6.3)
93
89
  rack (>= 1.0)
94
- rails (4.2.0)
95
- actionmailer (= 4.2.0)
96
- actionpack (= 4.2.0)
97
- actionview (= 4.2.0)
98
- activejob (= 4.2.0)
99
- activemodel (= 4.2.0)
100
- activerecord (= 4.2.0)
101
- activesupport (= 4.2.0)
90
+ rails (4.2.6)
91
+ actionmailer (= 4.2.6)
92
+ actionpack (= 4.2.6)
93
+ actionview (= 4.2.6)
94
+ activejob (= 4.2.6)
95
+ activemodel (= 4.2.6)
96
+ activerecord (= 4.2.6)
97
+ activesupport (= 4.2.6)
102
98
  bundler (>= 1.3.0, < 2.0)
103
- railties (= 4.2.0)
99
+ railties (= 4.2.6)
104
100
  sprockets-rails
105
101
  rails-deprecated_sanitizer (1.0.3)
106
102
  activesupport (>= 4.2.0.alpha)
107
- rails-dom-testing (1.0.5)
103
+ rails-dom-testing (1.0.7)
108
104
  activesupport (>= 4.2.0.beta, < 5.0)
109
105
  nokogiri (~> 1.6.0)
110
106
  rails-deprecated_sanitizer (>= 1.0.1)
111
- rails-html-sanitizer (1.0.1)
107
+ rails-html-sanitizer (1.0.3)
112
108
  loofah (~> 2.0)
113
- railties (4.2.0)
114
- actionpack (= 4.2.0)
115
- activesupport (= 4.2.0)
109
+ railties (4.2.6)
110
+ actionpack (= 4.2.6)
111
+ activesupport (= 4.2.6)
116
112
  rake (>= 0.8.7)
117
113
  thor (>= 0.18.1, < 2.0)
118
- rainbow (2.0.0)
119
- rake (10.4.2)
120
- redis (3.2.0)
121
- redis-namespace (1.5.1)
114
+ rainbow (2.1.0)
115
+ rake (11.1.2)
116
+ redis (3.3.0)
117
+ redis-namespace (1.5.2)
122
118
  redis (~> 3.0, >= 3.0.4)
123
- resque (1.25.2)
119
+ resque (1.26.0)
124
120
  mono_logger (~> 1.0)
125
121
  multi_json (~> 1.0)
126
122
  redis-namespace (~> 1.3)
127
123
  sinatra (>= 0.9.2)
128
124
  vegas (~> 0.1.2)
129
- resque-scheduler (4.0.0)
125
+ resque-scheduler (4.2.0)
130
126
  mono_logger (~> 1.0)
131
127
  redis (~> 3.0)
132
128
  resque (~> 1.25)
133
- rufus-scheduler (~> 3.0)
134
- rspec (3.1.0)
135
- rspec-core (~> 3.1.0)
136
- rspec-expectations (~> 3.1.0)
137
- rspec-mocks (~> 3.1.0)
138
- rspec-core (3.1.7)
139
- rspec-support (~> 3.1.0)
140
- rspec-expectations (3.1.2)
129
+ rufus-scheduler (~> 3.2)
130
+ rspec (3.4.0)
131
+ rspec-core (~> 3.4.0)
132
+ rspec-expectations (~> 3.4.0)
133
+ rspec-mocks (~> 3.4.0)
134
+ rspec-core (3.4.4)
135
+ rspec-support (~> 3.4.0)
136
+ rspec-expectations (3.4.0)
141
137
  diff-lcs (>= 1.2.0, < 2.0)
142
- rspec-support (~> 3.1.0)
143
- rspec-its (1.1.0)
138
+ rspec-support (~> 3.4.0)
139
+ rspec-its (1.2.0)
144
140
  rspec-core (>= 3.0.0)
145
141
  rspec-expectations (>= 3.0.0)
146
- rspec-mocks (3.1.3)
147
- rspec-support (~> 3.1.0)
148
- rspec-support (3.1.2)
149
- rubocop (0.28.0)
150
- astrolabe (~> 1.3)
151
- parser (>= 2.2.0.pre.7, < 3.0)
152
- powerpack (~> 0.0.6)
142
+ rspec-mocks (3.4.1)
143
+ diff-lcs (>= 1.2.0, < 2.0)
144
+ rspec-support (~> 3.4.0)
145
+ rspec-support (3.4.1)
146
+ rubocop (0.40.0)
147
+ parser (>= 2.3.1.0, < 3.0)
148
+ powerpack (~> 0.1)
153
149
  rainbow (>= 1.99.1, < 3.0)
154
- ruby-progressbar (~> 1.4)
155
- ruby-progressbar (1.7.1)
156
- rufus-scheduler (3.0.9)
157
- tzinfo
158
- sequel (4.17.0)
159
- sidekiq (3.3.0)
160
- celluloid (>= 0.16.0)
161
- connection_pool (>= 2.0.0)
162
- json
163
- redis (>= 3.0.6)
164
- redis-namespace (>= 1.3.1)
165
- sinatra (1.4.5)
166
- rack (~> 1.4)
150
+ ruby-progressbar (~> 1.7)
151
+ unicode-display_width (~> 1.0, >= 1.0.1)
152
+ ruby-progressbar (1.8.1)
153
+ rufus-scheduler (3.2.1)
154
+ sequel (4.34.0)
155
+ sidekiq (4.1.2)
156
+ concurrent-ruby (~> 1.0)
157
+ connection_pool (~> 2.2, >= 2.2.0)
158
+ redis (~> 3.2, >= 3.2.1)
159
+ sinatra (1.4.7)
160
+ rack (~> 1.5)
167
161
  rack-protection (~> 1.4)
168
- tilt (~> 1.3, >= 1.3.4)
169
- slop (3.6.0)
170
- sprockets (2.12.3)
171
- hike (~> 1.2)
172
- multi_json (~> 1.0)
173
- rack (~> 1.0)
174
- tilt (~> 1.1, != 1.3.0)
175
- sprockets-rails (2.2.2)
176
- actionpack (>= 3.0)
177
- activesupport (>= 3.0)
178
- sprockets (>= 2.8, < 4.0)
179
- sqlite3 (1.3.10)
162
+ tilt (>= 1.3, < 3)
163
+ sprockets (3.6.0)
164
+ concurrent-ruby (~> 1.0)
165
+ rack (> 1, < 3)
166
+ sprockets-rails (3.0.4)
167
+ actionpack (>= 4.0)
168
+ activesupport (>= 4.0)
169
+ sprockets (>= 3.0.0)
170
+ sqlite3 (1.3.11)
180
171
  thor (0.19.1)
181
- thread_safe (0.3.4)
182
- tilt (1.4.1)
183
- timers (4.0.1)
184
- hitimes
172
+ thread_safe (0.3.5)
173
+ tilt (2.0.4)
185
174
  tzinfo (1.2.2)
186
175
  thread_safe (~> 0.1)
176
+ unicode-display_width (1.0.5)
187
177
  vegas (0.1.11)
188
178
  rack (>= 1.0.0)
189
179
 
@@ -209,4 +199,4 @@ DEPENDENCIES
209
199
  sqlite3
210
200
 
211
201
  BUNDLED WITH
212
- 1.10.6
202
+ 1.12.3
data/README.md CHANGED
@@ -1,26 +1,27 @@
1
- ActiveJob::Retry [![Build Status](https://travis-ci.org/gocardless/activejob-retry.svg?branch=master)](https://travis-ci.org/gocardless/activejob-retry)
1
+ ActiveJob::Retry [![Build Status](https://travis-ci.org/isaacseymour/activejob-retry.svg?branch=master)](https://travis-ci.org/isaacseymour/activejob-retry)
2
2
  ================
3
3
 
4
4
  **This is an alpha library** in active development, so the API may change.
5
5
 
6
- Automatic retry functionality for ActiveJob. Just `include ActiveJob::Retry` in your job
7
- class and call one of `constant_retry`, `variable_retry`, `exponential_retry` or `retry_with` to define your
8
- retry strategy:
6
+ Automatic retry functionality for ActiveJob. Just `include ActiveJob::Retry.new(strategy:
7
+ :something, **options)` in your job class:
9
8
 
10
9
  ```ruby
11
10
  class ProcessWebhook < ActiveJob::Base
12
- include ActiveJob::Retry
13
-
14
11
  queue_as :webhooks
15
12
 
16
13
  # Constant delay between attempts:
17
- constant_retry limit: 3, delay: 5.minutes, retryable_exceptions: [TimeoutError, NetworkError]
14
+ include ActiveJob::Retry.new(strategy: :constant,
15
+ limit: 3,
16
+ delay: 5.minutes,
17
+ retryable_exceptions: [TimeoutError, NetworkError])
18
18
 
19
19
  # Or, variable delay between attempts:
20
- variable_retry delays: [1.minute, 5.minutes, 10.minutes, 30.minutes]
20
+ include ActiveJob::Retry.new(strategy: :variable,
21
+ delays: [1.minute, 5.minutes, 10.minutes, 30.minutes])
21
22
 
22
23
  # Or, exponential delay between attempts:
23
- exponential_retry limit: 25
24
+ include ActiveJob::Retry.new(strategy: :exponential, limit: 25)
24
25
 
25
26
  # You can also use a custom backoff strategy by passing an object which responds to
26
27
  # `should_retry?(attempt, exception)`, and `retry_delay(attempt, exception)`
@@ -35,7 +36,7 @@ class ProcessWebhook < ActiveJob::Base
35
36
  end
36
37
  end
37
38
 
38
- retry_with ChaoticBackoffStrategy
39
+ include ActiveJob::Retry.new(strategy: ChaoticBackoffStrategy)
39
40
 
40
41
  def perform(webhook)
41
42
  webhook.process!
@@ -43,10 +44,10 @@ class ProcessWebhook < ActiveJob::Base
43
44
  end
44
45
  ```
45
46
 
46
- The retry will get executed before any `rescue_from` blocks, which will only get executed
47
+ The retry will get executed **before** any `rescue_from` blocks, which will only get executed
47
48
  if the exception is not going to be retried, or has failed the final retry.
48
49
 
49
- #### constant_retry options
50
+ #### constant options
50
51
  | Option | Default | Description |
51
52
  |:---------------------- |:------- |:-------------- |
52
53
  | `limit` | `1` | Maximum number of times to attempt the job (default: 1).
@@ -55,40 +56,41 @@ if the exception is not going to be retried, or has failed the final retry.
55
56
  | `retryable_exceptions` | `nil` | A whitelist of exceptions to retry. When `nil`, all exceptions will result in a retry.
56
57
  | `fatal_exceptions` | `[]` | A blacklist of exceptions to not retry (default: []).
57
58
 
58
- #### exponential_retry options
59
+ #### exponential options
59
60
  | Option | Default | Description |
60
61
  |:---------------------- |:------- |:-------------- |
61
62
  | `limit` | `1` | Maximum number of times to attempt the job (default: 1).
62
63
  | `unlimited_retries` | `false` | If set to `true`, this job will be repeated indefinitely until in succeeds. Use with extreme caution.
63
- | `retryable_exceptions` | `nil` | Same as for [constant_retry](#constant_retry-options).
64
- | `fatal_exceptions` | `[]` | Same as for [constant_retry](#constant_retry-options).
64
+ | `retryable_exceptions` | `nil` | Same as for [constant](#constant-options).
65
+ | `fatal_exceptions` | `[]` | Same as for [constant](#constant-options).
65
66
 
66
- #### variable_retry options
67
+ #### variable options
67
68
 
68
69
  | Option | Default | Description |
69
70
  |:---------------------- |:------- |:------------- |
70
71
  | `delays` | | __required__ An array of delays between attempts in seconds. The first attempt will occur whenever you originally enqueued the job to happen.
71
72
  | `min_delay_multiplier` | | If supplied, each delay will be multiplied by a random number between this and `max_delay_multiplier`.
72
73
  | `max_delay_multiplier` | | The other end of the range for `min_delay_multiplier`. If one is supplied, both must be.
73
- | `retryable_exceptions` | `nil` | Same as for [constant_retry](#constant_retry-options).
74
- | `fatal_exceptions` | `[]` | Same as for [constant_retry](#constant_retry-options).
74
+ | `retryable_exceptions` | `nil` | Same as for [constant](#constant-options).
75
+ | `fatal_exceptions` | `[]` | Same as for [constant](#constant-options).
75
76
 
76
77
  ## Supported backends
77
78
 
78
79
  Any queue adapter which supports delayed enqueuing (i.e. the `enqueue_at`
79
- method) will work with ActiveJob::Retry, however some queue backends have
80
+ method) will work with `ActiveJob::Retry`, however some queue backends have
80
81
  automatic retry logic, which should be disabled. The cleanest way to do this is
81
82
  to use a `rescue_from` in the jobs for which you're using ActiveJob::Retry, so
82
83
  the queue backend never perceives the job as having failed. E.g.:
83
84
 
84
85
  ```ruby
85
86
  class MyJob < ActiveJob::Base
86
- include ActiveJob::Retry
87
+ include ActiveJob::Retry.new(strategy: :constant,
88
+ limit: 3,
89
+ delay: 5,
90
+ retryable_exceptions: [TimeoutError, NetworkError])
87
91
 
88
92
  queue_as :some_job
89
93
 
90
- constant_retry limit: 3, delay: 5, retryable_exceptions: [TimeoutError, NetworkError]
91
-
92
94
  rescue_from(StandardError) { |error| MyErrorService.record(error) }
93
95
 
94
96
  def perform
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rake/testtask'
2
2
  require 'rubygems/package_task'
3
3
 
4
- ACTIVEJOB_ADAPTERS = %w(backburner delayed_job que resque sidekiq)
4
+ ACTIVEJOB_ADAPTERS = %w(backburner delayed_job que resque sidekiq).freeze
5
5
 
6
6
  task default: :test
7
7
  task test: 'test:default'
@@ -49,14 +49,15 @@ namespace :test do
49
49
 
50
50
  namespace :integration do
51
51
  Rake::TestTask.new(
52
- adapter => ["test:env:#{adapter}", 'test:env:integration']) do |t|
53
- t.description = "Run integration tests for #{adapter}"
54
- t.libs << 'test'
55
- t.test_files = FileList['test/integration/**/*_test.rb']
56
- t.verbose = true
57
- t.warning = true
58
- t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
59
- end
52
+ adapter => ["test:env:#{adapter}", 'test:env:integration']
53
+ ) do |t|
54
+ t.description = "Run integration tests for #{adapter}"
55
+ t.libs << 'test'
56
+ t.test_files = FileList['test/integration/**/*_test.rb']
57
+ t.verbose = true
58
+ t.warning = true
59
+ t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
60
+ end
60
61
  end
61
62
  end
62
63
  end
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  * Exponential backoff (varying the delay between retries).
19
19
  * Light and easy to override retry logic.
20
20
  EOL
21
- s.homepage = 'http://github.com/gocardless/activejob-retry'
21
+ s.homepage = 'https://github.com/isaacseymour/activejob-retry'
22
22
  s.license = 'MIT'
23
23
 
24
24
  s.has_rdoc = false
@@ -11,8 +11,17 @@ unless ActiveJob::Base.method_defined?(:deserialize)
11
11
  require 'active_job/retry/deserialize_monkey_patch'
12
12
  end
13
13
 
14
+ def choose_strategy(strategy, options)
15
+ case strategy
16
+ when :constant then ActiveJob::Retry::ConstantBackoffStrategy.new(options)
17
+ when :variable then ActiveJob::Retry::VariableBackoffStrategy.new(options)
18
+ when :exponential then ActiveJob::Retry::ExponentialBackoffStrategy.new(options)
19
+ else strategy
20
+ end
21
+ end
22
+
14
23
  module ActiveJob
15
- module Retry
24
+ class Retry < Module
16
25
  PROBLEMATIC_ADAPTERS = [
17
26
  'ActiveJob::QueueAdapters::InlineAdapter',
18
27
  'ActiveJob::QueueAdapters::QuAdapter',
@@ -20,88 +29,89 @@ module ActiveJob
20
29
  'ActiveJob::QueueAdapters::SuckerPunchAdapter'
21
30
  ].freeze
22
31
 
23
- def self.included(base)
24
- if PROBLEMATIC_ADAPTERS.include?(ActiveJob::Base.queue_adapter.name)
25
- warn("#{ActiveJob::Base.queue_adapter.name} does not support delayed retries, " \
26
- 'so does not work with ActiveJob::Retry. You may experience strange ' \
27
- 'behaviour.')
28
- end
29
-
30
- base.extend(ClassMethods)
31
- end
32
-
33
32
  #################
34
33
  # Configuration #
35
34
  #################
36
-
37
- module ClassMethods
38
- attr_reader :backoff_strategy
39
-
40
- def constant_retry(options)
41
- retry_with(ConstantBackoffStrategy.new(options))
42
- end
43
-
44
- def variable_retry(options)
45
- retry_with(VariableBackoffStrategy.new(options))
35
+ def initialize(strategy: nil, **options)
36
+ check_adapter!
37
+ @backoff_strategy = choose_strategy(strategy, options)
38
+
39
+ unless backoff_strategy_valid?
40
+ raise InvalidConfigurationError,
41
+ 'Backoff strategies must define `should_retry?(attempt, exception)`, ' \
42
+ 'and `retry_delay(attempt, exception)`.'
46
43
  end
44
+ end
47
45
 
48
- def exponential_retry(options)
49
- retry_with(ExponentialBackoffStrategy.new(options))
50
- end
46
+ def included(base)
47
+ define_backoff_strategy(base)
48
+ define_retry_attempt_tracking(base)
49
+ define_retry_method(base)
50
+ define_retry_logic(base)
51
+ end
51
52
 
52
- def retry_with(backoff_strategy)
53
- unless backoff_strategy_valid?(backoff_strategy)
54
- raise InvalidConfigurationError,
55
- 'Backoff strategies must define `should_retry?(attempt, exception)`, ' \
56
- 'and `retry_delay(attempt, exception)`.'
57
- end
53
+ private
58
54
 
59
- @backoff_strategy = backoff_strategy
60
- end
55
+ attr_reader :backoff_strategy
61
56
 
62
- def backoff_strategy_valid?(backoff_strategy)
63
- backoff_strategy.respond_to?(:should_retry?) &&
64
- backoff_strategy.respond_to?(:retry_delay) &&
65
- backoff_strategy.method(:should_retry?).arity == 2 &&
66
- backoff_strategy.method(:retry_delay).arity == 2
67
- end
57
+ def define_backoff_strategy(klass)
58
+ klass.instance_variable_set(:@backoff_strategy, @backoff_strategy)
59
+ klass.define_singleton_method(:backoff_strategy) { @backoff_strategy }
68
60
  end
69
61
 
70
- #############################
71
- # Storage of attempt number #
72
- #############################
73
-
74
- def serialize
75
- super.merge('retry_attempt' => retry_attempt)
62
+ def define_retry_attempt_tracking(klass)
63
+ klass.instance_eval do
64
+ define_method(:serialize) do |*args|
65
+ super(*args).merge('retry_attempt' => retry_attempt)
66
+ end
67
+ define_method :deserialize do |job_data|
68
+ super(job_data)
69
+ @retry_attempt = job_data['retry_attempt']
70
+ end
71
+ define_method(:retry_attempt) { @retry_attempt ||= 1 }
72
+ end
76
73
  end
77
74
 
78
- def deserialize(job_data)
79
- super(job_data)
80
- @retry_attempt = job_data['retry_attempt']
75
+ def define_retry_method(klass)
76
+ klass.instance_eval do
77
+ define_method :internal_retry do |exception|
78
+ this_delay = self.class.backoff_strategy.retry_delay(retry_attempt, exception)
79
+ # TODO: This breaks DelayedJob and Resque for some weird ActiveSupport reason.
80
+ # logger.info("Retrying (attempt #{retry_attempt + 1}, waiting #{this_delay}s)")
81
+ @retry_attempt += 1
82
+ retry_job(wait: this_delay)
83
+ end
84
+ end
81
85
  end
82
86
 
83
- def retry_attempt
84
- @retry_attempt ||= 1
87
+ def define_retry_logic(klass)
88
+ klass.instance_eval do
89
+ # Override `rescue_with_handler` to make sure our catch is before callbacks,
90
+ # so `rescue_from`s will only be run after any retry attempts have been exhausted.
91
+ define_method :rescue_with_handler do |exception|
92
+ if self.class.backoff_strategy.should_retry?(retry_attempt, exception)
93
+ internal_retry(exception)
94
+ return true # Exception has been handled
95
+ else
96
+ return super(exception)
97
+ end
98
+ end
99
+ end
85
100
  end
86
101
 
87
- ##########################
88
- # Performing the retries #
89
- ##########################
90
-
91
- # Override `rescue_with_handler` to make sure our catch is before callbacks,
92
- # so `rescue_from`s will only be run after any retry attempts have been exhausted.
93
- def rescue_with_handler(exception)
94
- unless self.class.backoff_strategy.should_retry?(retry_attempt, exception)
95
- return super
102
+ def check_adapter!
103
+ if PROBLEMATIC_ADAPTERS.include?(ActiveJob::Base.queue_adapter.name)
104
+ warn("#{ActiveJob::Base.queue_adapter.name} does not support delayed retries, " \
105
+ 'so does not work with ActiveJob::Retry. You may experience strange ' \
106
+ 'behaviour.')
96
107
  end
108
+ end
97
109
 
98
- this_delay = self.class.backoff_strategy.retry_delay(retry_attempt, exception)
99
- # TODO: This breaks DelayedJob and Resque for some weird ActiveSupport reason.
100
- # logger.info("Retrying (attempt #{retry_attempt + 1}, waiting #{this_delay}s)")
101
- @retry_attempt += 1
102
- retry_job(wait: this_delay)
103
-
104
- true # Exception has been handled
110
+ def backoff_strategy_valid?
111
+ backoff_strategy.respond_to?(:should_retry?) &&
112
+ backoff_strategy.respond_to?(:retry_delay) &&
113
+ backoff_strategy.method(:should_retry?).arity == 2 &&
114
+ backoff_strategy.method(:retry_delay).arity == 2
105
115
  end
106
116
  end
107
117
  end
@@ -1,7 +1,7 @@
1
1
  require 'active_job/retry/constant_options_validator'
2
2
 
3
3
  module ActiveJob
4
- module Retry
4
+ class Retry < Module
5
5
  class ConstantBackoffStrategy
6
6
  def initialize(options)
7
7
  ConstantOptionsValidator.new(options).validate!
@@ -1,7 +1,7 @@
1
1
  require 'active_job/retry/errors'
2
2
 
3
3
  module ActiveJob
4
- module Retry
4
+ class Retry < Module
5
5
  class ConstantOptionsValidator
6
6
  def initialize(options)
7
7
  @options = options
@@ -1,5 +1,5 @@
1
1
  module ActiveJob
2
- module Retry
2
+ class Retry < Module
3
3
  class InvalidConfigurationError < StandardError; end
4
4
  class UnsupportedAdapterError < StandardError; end
5
5
  end
@@ -1,7 +1,7 @@
1
1
  require 'active_job/retry/exponential_options_validator'
2
2
 
3
3
  module ActiveJob
4
- module Retry
4
+ class Retry < Module
5
5
  class ExponentialBackoffStrategy < ConstantBackoffStrategy
6
6
  def initialize(options)
7
7
  ExponentialOptionsValidator.new(options).validate!
@@ -1,7 +1,7 @@
1
1
  require 'active_job/retry/errors'
2
2
 
3
3
  module ActiveJob
4
- module Retry
4
+ class Retry < Module
5
5
  class ExponentialOptionsValidator
6
6
  def initialize(options)
7
7
  @options = options
@@ -2,7 +2,7 @@ require 'active_job/retry/constant_backoff_strategy'
2
2
  require 'active_job/retry/variable_options_validator'
3
3
 
4
4
  module ActiveJob
5
- module Retry
5
+ class Retry < Module
6
6
  class VariableBackoffStrategy < ConstantBackoffStrategy
7
7
  def initialize(options)
8
8
  super(options)
@@ -1,7 +1,7 @@
1
1
  require 'active_job/retry/errors'
2
2
 
3
3
  module ActiveJob
4
- module Retry
4
+ class Retry < Module
5
5
  class VariableOptionsValidator
6
6
  DELAY_MULTIPLIER_KEYS = [:min_delay_multiplier, :max_delay_multiplier].freeze
7
7
 
@@ -1,5 +1,5 @@
1
1
  module ActiveJob
2
- module Retry
3
- VERSION = '0.5.1'
2
+ class Retry < Module
3
+ VERSION = '0.6.0'.freeze
4
4
  end
5
5
  end
@@ -1,19 +1,22 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe ActiveJob::Retry do
4
- subject(:job) do
4
+ let(:strategy) { :constant }
5
+ let(:options) { {} }
6
+ let(:retry_instance) { described_class.new(strategy: strategy, **options) }
7
+ let(:job) do
5
8
  Class.new(ActiveJob::Base) do
6
- include ActiveJob::Retry
7
-
8
9
  def perform(*_args)
9
10
  raise RuntimeError
10
11
  end
11
- end
12
+ end.include(retry_instance)
12
13
  end
13
14
 
14
15
  describe '.constant_retry' do
16
+ let(:strategy) { :constant }
17
+ let(:options) { { limit: 10, delay: 5 } }
18
+
15
19
  it 'sets a ConstantBackoffStrategy' do
16
- job.constant_retry(limit: 10, delay: 5)
17
20
  expect(job.backoff_strategy).to be_a(ActiveJob::Retry::ConstantBackoffStrategy)
18
21
  end
19
22
 
@@ -21,15 +24,17 @@ RSpec.describe ActiveJob::Retry do
21
24
  let(:options) { { limit: -2 } }
22
25
 
23
26
  specify do
24
- expect { job.constant_retry(options) }.
27
+ expect { retry_instance }.
25
28
  to raise_error(ActiveJob::Retry::InvalidConfigurationError)
26
29
  end
27
30
  end
28
31
  end
29
32
 
30
33
  describe '.variable_retry' do
34
+ let(:strategy) { :variable }
35
+ let(:options) { { delays: [0, 5, 10, 60, 200] } }
36
+
31
37
  it 'sets a VariableBackoffStrategy' do
32
- job.variable_retry(delays: [0, 5, 10, 60, 200])
33
38
  expect(job.backoff_strategy).to be_a(ActiveJob::Retry::VariableBackoffStrategy)
34
39
  end
35
40
 
@@ -37,52 +42,58 @@ RSpec.describe ActiveJob::Retry do
37
42
  let(:options) { {} }
38
43
 
39
44
  specify do
40
- expect { job.variable_retry(options) }.
45
+ expect { retry_instance }.
41
46
  to raise_error(ActiveJob::Retry::InvalidConfigurationError)
42
47
  end
43
48
  end
44
49
  end
45
50
 
46
51
  describe '.exponential_retry' do
52
+ let(:strategy) { :exponential }
53
+ let(:options) { { limit: 10 } }
54
+
47
55
  it 'sets an ExponentialBackoffStrategy' do
48
- job.exponential_retry(limit: 10)
49
56
  expect(job.backoff_strategy).to be_a(ActiveJob::Retry::ExponentialBackoffStrategy)
50
57
  end
51
58
 
52
- context 'invalid options' do
59
+ context 'invalid limit' do
53
60
  let(:options) { { limit: -2 } }
54
- let(:options_with_delay) { { limit: 2, delay: 3 } }
55
61
 
56
62
  specify do
57
- expect { job.exponential_retry(options) }.
63
+ expect { retry_instance }.
58
64
  to raise_error(ActiveJob::Retry::InvalidConfigurationError)
59
65
  end
66
+ end
67
+
68
+ context 'invalid option included' do
69
+ let(:options) { { limit: 2, delay: 3 } }
60
70
 
61
71
  specify do
62
- expect { job.exponential_retry(options_with_delay) }.
72
+ expect { retry_instance }.
63
73
  to raise_error(ActiveJob::Retry::InvalidConfigurationError)
64
74
  end
65
75
  end
66
76
  end
67
77
 
68
- describe '.retry_with' do
78
+ describe 'custom strategy' do
79
+ module CustomBackoffStrategy
80
+ def self.should_retry?(_attempt, _exception)
81
+ true
82
+ end
83
+
84
+ def self.retry_delay(_attempt, _exception)
85
+ 5
86
+ end
87
+ end
88
+
69
89
  it 'rejects invalid backoff strategies' do
70
- expect { job.retry_with(Object.new) }.
90
+ expect { described_class.new(strategy: Object.new) }.
71
91
  to raise_error(ActiveJob::Retry::InvalidConfigurationError)
72
92
  end
73
93
 
74
- it 'sets the backoff_strategy when it is valid' do
75
- module CustomBackoffStrategy
76
- def self.should_retry?(_attempt, _exception)
77
- true
78
- end
79
-
80
- def self.retry_delay(_attempt, _exception)
81
- 5
82
- end
83
- end
94
+ let(:strategy) { CustomBackoffStrategy }
84
95
 
85
- job.retry_with(CustomBackoffStrategy)
96
+ it 'sets the backoff_strategy when it is valid' do
86
97
  expect(job.backoff_strategy).to eq(CustomBackoffStrategy)
87
98
  end
88
99
  end
@@ -143,17 +154,16 @@ RSpec.describe ActiveJob::Retry do
143
154
  end
144
155
 
145
156
  describe '#rescue_with_handler' do
146
- let(:backoff_strategy) { described_class::ConstantBackoffStrategy.new(limit: 100) }
157
+ let(:mod) { described_class.new(strategy: :constant, limit: 100) }
147
158
  let(:instance) { job.new }
148
- before { job.retry_with(backoff_strategy) }
149
159
  subject(:perform) { instance.perform_now }
150
160
 
151
161
  context 'when the job should be retried' do
152
162
  before do
153
- expect(backoff_strategy).to receive(:should_retry?).
163
+ expect(job.backoff_strategy).to receive(:should_retry?).
154
164
  with(1, instance_of(RuntimeError)).
155
165
  and_return(true)
156
- expect(backoff_strategy).to receive(:retry_delay).
166
+ expect(job.backoff_strategy).to receive(:retry_delay).
157
167
  with(1, instance_of(RuntimeError)).
158
168
  and_return(5)
159
169
  end
@@ -179,7 +189,7 @@ RSpec.describe ActiveJob::Retry do
179
189
 
180
190
  context 'when the job should not be retried' do
181
191
  before do
182
- expect(backoff_strategy).to receive(:should_retry?).
192
+ expect(job.backoff_strategy).to receive(:should_retry?).
183
193
  with(1, instance_of(RuntimeError)).
184
194
  and_return(false)
185
195
  end
@@ -1,7 +1,7 @@
1
1
  require 'active_job/retry'
2
2
  require 'rspec/its'
3
3
 
4
- ActiveJob::Base.queue_adapter = :resque
4
+ ActiveJob::Base.queue_adapter = :test
5
5
 
6
6
  RSpec.configure do |config|
7
7
  config.expect_with :rspec do |expectations|
@@ -1,5 +1,5 @@
1
1
  class CallbackJob < ActiveJob::Base
2
- include ActiveJob::Retry
2
+ include ActiveJob::Retry.new(strategy: :constant)
3
3
 
4
4
  before_perform ->(job) { job.history << "CallbackJob ran before_perform" }
5
5
  after_perform ->(job) { job.history << "CallbackJob ran after_perform" }
@@ -1,7 +1,7 @@
1
1
  require_relative '../support/job_buffer'
2
2
 
3
3
  class GidJob < ActiveJob::Base
4
- include ActiveJob::Retry
4
+ include ActiveJob::Retry.new(strategy: :constant)
5
5
 
6
6
  def perform(person)
7
7
  JobBuffer.add("Person with ID: #{person.id}")
@@ -1,7 +1,7 @@
1
1
  require_relative '../support/job_buffer'
2
2
 
3
3
  class HelloJob < ActiveJob::Base
4
- include ActiveJob::Retry
4
+ include ActiveJob::Retry.new(strategy: :constant)
5
5
 
6
6
  def perform(greeter = "David")
7
7
  JobBuffer.add("#{greeter} says hello")
@@ -1,5 +1,5 @@
1
1
  class LoggingJob < ActiveJob::Base
2
- include ActiveJob::Retry
2
+ include ActiveJob::Retry.new(strategy: :constant)
3
3
 
4
4
  def perform(dummy)
5
5
  logger.info "Dummy, here is it: #{dummy}"
@@ -1,5 +1,5 @@
1
1
  class NestedJob < ActiveJob::Base
2
- include ActiveJob::Retry
2
+ include ActiveJob::Retry.new(strategy: :constant)
3
3
 
4
4
  def perform
5
5
  LoggingJob.perform_later "NestedJob"
@@ -1,8 +1,7 @@
1
1
  require_relative '../support/job_buffer'
2
2
 
3
3
  class RescueJob < ActiveJob::Base
4
- include ActiveJob::Retry
5
- constant_retry limit: 0, delay: 0
4
+ include ActiveJob::Retry.new(strategy: :constant, limit: 0, delay: 0)
6
5
 
7
6
  class OtherError < StandardError; end
8
7
 
@@ -12,10 +12,9 @@ CODE
12
12
 
13
13
  file 'app/jobs/test_job.rb', <<-CODE
14
14
  class TestJob < ActiveJob::Base
15
- include ActiveJob::Retry
15
+ include ActiveJob::Retry.new(strategy: :constant, limit: 2, delay: 3)
16
16
 
17
17
  queue_as :integration_tests
18
- constant_retry limit: 2, delay: 3
19
18
 
20
19
  rescue_from(RuntimeError) do |e|
21
20
  if arguments[3]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activejob-retry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Isaac Seymour
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-28 00:00:00.000000000 Z
11
+ date: 2016-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -151,7 +151,6 @@ files:
151
151
  - test/adapters/sneakers.rb
152
152
  - test/adapters/sucker_punch.rb
153
153
  - test/cases/adapter_test.rb
154
- - test/cases/argument_serialization_test.rb
155
154
  - test/cases/callbacks_test.rb
156
155
  - test/cases/job_serialization_test.rb
157
156
  - test/cases/logging_test.rb
@@ -183,7 +182,7 @@ files:
183
182
  - test/support/integration/test_case_helpers.rb
184
183
  - test/support/job_buffer.rb
185
184
  - test/support/que/inline.rb
186
- homepage: http://github.com/gocardless/activejob-retry
185
+ homepage: https://github.com/isaacseymour/activejob-retry
187
186
  licenses:
188
187
  - MIT
189
188
  metadata: {}
@@ -203,7 +202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
203
202
  version: '0'
204
203
  requirements: []
205
204
  rubyforge_project:
206
- rubygems_version: 2.4.5.1
205
+ rubygems_version: 2.5.1
207
206
  signing_key:
208
207
  specification_version: 4
209
208
  summary: Automatic retry functionality for ActiveJob.
@@ -1,84 +0,0 @@
1
- require 'helper'
2
- require 'active_job/arguments'
3
- require 'models/person'
4
- require 'active_support/core_ext/hash/indifferent_access'
5
-
6
- class ArgumentSerializationTest < ActiveSupport::TestCase
7
- setup do
8
- @person = Person.find('5')
9
- end
10
-
11
- [ nil, 1, 1.0, 1_000_000_000_000_000_000_000,
12
- 'a', true, false,
13
- [ 1, 'a' ],
14
- { 'a' => 1 }
15
- ].each do |arg|
16
- test "serializes #{arg.class} verbatim" do
17
- assert_arguments_unchanged arg
18
- end
19
- end
20
-
21
- [ :a, Object.new, self, Person.find('5').to_gid ].each do |arg|
22
- test "does not serialize #{arg.class}" do
23
- assert_raises ActiveJob::SerializationError do
24
- ActiveJob::Arguments.serialize [ arg ]
25
- end
26
-
27
- assert_raises ActiveJob::DeserializationError do
28
- ActiveJob::Arguments.deserialize [ arg ]
29
- end
30
- end
31
- end
32
-
33
- test 'should convert records to Global IDs' do
34
- assert_arguments_roundtrip [@person], ['_aj_globalid' => @person.to_gid.to_s]
35
- end
36
-
37
- test 'should dive deep into arrays and hashes' do
38
- assert_arguments_roundtrip [3, [@person]], [3, ['_aj_globalid' => @person.to_gid.to_s]]
39
- assert_arguments_roundtrip [{ 'a' => @person }], [{ 'a' => { '_aj_globalid' => @person.to_gid.to_s }}.with_indifferent_access]
40
- end
41
-
42
- test 'should stringify symbol hash keys' do
43
- assert_equal [ 'a' => 1 ], ActiveJob::Arguments.serialize([ a: 1 ])
44
- end
45
-
46
- test 'should disallow non-string/symbol hash keys' do
47
- assert_raises ActiveJob::SerializationError do
48
- ActiveJob::Arguments.serialize [ { 1 => 2 } ]
49
- end
50
-
51
- assert_raises ActiveJob::SerializationError do
52
- ActiveJob::Arguments.serialize [ { :a => [{ 2 => 3 }] } ]
53
- end
54
-
55
- assert_raises ActiveJob::SerializationError do
56
- ActiveJob::Arguments.serialize [ '_aj_globalid' => 1 ]
57
- end
58
-
59
- assert_raises ActiveJob::SerializationError do
60
- ActiveJob::Arguments.serialize [ :_aj_globalid => 1 ]
61
- end
62
- end
63
-
64
- test 'should not allow non-primitive objects' do
65
- assert_raises ActiveJob::SerializationError do
66
- ActiveJob::Arguments.serialize [Object.new]
67
- end
68
-
69
- assert_raises ActiveJob::SerializationError do
70
- ActiveJob::Arguments.serialize [1, [Object.new]]
71
- end
72
- end
73
-
74
- private
75
- def assert_arguments_unchanged(*args)
76
- assert_arguments_roundtrip args, args
77
- end
78
-
79
- def assert_arguments_roundtrip(args, expected_serialized_args)
80
- serialized = ActiveJob::Arguments.serialize(args)
81
- assert_equal expected_serialized_args, serialized
82
- assert_equal args, ActiveJob::Arguments.deserialize(serialized)
83
- end
84
- end