activejob-retry 0.5.1 → 0.6.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.
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