delayed_job 4.0.6 → 4.1.8

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
- SHA1:
3
- metadata.gz: 437dd17835ae0e764f1fbd9ce13c55be2b7ddead
4
- data.tar.gz: abb136834dd2e39edb5a6cf5c846613734c3b518
2
+ SHA256:
3
+ metadata.gz: 3671145ff491daea2d3e26336b54cdae0d4e0293654c11fdcb3a41a516741219
4
+ data.tar.gz: 1dab4a137c0cec054cd7c73fc395d5a918f5fc7cc619ae3879687ae17b36a439
5
5
  SHA512:
6
- metadata.gz: b89e00ad9d939277b9f2da9e94a1b45310c9b7ea3b7e3f7535f434eb8b9ad46a6589ef541e76501e5c2da07cb982fb337453d6c5b642b2e20274daa1c0321452
7
- data.tar.gz: 3cb7e586ac2a1a7ea5967d5e35bf3cd325943bf37e28e913e77a787363c0efa46ab891c07c5f157b88a19f8bfda5444839b9b8071e9bc1feb8bcb715773089f9
6
+ metadata.gz: 707798d3698ef7e67896381a43b75f03f0b63653c79c514b3e932b53b6883ded11476fe8b5749e44b5722562d7f479fdb6b70f46c9c279fe08fe9eaafb0110a6
7
+ data.tar.gz: 917341ca90f82622329579ae796378b241822903f5570488e2a6f9dc785028f7e14567c395d5258b409ba8b2c30ee062c0cfeaf985e094b185416296d0e3a18b
@@ -1,3 +1,57 @@
1
+ 4.1.8 - 2019-08-16
2
+ =================
3
+ * Support for Rails 6.0.0
4
+
5
+ 4.1.7 - 2019-06-20
6
+ =================
7
+ * Fix loading Delayed::PerformableMailer when ActionMailer isn't loaded yet
8
+
9
+ 4.1.6 - 2019-06-19
10
+ =================
11
+ * Properly initialize ActionMailer outside railties (#1077)
12
+ * Fix Psych load_tags support (#1093)
13
+ * Replace REMOVED with FAILED in log message (#1048)
14
+ * Misc doc updates (#1052, #1074, #1064, #1063)
15
+
16
+ 4.1.5 - 2018-04-13
17
+ =================
18
+ * Allow Rails 5.2
19
+
20
+ 4.1.4 - 2017-12-29
21
+ =================
22
+ * Use `yaml_tag` instead of deprecated `yaml_as` (#996)
23
+ * Support ruby 2.5.0
24
+
25
+ 4.1.3 - 2017-05-26
26
+ =================
27
+ * Don't mutate the options hash (#877)
28
+ * Log an error message when a deserialization error occurs (#894)
29
+ * Adding the queue name to the log output (#917)
30
+ * Don't include ClassMethods with MessageSending (#924)
31
+ * Fix YAML deserialization error if original object is soft-deleted (#947)
32
+ * Add support for Rails 5.1 (#982)
33
+
34
+ 4.1.2 - 2016-05-16
35
+ ==================
36
+ * Added Delayed::Worker.queue_attributes
37
+ * Limit what we require in ActiveSupport
38
+ * Fix pid file creation when there is no tmp directory
39
+ * Rails 5 support
40
+
41
+ 4.1.1 - 2015-09-24
42
+ ==================
43
+ * Fix shared specs for back-ends that reload objects
44
+
45
+ 4.1.0 - 2015-09-22
46
+ ==================
47
+ * Alter `Delayed::Command` to work with or without Rails
48
+ * Allow `Delayed::Worker.delay_jobs` configuration to be a proc
49
+ * Add ability to set destroy failed jobs on a per job basis
50
+ * Make `Delayed::Worker.new` idempotent
51
+ * Set quiet from the environment
52
+ * Rescue `Exception` instead of `StandardError` in worker
53
+ * Fix worker crash on serialization error
54
+
1
55
  4.0.6 - 2014-12-22
2
56
  ==================
3
57
  * Revert removing test files from the gem
data/README.md CHANGED
@@ -1,14 +1,17 @@
1
+ **If you're viewing this at https://github.com/collectiveidea/delayed_job,
2
+ you're reading the documentation for the master branch.
3
+ [View documentation for the latest release
4
+ (4.1.8).](https://github.com/collectiveidea/delayed_job/tree/v4.1.8)**
5
+
1
6
  Delayed::Job
2
7
  ============
3
- [![Gem Version](https://badge.fury.io/rb/delayed_job.png)][gem]
4
- [![Build Status](https://travis-ci.org/collectiveidea/delayed_job.png?branch=master)][travis]
5
- [![Dependency Status](https://gemnasium.com/collectiveidea/delayed_job.png?travis)][gemnasium]
6
- [![Code Climate](https://codeclimate.com/github/collectiveidea/delayed_job.png)][codeclimate]
7
- [![Coverage Status](https://coveralls.io/repos/collectiveidea/delayed_job/badge.png?branch=master)][coveralls]
8
+ [![Gem Version](https://badge.fury.io/rb/delayed_job.svg)][gem]
9
+ [![Build Status](https://travis-ci.org/collectiveidea/delayed_job.svg?branch=master)][travis]
10
+ [![Code Climate](https://codeclimate.com/github/collectiveidea/delayed_job.svg)][codeclimate]
11
+ [![Coverage Status](https://coveralls.io/repos/collectiveidea/delayed_job/badge.svg?branch=master)][coveralls]
8
12
 
9
13
  [gem]: https://rubygems.org/gems/delayed_job
10
14
  [travis]: https://travis-ci.org/collectiveidea/delayed_job
11
- [gemnasium]: https://gemnasium.com/collectiveidea/delayed_job
12
15
  [codeclimate]: https://codeclimate.com/github/collectiveidea/delayed_job
13
16
  [coveralls]: https://coveralls.io/r/collectiveidea/delayed_job
14
17
 
@@ -32,8 +35,7 @@ multitude of core tasks. Amongst those tasks are:
32
35
 
33
36
  Installation
34
37
  ============
35
- delayed_job 3.0.0 only supports Rails 3.0+. See the [2.0
36
- branch](https://github.com/collectiveidea/delayed_job/tree/v2.0) for Rails 2.
38
+ delayed_job 3.0.0 only supports Rails 3.0+.
37
39
 
38
40
  delayed_job supports multiple backends for storing the job queue. [See the wiki
39
41
  for other backends](https://github.com/collectiveidea/delayed_job/wiki/Backends).
@@ -58,14 +60,30 @@ running the following command:
58
60
  rails generate delayed_job:active_record
59
61
  rake db:migrate
60
62
 
63
+ For Rails 4.2+, see [below](#active-job)
64
+
61
65
  Development
62
66
  ===========
63
67
  In development mode, if you are using Rails 3.1+, your application code will automatically reload every 100 jobs or when the queue finishes.
64
68
  You no longer need to restart Delayed Job every time you update your code in development.
65
69
 
66
- Rails 4
67
- =======
68
- If you are using the protected_attributes gem, it must appear before delayed_job in your gemfile.
70
+ Active Job
71
+ ==========
72
+ In Rails 4.2+, set the queue_adapter in config/application.rb
73
+
74
+ ```ruby
75
+ config.active_job.queue_adapter = :delayed_job
76
+ ```
77
+
78
+ See the [rails guide](http://guides.rubyonrails.org/active_job_basics.html#setting-the-backend) for more details.
79
+
80
+ Rails 4.x
81
+ =========
82
+ If you are using the protected_attributes gem, it must appear before delayed_job in your gemfile. If your jobs are failing with:
83
+
84
+ ActiveRecord::StatementInvalid: PG::NotNullViolation: ERROR: null value in column "handler" violates not-null constraint
85
+
86
+ then this is the fix you're looking for.
69
87
 
70
88
  Upgrading from 2.x to 3.0.0 on Active Record
71
89
  ============================================
@@ -103,9 +121,17 @@ device = Device.new
103
121
  device.deliver
104
122
  ```
105
123
 
106
- handle_asynchronously can take as options anything you can pass to delay. In
107
- addition, the values can be Proc objects allowing call time evaluation of the
108
- value. For some examples:
124
+ ## Parameters
125
+
126
+ `#handle_asynchronously` and `#delay` take these parameters:
127
+
128
+ - `:priority` (number): lower numbers run first; default is 0 but can be reconfigured (see below)
129
+ - `:run_at` (Time): run the job after this time (probably in the future)
130
+ - `:queue` (string): named queue to put this job in, an alternative to priorities (see below)
131
+
132
+ These params can be Proc objects, allowing call-time evaluation of the value.
133
+
134
+ For example:
109
135
 
110
136
  ```ruby
111
137
  class LongTasks
@@ -176,6 +202,27 @@ Delayed::Job.enqueue job, :queue => 'tracking'
176
202
  handle_asynchronously :tweet_later, :queue => 'tweets'
177
203
  ```
178
204
 
205
+ You can configure default priorities for named queues:
206
+
207
+ ```ruby
208
+ Delayed::Worker.queue_attributes = {
209
+ high_priority: { priority: -10 },
210
+ low_priority: { priority: 10 }
211
+ }
212
+ ```
213
+
214
+ Configured queue priorities can be overriden by passing priority to the delay method
215
+
216
+ ```ruby
217
+ object.delay(:queue => 'high_priority', priority: 0).method
218
+ ```
219
+
220
+ You can start processes to only work certain queues with the `queue` and `queues`
221
+ options defined below. Processes started without specifying a queue will run jobs
222
+ from **any** queue. To effectively have a process that runs jobs where a queue is not
223
+ specified, set a default queue name with `Delayed::Worker.default_queue_name` and
224
+ have the processes run that queue.
225
+
179
226
  Running Jobs
180
227
  ============
181
228
  `script/delayed_job` can be used to manage a background process which will
@@ -282,6 +329,20 @@ NewsletterJob = Struct.new(:text, :emails) do
282
329
  end
283
330
  ```
284
331
 
332
+ To set a per-job default for destroying failed jobs that overrides the Delayed::Worker.destroy_failed_jobs you can define a destroy_failed_jobs? method on the job
333
+
334
+ ```ruby
335
+ NewsletterJob = Struct.new(:text, :emails) do
336
+ def perform
337
+ emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
338
+ end
339
+
340
+ def destroy_failed_jobs?
341
+ false
342
+ end
343
+ end
344
+ ```
345
+
285
346
  To set a default queue name for a custom job that overrides Delayed::Worker.default_queue_name, you can define a queue_name method on the job
286
347
 
287
348
  ```ruby
@@ -314,6 +375,9 @@ Hooks
314
375
  =====
315
376
  You can define hooks on your job that will be called at different stages in the process:
316
377
 
378
+
379
+ **NOTE:** If you are using ActiveJob these hooks are **not** available to your jobs. You will need to use ActiveJob's callbacks. You can find details here https://guides.rubyonrails.org/active_job_basics.html#callbacks
380
+
317
381
  ```ruby
318
382
  class ParanoidNewsletterJob < NewsletterJob
319
383
  def enqueue(job)
@@ -367,7 +431,7 @@ end
367
431
 
368
432
  On error, the job is scheduled again in 5 seconds + N ** 4, where N is the number of attempts or using the job's defined `reschedule_at` method.
369
433
 
370
- The default `Worker.max_attempts` is 25. After this, the job either deleted (default), or left in the database with "failed_at" set.
434
+ The default `Worker.max_attempts` is 25. After this, the job is either deleted (default), or left in the database with "failed_at" set.
371
435
  With the default of 25 attempts, the last retry will be 20 days later, with the last interval being almost 100 hours.
372
436
 
373
437
  The default `Worker.max_run_time` is 4.hours. If your job takes longer than that, another computer could pick it up. It's up to you to
@@ -382,8 +446,18 @@ The default behavior is to read 5 jobs from the queue when finding an available
382
446
 
383
447
  By default all jobs will be queued without a named queue. A default named queue can be specified by using `Delayed::Worker.default_queue_name`.
384
448
 
449
+ If no jobs are found, the worker sleeps for the amount of time specified by the sleep delay option. Set `Delayed::Worker.sleep_delay = 60` for a 60 second sleep time.
450
+
385
451
  It is possible to disable delayed jobs for testing purposes. Set `Delayed::Worker.delay_jobs = false` to execute all jobs realtime.
386
452
 
453
+ Or `Delayed::Worker.delay_jobs` can be a Proc that decides whether to execute jobs inline on a per-job basis:
454
+
455
+ ```ruby
456
+ Delayed::Worker.delay_jobs = ->(job) {
457
+ job.queue != 'inline'
458
+ }
459
+ ```
460
+
387
461
  You may need to raise exceptions on SIGTERM signals, `Delayed::Worker.raise_signal_exceptions = :term` will cause the worker to raise a `SignalException` causing the running job to abort and be unlocked, which makes the job available to other workers. The default for this option is false.
388
462
 
389
463
  Here is an example of changing job parameters in Rails:
@@ -1,15 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+
1
3
  Gem::Specification.new do |spec|
2
- spec.add_dependency 'activesupport', ['>= 3.0', '< 5.0']
4
+ spec.add_dependency 'activesupport', ['>= 3.0', '< 6.1']
3
5
  spec.authors = ['Brandon Keepers', 'Brian Ryckbost', 'Chris Gaffney', 'David Genord II', 'Erik Michaels-Ober', 'Matt Griffin', 'Steve Richert', 'Tobias Lütke']
4
6
  spec.description = 'Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background. It is a direct extraction from Shopify where the job table is responsible for a multitude of core tasks.'
5
7
  spec.email = ['brian@collectiveidea.com']
6
8
  spec.files = %w[CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md Rakefile delayed_job.gemspec]
7
- spec.files += Dir.glob('{contrib,lib,recipes,spec}/**/*')
9
+ spec.files += Dir.glob('{contrib,lib,recipes,spec}/**/*') # rubocop:disable SpaceAroundOperators
8
10
  spec.homepage = 'http://github.com/collectiveidea/delayed_job'
9
11
  spec.licenses = ['MIT']
10
12
  spec.name = 'delayed_job'
11
13
  spec.require_paths = ['lib']
12
14
  spec.summary = 'Database-backed asynchronous priority queue system -- Extracted from Shopify'
13
15
  spec.test_files = Dir.glob('spec/**/*')
14
- spec.version = '4.0.6'
16
+ spec.version = '4.1.8'
15
17
  end
@@ -7,32 +7,16 @@ module Delayed
7
7
 
8
8
  module ClassMethods
9
9
  # Add a job to the queue
10
- def enqueue(*args) # rubocop:disable CyclomaticComplexity
11
- options = args.extract_options!
12
- options[:payload_object] ||= args.shift
13
- options[:priority] ||= Delayed::Worker.default_priority
14
-
15
- if options[:queue].nil?
16
- if options[:payload_object].respond_to?(:queue_name)
17
- options[:queue] = options[:payload_object].queue_name
18
- end
19
- options[:queue] ||= Delayed::Worker.default_queue_name
20
- end
21
-
22
- if args.size > 0
23
- warn '[DEPRECATION] Passing multiple arguments to `#enqueue` is deprecated. Pass a hash with :priority and :run_at.'
24
- options[:priority] = args.first || options[:priority]
25
- options[:run_at] = args[1]
26
- end
27
-
28
- unless options[:payload_object].respond_to?(:perform)
29
- raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
30
- end
10
+ def enqueue(*args)
11
+ job_options = Delayed::Backend::JobPreparer.new(*args).prepare
12
+ enqueue_job(job_options)
13
+ end
31
14
 
15
+ def enqueue_job(options)
32
16
  new(options).tap do |job|
33
17
  Delayed::Worker.lifecycle.run_callbacks(:enqueue, job) do
34
18
  job.hook(:enqueue)
35
- Delayed::Worker.delay_jobs ? job.save : job.invoke_job
19
+ Delayed::Worker.delay_job?(job) ? job.save : job.invoke_job
36
20
  end
37
21
  end
38
22
  end
@@ -46,16 +30,13 @@ module Delayed
46
30
  end
47
31
 
48
32
  # Allow the backend to attempt recovery from reserve errors
49
- def recover_from(_error)
50
- end
33
+ def recover_from(_error); end
51
34
 
52
35
  # Hook method that is called before a new worker is forked
53
- def before_fork
54
- end
36
+ def before_fork; end
55
37
 
56
38
  # Hook method that is called after a new worker is forked
57
- def after_fork
58
- end
39
+ def after_fork; end
59
40
 
60
41
  def work_off(num = 100)
61
42
  warn '[DEPRECATION] `Delayed::Job.work_off` is deprecated. Use `Delayed::Worker.new.work_off instead.'
@@ -63,12 +44,18 @@ module Delayed
63
44
  end
64
45
  end
65
46
 
47
+ attr_reader :error
48
+ def error=(error)
49
+ @error = error
50
+ self.last_error = "#{error.message}\n#{error.backtrace.join("\n")}" if respond_to?(:last_error=)
51
+ end
52
+
66
53
  def failed?
67
54
  !!failed_at
68
55
  end
69
56
  alias_method :failed, :failed?
70
57
 
71
- ParseObjectFromYaml = /\!ruby\/\w+\:([^\s]+)/ # rubocop:disable ConstantName
58
+ ParseObjectFromYaml = %r{\!ruby/\w+\:([^\s]+)} # rubocop:disable ConstantName
72
59
 
73
60
  def name
74
61
  @name ||= payload_object.respond_to?(:display_name) ? payload_object.display_name : payload_object.class.name
@@ -93,7 +80,7 @@ module Delayed
93
80
  hook :before
94
81
  payload_object.perform
95
82
  hook :success
96
- rescue => e
83
+ rescue Exception => e # rubocop:disable RescueException
97
84
  hook :error, e
98
85
  raise e
99
86
  ensure
@@ -111,7 +98,7 @@ module Delayed
111
98
  def hook(name, *args)
112
99
  if payload_object.respond_to?(name)
113
100
  method = payload_object.method(name)
114
- method.arity == 0 ? method.call : method.call(self, *args)
101
+ method.arity.zero? ? method.call : method.call(self, *args)
115
102
  end
116
103
  rescue DeserializationError # rubocop:disable HandleExceptions
117
104
  end
@@ -139,8 +126,15 @@ module Delayed
139
126
  end
140
127
  end
141
128
 
129
+ def destroy_failed_jobs?
130
+ payload_object.respond_to?(:destroy_failed_jobs?) ? payload_object.destroy_failed_jobs? : Delayed::Worker.destroy_failed_jobs
131
+ rescue DeserializationError
132
+ Delayed::Worker.destroy_failed_jobs
133
+ end
134
+
142
135
  def fail!
143
- update_attributes(:failed_at => self.class.db_time_now)
136
+ self.failed_at = self.class.db_time_now
137
+ save!
144
138
  end
145
139
 
146
140
  protected
@@ -0,0 +1,53 @@
1
+ module Delayed
2
+ module Backend
3
+ class JobPreparer
4
+ attr_reader :options, :args
5
+
6
+ def initialize(*args)
7
+ @options = args.extract_options!.dup
8
+ @args = args
9
+ end
10
+
11
+ def prepare
12
+ set_payload
13
+ set_queue_name
14
+ set_priority
15
+ handle_deprecation
16
+ options
17
+ end
18
+
19
+ private
20
+
21
+ def set_payload
22
+ options[:payload_object] ||= args.shift
23
+ end
24
+
25
+ def set_queue_name
26
+ if options[:queue].nil? && options[:payload_object].respond_to?(:queue_name)
27
+ options[:queue] = options[:payload_object].queue_name
28
+ else
29
+ options[:queue] ||= Delayed::Worker.default_queue_name
30
+ end
31
+ end
32
+
33
+ def set_priority
34
+ queue_attribute = Delayed::Worker.queue_attributes[options[:queue]]
35
+ options[:priority] ||= (queue_attribute && queue_attribute[:priority]) || Delayed::Worker.default_priority
36
+ end
37
+
38
+ def handle_deprecation
39
+ if args.size > 0
40
+ warn '[DEPRECATION] Passing multiple arguments to `#enqueue` is deprecated. Pass a hash with :priority and :run_at.'
41
+ options[:priority] = args.first || options[:priority]
42
+ options[:run_at] = args[1]
43
+ end
44
+
45
+ # rubocop:disable GuardClause
46
+ unless options[:payload_object].respond_to?(:perform)
47
+ raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
48
+ end
49
+ # rubocop:enabled GuardClause
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,6 +1,6 @@
1
1
  require File.expand_path('../../../../spec/sample_jobs', __FILE__)
2
2
 
3
- require 'active_support/core_ext'
3
+ require 'active_support/core_ext/numeric/time'
4
4
 
5
5
  shared_examples_for 'a delayed_job backend' do
6
6
  let(:worker) { Delayed::Worker.new }
@@ -113,6 +113,12 @@ shared_examples_for 'a delayed_job backend' do
113
113
  job = described_class.enqueue M::ModuleJob.new
114
114
  expect { job.invoke_job }.to change { M::ModuleJob.runs }.from(0).to(1)
115
115
  end
116
+
117
+ it 'does not mutate the options hash' do
118
+ options = {:priority => 1}
119
+ described_class.enqueue SimpleJob.new, options
120
+ expect(options).to eq(:priority => 1)
121
+ end
116
122
  end
117
123
 
118
124
  context 'with delay_jobs = false' do
@@ -161,7 +167,7 @@ shared_examples_for 'a delayed_job backend' do
161
167
  job = described_class.enqueue(CallbackJob.new)
162
168
  expect(job.payload_object).to receive(:perform).and_raise(RuntimeError.new('fail'))
163
169
 
164
- expect { job.invoke_job }.to raise_error
170
+ expect { job.invoke_job }.to raise_error(RuntimeError)
165
171
  expect(CallbackJob.messages).to eq(['enqueue', 'before', 'error: RuntimeError', 'after'])
166
172
  end
167
173
 
@@ -279,6 +285,7 @@ shared_examples_for 'a delayed_job backend' do
279
285
  after do
280
286
  Delayed::Worker.max_priority = nil
281
287
  Delayed::Worker.min_priority = nil
288
+ Delayed::Worker.queue_attributes = {}
282
289
  end
283
290
 
284
291
  it 'fetches jobs ordered by priority' do
@@ -314,6 +321,18 @@ shared_examples_for 'a delayed_job backend' do
314
321
  end
315
322
  expect(described_class.reserve(worker)).to be_nil
316
323
  end
324
+
325
+ it 'sets job priority based on queue_attributes configuration' do
326
+ Delayed::Worker.queue_attributes = {'job_tracking' => {:priority => 4}}
327
+ job = described_class.enqueue :payload_object => NamedQueueJob.new
328
+ expect(job.priority).to eq(4)
329
+ end
330
+
331
+ it 'sets job priority based on the passed in priority overrideing queue_attributes configuration' do
332
+ Delayed::Worker.queue_attributes = {'job_tracking' => {:priority => 4}}
333
+ job = described_class.enqueue :payload_object => NamedQueueJob.new, :priority => 10
334
+ expect(job.priority).to eq(10)
335
+ end
317
336
  end
318
337
 
319
338
  context 'clear_locks!' do
@@ -450,6 +469,34 @@ shared_examples_for 'a delayed_job backend' do
450
469
  end
451
470
  end
452
471
 
472
+ describe 'destroy_failed_jobs' do
473
+ context 'with a SimpleJob' do
474
+ before(:each) do
475
+ @job = described_class.enqueue SimpleJob.new
476
+ end
477
+
478
+ it 'is not defined' do
479
+ expect(@job.destroy_failed_jobs?).to be true
480
+ end
481
+
482
+ it 'uses the destroy failed jobs value on the payload when defined' do
483
+ expect(@job.payload_object).to receive(:destroy_failed_jobs?).and_return(false)
484
+ expect(@job.destroy_failed_jobs?).to be false
485
+ end
486
+ end
487
+
488
+ context 'with a job that raises DserializationError' do
489
+ before(:each) do
490
+ @job = described_class.new :handler => '--- !ruby/struct:GoingToRaiseArgError {}'
491
+ end
492
+
493
+ it 'falls back reasonably' do
494
+ expect(YAML).to receive(:load_dj).and_raise(ArgumentError)
495
+ expect(@job.destroy_failed_jobs?).to be true
496
+ end
497
+ end
498
+ end
499
+
453
500
  describe 'yaml serialization' do
454
501
  context 'when serializing jobs' do
455
502
  it 'raises error ArgumentError for new records' do
@@ -476,7 +523,8 @@ shared_examples_for 'a delayed_job backend' do
476
523
  it 'reloads changed attributes' do
477
524
  story = Story.create(:text => 'hello')
478
525
  job = story.delay.tell
479
- story.update_attributes :text => 'goodbye'
526
+ story.text = 'goodbye'
527
+ story.save!
480
528
  expect(job.reload.payload_object.object.text).to eq('goodbye')
481
529
  end
482
530
 
@@ -500,6 +548,7 @@ shared_examples_for 'a delayed_job backend' do
500
548
  Delayed::Worker.max_run_time = 1.second
501
549
  job = Delayed::Job.create :payload_object => LongRunningJob.new
502
550
  worker.run(job)
551
+ expect(job.error).to_not be_nil
503
552
  expect(job.reload.last_error).to match(/expired/)
504
553
  expect(job.reload.last_error).to match(/Delayed::Worker\.max_run_time is only 1 second/)
505
554
  expect(job.attempts).to eq(1)
@@ -513,6 +562,7 @@ shared_examples_for 'a delayed_job backend' do
513
562
  it 'marks the job as failed' do
514
563
  Delayed::Worker.destroy_failed_jobs = false
515
564
  job = described_class.create! :handler => '--- !ruby/object:JobThatDoesNotExist {}'
565
+ expect_any_instance_of(described_class).to receive(:destroy_failed_jobs?).and_return(false)
516
566
  worker.work_off
517
567
  job.reload
518
568
  expect(job).to be_failed
@@ -535,6 +585,7 @@ shared_examples_for 'a delayed_job backend' do
535
585
  Delayed::Worker.max_attempts = 1
536
586
  worker.run(@job)
537
587
  @job.reload
588
+ expect(@job.error).to_not be_nil
538
589
  expect(@job.last_error).to match(/did not work/)
539
590
  expect(@job.attempts).to eq(1)
540
591
  expect(@job).to be_failed
@@ -617,6 +668,10 @@ shared_examples_for 'a delayed_job backend' do
617
668
  end
618
669
 
619
670
  context 'and we want to destroy jobs' do
671
+ after do
672
+ Delayed::Worker.destroy_failed_jobs = true
673
+ end
674
+
620
675
  it_behaves_like 'any failure more than Worker.max_attempts times'
621
676
 
622
677
  it 'is destroyed if it failed more than Worker.max_attempts times' do
@@ -624,6 +679,13 @@ shared_examples_for 'a delayed_job backend' do
624
679
  Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
625
680
  end
626
681
 
682
+ it 'is destroyed if the job has destroy failed jobs set' do
683
+ Delayed::Worker.destroy_failed_jobs = false
684
+ expect(@job).to receive(:destroy_failed_jobs?).and_return(true)
685
+ expect(@job).to receive(:destroy)
686
+ Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
687
+ end
688
+
627
689
  it 'is not destroyed if failed fewer than Worker.max_attempts times' do
628
690
  expect(@job).not_to receive(:destroy)
629
691
  (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
@@ -641,15 +703,35 @@ shared_examples_for 'a delayed_job backend' do
641
703
 
642
704
  it_behaves_like 'any failure more than Worker.max_attempts times'
643
705
 
644
- it 'is failed if it failed more than Worker.max_attempts times' do
645
- expect(@job.reload).not_to be_failed
646
- Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
647
- expect(@job.reload).to be_failed
706
+ context 'and destroy failed jobs is false' do
707
+ it 'is failed if it failed more than Worker.max_attempts times' do
708
+ expect(@job.reload).not_to be_failed
709
+ Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
710
+ expect(@job.reload).to be_failed
711
+ end
712
+
713
+ it 'is not failed if it failed fewer than Worker.max_attempts times' do
714
+ (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
715
+ expect(@job.reload).not_to be_failed
716
+ end
648
717
  end
649
718
 
650
- it 'is not failed if it failed fewer than Worker.max_attempts times' do
651
- (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
652
- expect(@job.reload).not_to be_failed
719
+ context 'and destroy failed jobs for job is false' do
720
+ before do
721
+ Delayed::Worker.destroy_failed_jobs = true
722
+ end
723
+
724
+ it 'is failed if it failed more than Worker.max_attempts times' do
725
+ expect(@job).to receive(:destroy_failed_jobs?).and_return(false)
726
+ expect(@job.reload).not_to be_failed
727
+ Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
728
+ expect(@job.reload).to be_failed
729
+ end
730
+
731
+ it 'is not failed if it failed fewer than Worker.max_attempts times' do
732
+ (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
733
+ expect(@job.reload).not_to be_failed
734
+ end
653
735
  end
654
736
  end
655
737
  end