delayed_job 4.0.3 → 4.0.4

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: f8464e2a78c559301360d0c083d940e73f314406
4
- data.tar.gz: 819bd2dc775364ed9e6311be0a82fb3bdc20d6a6
3
+ metadata.gz: 458beb66285efe46237b4f094fc7eadbbecf3e79
4
+ data.tar.gz: 85e801616a1d8ad319b705386adcd602e3045234
5
5
  SHA512:
6
- metadata.gz: c7d9b76c5f6244b37e79f2df7f5d5d270bd0769366ab0d93967ca02bd9ff434b98801ed92032815d0e0dd1abba5f9617881dd64aeabf09ed0700066f59985a3f
7
- data.tar.gz: 3f99c1bcddf3c835a4eea9cd200b559d1d284d169633016e79b0303549583be78cdfd888671139fe83ecec669b140171bb255d3f4bf03eaa9a4a45ed2c2bc7e2
6
+ metadata.gz: e375922727337f1aa87d578715f5679b1edddc3c15f3505cfb8d084ef063eeeace708ee32c85291bff340996662751640c0627babe0e8c0beb3bb55856e83432
7
+ data.tar.gz: 9da95592d2f8999d8ddb7365234791a117641bba32edb13361ec177b6a2f4d094a75707be4bdbee0eeacb0f7015fd7e4b072a222d2bb074b95497f0524eee3be
@@ -1,3 +1,12 @@
1
+ 4.0.4 - 2014-09-24
2
+ ==================
3
+ * Fix using options passed into delayed_job command
4
+ * Add the ability to set a default queue for a custom job
5
+ * Add the ability to override the max_run_time on a custom job. MUST be lower than worker setting
6
+ * Psych YAML overrides are now exclusively used only when loading a job payload
7
+ * SLEEP_DELAY and READ_AHEAD can be set for the rake task
8
+ * Some updates for Rails 4.2 support
9
+
1
10
  4.0.3 - 2014-09-04
2
11
  ==================
3
12
  * Added --pools option to delayed_job command
data/README.md CHANGED
@@ -242,9 +242,11 @@ NewsletterJob = Struct.new(:text, :emails) do
242
242
  end
243
243
  end
244
244
 
245
- Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.find(:all).collect(&:email))
245
+ Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.pluck(:email))
246
246
  ```
247
+
247
248
  To set a per-job max attempts that overrides the Delayed::Worker.max_attempts you can define a max_attempts method on the job
249
+
248
250
  ```ruby
249
251
  NewsletterJob = Struct.new(:text, :emails) do
250
252
  def perform
@@ -252,10 +254,40 @@ NewsletterJob = Struct.new(:text, :emails) do
252
254
  end
253
255
 
254
256
  def max_attempts
255
- return 3
257
+ 3
256
258
  end
257
259
  end
258
- ````
260
+ ```
261
+
262
+ To set a per-job max run time that overrides the Delayed::Worker.max_run_time you can define a max_run_time method on the job
263
+
264
+ NOTE: this can ONLY be used to set a max_run_time that is lower than Delayed::Worker.max_run_time. Otherwise the lock on the job would expire and another worker would start the working on the in progress job.
265
+
266
+ ```ruby
267
+ NewsletterJob = Struct.new(:text, :emails) do
268
+ def perform
269
+ emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
270
+ end
271
+
272
+ def max_run_time
273
+ 120 # seconds
274
+ end
275
+ end
276
+ ```
277
+
278
+ 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
279
+
280
+ ```ruby
281
+ NewsletterJob = Struct.new(:text, :emails) do
282
+ def perform
283
+ emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
284
+ end
285
+
286
+ def queue_name
287
+ 'newsletter_queue'
288
+ end
289
+ end
290
+ ```
259
291
 
260
292
 
261
293
  Hooks
@@ -332,6 +364,8 @@ By default all jobs will be queued without a named queue. A default named queue
332
364
 
333
365
  It is possible to disable delayed jobs for testing purposes. Set `Delayed::Worker.delay_jobs = false` to execute all jobs realtime.
334
366
 
367
+ 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.
368
+
335
369
  Here is an example of changing job parameters in Rails:
336
370
 
337
371
  ```ruby
@@ -343,6 +377,7 @@ Delayed::Worker.max_run_time = 5.minutes
343
377
  Delayed::Worker.read_ahead = 10
344
378
  Delayed::Worker.default_queue_name = 'default'
345
379
  Delayed::Worker.delay_jobs = !Rails.env.test?
380
+ Delayed::Worker.raise_signal_exceptions = :term
346
381
  ```
347
382
 
348
383
  Cleaning up
@@ -11,5 +11,5 @@ Gem::Specification.new do |spec|
11
11
  spec.require_paths = ['lib']
12
12
  spec.summary = 'Database-backed asynchronous priority queue system -- Extracted from Shopify'
13
13
  spec.test_files = Dir.glob('spec/**/*')
14
- spec.version = '4.0.3'
14
+ spec.version = '4.0.4'
15
15
  end
@@ -7,13 +7,17 @@ module Delayed
7
7
 
8
8
  module ClassMethods
9
9
  # Add a job to the queue
10
- def enqueue(*args)
11
- options = {
12
- :priority => Delayed::Worker.default_priority,
13
- :queue => Delayed::Worker.default_queue_name
14
- }.merge!(args.extract_options!)
15
-
10
+ def enqueue(*args) # rubocop:disable CyclomaticComplexity
11
+ options = args.extract_options!
16
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
17
21
 
18
22
  if args.size > 0
19
23
  warn '[DEPRECATION] Passing multiple arguments to `#enqueue` is deprecated. Pass a hash with :priority and :run_at.'
@@ -25,16 +29,10 @@ module Delayed
25
29
  raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
26
30
  end
27
31
 
28
- if Delayed::Worker.delay_jobs
29
- new(options).tap do |job|
30
- Delayed::Worker.lifecycle.run_callbacks(:enqueue, job) do
31
- job.hook(:enqueue)
32
- job.save
33
- end
34
- end
35
- else
36
- Delayed::Job.new(:payload_object => options[:payload_object]).tap do |job|
37
- job.invoke_job
32
+ new(options).tap do |job|
33
+ Delayed::Worker.lifecycle.run_callbacks(:enqueue, job) do
34
+ job.hook(:enqueue)
35
+ Delayed::Worker.delay_jobs ? job.save : job.invoke_job
38
36
  end
39
37
  end
40
38
  end
@@ -84,14 +82,8 @@ module Delayed
84
82
  end
85
83
 
86
84
  def payload_object
87
- if YAML.respond_to?(:unsafe_load)
88
- # See https://github.com/dtao/safe_yaml
89
- # When the method is there, we need to load our YAML like this...
90
- @payload_object ||= YAML.load(handler, :safe => false)
91
- else
92
- @payload_object ||= YAML.load(handler)
93
- end
94
- rescue TypeError, LoadError, NameError, ArgumentError => e
85
+ @payload_object ||= YAML.load_dj(handler)
86
+ rescue TypeError, LoadError, NameError, ArgumentError, SyntaxError, Psych::SyntaxError => e
95
87
  raise DeserializationError, "Job failed to load: #{e.message}. Handler: #{handler.inspect}"
96
88
  end
97
89
 
@@ -136,6 +128,17 @@ module Delayed
136
128
  payload_object.max_attempts if payload_object.respond_to?(:max_attempts)
137
129
  end
138
130
 
131
+ def max_run_time
132
+ return unless payload_object.respond_to?(:max_run_time)
133
+ return unless (run_time = payload_object.max_run_time)
134
+
135
+ if run_time > Delayed::Worker.max_run_time
136
+ Delayed::Worker.max_run_time
137
+ else
138
+ run_time
139
+ end
140
+ end
141
+
139
142
  def fail!
140
143
  update_attributes(:failed_at => self.class.db_time_now)
141
144
  end
@@ -14,6 +14,7 @@ shared_examples_for 'a delayed_job backend' do
14
14
  Delayed::Worker.min_priority = nil
15
15
  Delayed::Worker.default_priority = 99
16
16
  Delayed::Worker.delay_jobs = true
17
+ Delayed::Worker.default_queue_name = 'default_tracking'
17
18
  SimpleJob.runs = 0
18
19
  described_class.delete_all
19
20
  end
@@ -62,9 +63,19 @@ shared_examples_for 'a delayed_job backend' do
62
63
  end
63
64
 
64
65
  it 'is able to set queue' do
65
- job = described_class.enqueue :payload_object => SimpleJob.new, :queue => 'tracking'
66
+ job = described_class.enqueue :payload_object => NamedQueueJob.new, :queue => 'tracking'
66
67
  expect(job.queue).to eq('tracking')
67
68
  end
69
+
70
+ it 'uses default queue' do
71
+ job = described_class.enqueue :payload_object => SimpleJob.new
72
+ expect(job.queue).to eq(Delayed::Worker.default_queue_name)
73
+ end
74
+
75
+ it "uses the payload object's queue" do
76
+ job = described_class.enqueue :payload_object => NamedQueueJob.new
77
+ expect(job.queue).to eq(NamedQueueJob.new.queue_name)
78
+ end
68
79
  end
69
80
 
70
81
  context 'with multiple arguments' do
@@ -175,9 +186,17 @@ shared_examples_for 'a delayed_job backend' do
175
186
 
176
187
  it 'raises a DeserializationError when the YAML.load raises argument error' do
177
188
  job = described_class.new :handler => '--- !ruby/struct:GoingToRaiseArgError {}'
178
- expect(YAML).to receive(:load).and_raise(ArgumentError)
189
+ expect(YAML).to receive(:load_dj).and_raise(ArgumentError)
179
190
  expect { job.payload_object }.to raise_error(Delayed::DeserializationError)
180
191
  end
192
+
193
+ it 'raises a DeserializationError when the YAML.load raises syntax error' do
194
+ # only test with Psych since the other YAML parsers don't raise a SyntaxError
195
+ if YAML.parser.class.name !~ /syck|yecht/i
196
+ job = described_class.new :handler => 'message: "no ending quote'
197
+ expect { job.payload_object }.to raise_error(Delayed::DeserializationError)
198
+ end
199
+ end
181
200
  end
182
201
 
183
202
  describe 'reserve' do
@@ -398,25 +417,58 @@ shared_examples_for 'a delayed_job backend' do
398
417
  expect(@job.max_attempts).to be_nil
399
418
  end
400
419
 
401
- it 'uses the max_retries value on the payload when defined' do
420
+ it 'uses the max_attempts value on the payload when defined' do
402
421
  expect(@job.payload_object).to receive(:max_attempts).and_return(99)
403
422
  expect(@job.max_attempts).to eq(99)
404
423
  end
405
424
  end
406
425
 
426
+ describe '#max_run_time' do
427
+ before(:each) { @job = described_class.enqueue SimpleJob.new }
428
+
429
+ it 'is not defined' do
430
+ expect(@job.max_run_time).to be_nil
431
+ end
432
+
433
+ it 'results in a default run time when not defined' do
434
+ expect(worker.max_run_time(@job)).to eq(Delayed::Worker::DEFAULT_MAX_RUN_TIME)
435
+ end
436
+
437
+ it 'uses the max_run_time value on the payload when defined' do
438
+ expect(@job.payload_object).to receive(:max_run_time).and_return(30.minutes)
439
+ expect(@job.max_run_time).to eq(30.minutes)
440
+ end
441
+
442
+ it 'results in an overridden run time when defined' do
443
+ expect(@job.payload_object).to receive(:max_run_time).and_return(45.minutes)
444
+ expect(worker.max_run_time(@job)).to eq(45.minutes)
445
+ end
446
+
447
+ it 'job set max_run_time can not exceed default max run time' do
448
+ expect(@job.payload_object).to receive(:max_run_time).and_return(Delayed::Worker::DEFAULT_MAX_RUN_TIME + 60)
449
+ expect(worker.max_run_time(@job)).to eq(Delayed::Worker::DEFAULT_MAX_RUN_TIME)
450
+ end
451
+ end
452
+
407
453
  describe 'yaml serialization' do
408
454
  context 'when serializing jobs' do
409
455
  it 'raises error ArgumentError for new records' do
410
456
  story = Story.new(:text => 'hello')
411
457
  if story.respond_to?(:new_record?)
412
- expect { story.delay.tell }.to raise_error(ArgumentError, 'Jobs cannot be created for non-persisted records')
458
+ expect { story.delay.tell }.to raise_error(
459
+ ArgumentError,
460
+ "job cannot be created for non-persisted record: #{story.inspect}"
461
+ )
413
462
  end
414
463
  end
415
464
 
416
465
  it 'raises error ArgumentError for destroyed records' do
417
466
  story = Story.create(:text => 'hello')
418
467
  story.destroy
419
- expect { story.delay.tell }.to raise_error(ArgumentError, 'Jobs cannot be created for non-persisted records')
468
+ expect { story.delay.tell }.to raise_error(
469
+ ArgumentError,
470
+ "job cannot be created for non-persisted record: #{story.inspect}"
471
+ )
420
472
  end
421
473
  end
422
474
 
@@ -109,7 +109,7 @@ module Delayed
109
109
  Delayed::Worker.before_fork
110
110
  Daemons.run_proc(process_name, :dir => options[:pid_dir], :dir_mode => :normal, :monitor => @monitor, :ARGV => @args) do |*_args|
111
111
  $0 = File.join(options[:prefix], process_name) if @options[:prefix]
112
- run process_name
112
+ run process_name, options
113
113
  end
114
114
  end
115
115
 
@@ -3,7 +3,8 @@ require 'mail'
3
3
  module Delayed
4
4
  class PerformableMailer < PerformableMethod
5
5
  def perform
6
- object.send(method_name, *args).deliver
6
+ mailer = object.send(method_name, *args)
7
+ mailer.respond_to?(:deliver_now) ? mailer.deliver_now : mailer.deliver
7
8
  end
8
9
  end
9
10
 
@@ -10,7 +10,7 @@ module Delayed
10
10
  raise NoMethodError, "undefined method `#{method_name}' for #{object.inspect}" unless object.respond_to?(method_name, true)
11
11
 
12
12
  if object.respond_to?(:persisted?) && !object.persisted?
13
- raise ArgumentError, 'Jobs cannot be created for non-persisted records'
13
+ raise(ArgumentError, "job cannot be created for non-persisted record: #{object.inspect}")
14
14
  end
15
15
 
16
16
  self.object = object
@@ -19,7 +19,11 @@ module Delayed
19
19
  end
20
20
 
21
21
  def display_name
22
- "#{object.class}##{method_name}"
22
+ if object.is_a?(Class)
23
+ "#{object}.#{method_name}"
24
+ else
25
+ "#{object.class}##{method_name}"
26
+ end
23
27
  end
24
28
 
25
29
  def perform
@@ -1,22 +1,3 @@
1
- if defined?(ActiveRecord)
2
- ActiveRecord::Base.class_eval do
3
- # rubocop:disable BlockNesting
4
- if instance_methods.include?(:encode_with)
5
- def encode_with_override(coder)
6
- encode_with_without_override(coder)
7
- coder.tag = "!ruby/ActiveRecord:#{self.class.name}" if coder.respond_to?(:tag=)
8
- end
9
- alias_method :encode_with_without_override, :encode_with
10
- alias_method :encode_with, :encode_with_override
11
- else
12
- def encode_with(coder)
13
- coder['attributes'] = attributes
14
- coder.tag = "!ruby/ActiveRecord:#{self.class.name}" if coder.respond_to?(:tag=)
15
- end
16
- end
17
- end
18
- end
19
-
20
1
  module Delayed
21
2
  class PerformableMethod
22
3
  # serialize to YAML
@@ -31,16 +12,41 @@ module Delayed
31
12
  end
32
13
 
33
14
  module Psych
34
- module Visitors
35
- class ToRuby
36
- def visit_Psych_Nodes_Mapping_with_class(object) # rubocop:disable CyclomaticComplexity, MethodName
15
+ def self.load_dj(yaml)
16
+ result = parse(yaml)
17
+ result ? Delayed::PsychExt::ToRuby.create.accept(result) : result
18
+ end
19
+ end
20
+
21
+ module Delayed
22
+ module PsychExt
23
+ class ToRuby < Psych::Visitors::ToRuby
24
+ unless respond_to?(:create)
25
+ def self.create
26
+ new
27
+ end
28
+ end
29
+
30
+ def visit_Psych_Nodes_Mapping(object) # rubocop:disable CyclomaticComplexity, MethodName, PerceivedComplexity
37
31
  return revive(Psych.load_tags[object.tag], object) if Psych.load_tags[object.tag]
38
32
 
39
33
  case object.tag
34
+ when /^!ruby\/object/
35
+ result = super
36
+ if defined?(ActiveRecord::Base) && result.is_a?(ActiveRecord::Base)
37
+ begin
38
+ result.class.find(result[result.class.primary_key])
39
+ rescue ActiveRecord::RecordNotFound => error # rubocop:disable BlockNesting
40
+ raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass}, primary key: #{id} (#{error.message})"
41
+ end
42
+ else
43
+ result
44
+ end
40
45
  when /^!ruby\/ActiveRecord:(.+)$/
41
46
  klass = resolve_class(Regexp.last_match[1])
42
47
  payload = Hash[*object.children.map { |c| accept c }]
43
48
  id = payload['attributes'][klass.primary_key]
49
+ id = id.value if defined?(ActiveRecord::Attribute) && id.is_a?(ActiveRecord::Attribute)
44
50
  begin
45
51
  klass.unscoped.find(id)
46
52
  rescue ActiveRecord::RecordNotFound => error
@@ -66,17 +72,16 @@ module Psych
66
72
  raise Delayed::DeserializationError, "DataMapper::ObjectNotFoundError, class: #{klass} (#{error.message})"
67
73
  end
68
74
  else
69
- visit_Psych_Nodes_Mapping_without_class(object)
75
+ super
70
76
  end
71
77
  end
72
- alias_method_chain :visit_Psych_Nodes_Mapping, :class
73
78
 
74
- def resolve_class_with_constantize(klass_name)
79
+ def resolve_class(klass_name)
80
+ return nil if !klass_name || klass_name.empty?
75
81
  klass_name.constantize
76
82
  rescue
77
- resolve_class_without_constantize(klass_name)
83
+ super
78
84
  end
79
- alias_method_chain :resolve_class, :constantize
80
85
  end
81
86
  end
82
87
  end
@@ -7,6 +7,12 @@ module Delayed
7
7
  ActiveSupport.on_load(:action_mailer) do
8
8
  ActionMailer::Base.extend(Delayed::DelayMail)
9
9
  end
10
+
11
+ Delayed::Worker.logger = if defined?(Rails)
12
+ Rails.logger
13
+ elsif defined?(RAILS_DEFAULT_LOGGER)
14
+ RAILS_DEFAULT_LOGGER
15
+ end
10
16
  end
11
17
 
12
18
  rake_tasks do
@@ -38,7 +38,7 @@ Capistrano::Configuration.instance.load do
38
38
 
39
39
  desc 'Stop the delayed_job process'
40
40
  task :stop, :roles => lambda { roles } do
41
- run "cd #{current_path};#{rails_env} #{delayed_job_command} stop"
41
+ run "cd #{current_path};#{rails_env} #{delayed_job_command} stop #{args}"
42
42
  end
43
43
 
44
44
  desc 'Start the delayed_job process'
@@ -32,3 +32,11 @@ class Struct
32
32
  "Struct::#{ name }"
33
33
  end
34
34
  end
35
+
36
+ module YAML
37
+ def load_dj(yaml)
38
+ # See https://github.com/dtao/safe_yaml
39
+ # When the method is there, we need to load our YAML like this...
40
+ respond_to?(:unsafe_load) ? load(yaml, :safe => false) : load(yaml)
41
+ end
42
+ end
@@ -21,6 +21,9 @@ namespace :jobs do
21
21
  :queues => (ENV['QUEUES'] || ENV['QUEUE'] || '').split(','),
22
22
  :quiet => false
23
23
  }
24
+
25
+ @worker_options[:sleep_delay] = ENV['SLEEP_DELAY'].to_i if ENV['SLEEP_DELAY']
26
+ @worker_options[:read_ahead] = ENV['READ_AHEAD'].to_i if ENV['READ_AHEAD']
24
27
  end
25
28
 
26
29
  desc "Exit with error status if any jobs older than max_age seconds haven't been attempted yet."
@@ -19,7 +19,8 @@ module Delayed
19
19
 
20
20
  cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time,
21
21
  :default_priority, :sleep_delay, :logger, :delay_jobs, :queues,
22
- :read_ahead, :plugins, :destroy_failed_jobs, :exit_on_complete
22
+ :read_ahead, :plugins, :destroy_failed_jobs, :exit_on_complete,
23
+ :default_log_level
23
24
 
24
25
  # Named queue into which jobs are enqueued by default
25
26
  cattr_accessor :default_queue_name
@@ -30,13 +31,14 @@ module Delayed
30
31
  attr_accessor :name_prefix
31
32
 
32
33
  def self.reset
33
- self.sleep_delay = DEFAULT_SLEEP_DELAY
34
- self.max_attempts = DEFAULT_MAX_ATTEMPTS
35
- self.max_run_time = DEFAULT_MAX_RUN_TIME
36
- self.default_priority = DEFAULT_DEFAULT_PRIORITY
37
- self.delay_jobs = DEFAULT_DELAY_JOBS
38
- self.queues = DEFAULT_QUEUES
39
- self.read_ahead = DEFAULT_READ_AHEAD
34
+ self.default_log_level = DEFAULT_LOG_LEVEL
35
+ self.sleep_delay = DEFAULT_SLEEP_DELAY
36
+ self.max_attempts = DEFAULT_MAX_ATTEMPTS
37
+ self.max_run_time = DEFAULT_MAX_RUN_TIME
38
+ self.default_priority = DEFAULT_DEFAULT_PRIORITY
39
+ self.delay_jobs = DEFAULT_DELAY_JOBS
40
+ self.queues = DEFAULT_QUEUES
41
+ self.read_ahead = DEFAULT_READ_AHEAD
40
42
  end
41
43
 
42
44
  reset
@@ -57,12 +59,6 @@ module Delayed
57
59
  cattr_accessor :raise_signal_exceptions
58
60
  self.raise_signal_exceptions = false
59
61
 
60
- self.logger = if defined?(Rails)
61
- Rails.logger
62
- elsif defined?(RAILS_DEFAULT_LOGGER)
63
- RAILS_DEFAULT_LOGGER
64
- end
65
-
66
62
  def self.backend=(backend)
67
63
  if backend.is_a? Symbol
68
64
  require "delayed/serialization/#{backend}"
@@ -130,13 +126,13 @@ module Delayed
130
126
 
131
127
  def start # rubocop:disable CyclomaticComplexity, PerceivedComplexity
132
128
  trap('TERM') do
133
- say 'Exiting...'
129
+ Thread.new { say 'Exiting...' }
134
130
  stop
135
131
  raise SignalException, 'TERM' if self.class.raise_signal_exceptions
136
132
  end
137
133
 
138
134
  trap('INT') do
139
- say 'Exiting...'
135
+ Thread.new { say 'Exiting...' }
140
136
  stop
141
137
  raise SignalException, 'INT' if self.class.raise_signal_exceptions && self.class.raise_signal_exceptions != :term
142
138
  end
@@ -200,7 +196,7 @@ module Delayed
200
196
  def run(job)
201
197
  job_say job, 'RUNNING'
202
198
  runtime = Benchmark.realtime do
203
- Timeout.timeout(self.class.max_run_time.to_i, WorkerTimeout) { job.invoke_job }
199
+ Timeout.timeout(max_run_time(job).to_i, WorkerTimeout) { job.invoke_job }
204
200
  job.destroy
205
201
  end
206
202
  job_say job, format('COMPLETED after %.4f', runtime)
@@ -240,12 +236,12 @@ module Delayed
240
236
  end
241
237
  end
242
238
 
243
- def job_say(job, text, level = DEFAULT_LOG_LEVEL)
239
+ def job_say(job, text, level = default_log_level)
244
240
  text = "Job #{job.name} (id=#{job.id}) #{text}"
245
241
  say text, level
246
242
  end
247
243
 
248
- def say(text, level = DEFAULT_LOG_LEVEL)
244
+ def say(text, level = default_log_level)
249
245
  text = "[Worker(#{name})] #{text}"
250
246
  puts text unless @quiet
251
247
  return unless logger
@@ -260,6 +256,10 @@ module Delayed
260
256
  job.max_attempts || self.class.max_attempts
261
257
  end
262
258
 
259
+ def max_run_time(job)
260
+ job.max_run_time || self.class.max_run_time
261
+ end
262
+
263
263
  protected
264
264
 
265
265
  def handle_failed_job(job, error)
@@ -1,4 +1,4 @@
1
- require 'rails/generators'
1
+ require 'rails/generators/base'
2
2
  require 'delayed/compatibility'
3
3
 
4
4
  class DelayedJobGenerator < Rails::Generators::Base
@@ -20,9 +20,20 @@ require 'active_record'
20
20
  require 'delayed_job'
21
21
  require 'delayed/backend/shared_spec'
22
22
 
23
- Delayed::Worker.logger = Logger.new('/tmp/dj.log')
23
+ if ENV['DEBUG_LOGS']
24
+ Delayed::Worker.logger = Logger.new(STDOUT)
25
+ else
26
+ require 'tempfile'
27
+
28
+ tf = Tempfile.new('dj.log')
29
+ Delayed::Worker.logger = Logger.new(tf.path)
30
+ tf.unlink
31
+ end
24
32
  ENV['RAILS_ENV'] = 'test'
25
33
 
34
+ # Trigger AR to initialize
35
+ ActiveRecord::Base # rubocop:disable Void
36
+
26
37
  module Rails
27
38
  def self.root
28
39
  '.'
@@ -37,6 +37,16 @@ describe Delayed::PerformableMethod do
37
37
  expect { Delayed::PerformableMethod.new(clazz.new, :private_method, []) }.not_to raise_error
38
38
  end
39
39
 
40
+ describe 'display_name' do
41
+ it 'returns class_name#method_name for instance methods' do
42
+ expect(Delayed::PerformableMethod.new('foo', :count, ['o']).display_name).to eq('String#count')
43
+ end
44
+
45
+ it 'returns class_name.method_name for class methods' do
46
+ expect(Delayed::PerformableMethod.new(Class, :inspect, []).display_name).to eq('Class.inspect')
47
+ end
48
+ end
49
+
40
50
  describe 'hooks' do
41
51
  %w[before after success].each do |hook|
42
52
  it "delegates #{hook} hook to object" do
@@ -12,6 +12,12 @@ class SimpleJob
12
12
  end
13
13
  end
14
14
 
15
+ class NamedQueueJob < SimpleJob
16
+ def queue_name
17
+ 'job_tracking'
18
+ end
19
+ end
20
+
15
21
  class ErrorJob
16
22
  cattr_accessor :runs
17
23
  @runs = 0
@@ -29,7 +29,15 @@ describe Delayed::Worker do
29
29
 
30
30
  it 'logs with job name and id' do
31
31
  expect(@worker).to receive(:say).
32
- with('Job ExampleJob (id=123) message', Delayed::Worker::DEFAULT_LOG_LEVEL)
32
+ with('Job ExampleJob (id=123) message', Delayed::Worker.default_log_level)
33
+ @worker.job_say(@job, 'message')
34
+ end
35
+
36
+ it 'has a configurable default log level' do
37
+ Delayed::Worker.default_log_level = 'error'
38
+
39
+ expect(@worker).to receive(:say).
40
+ with('Job ExampleJob (id=123) message', 'error')
33
41
  @worker.job_say(@job, 'message')
34
42
  end
35
43
  end
@@ -140,10 +148,10 @@ describe Delayed::Worker do
140
148
  it_behaves_like 'a worker which logs on the correct severity', severity
141
149
  end
142
150
 
143
- it "logs a message on the default log's level" do
151
+ it 'logs a message on the default log\'s level' do
144
152
  expect(@worker.logger).to receive(:send).
145
153
  with('info', "#{@expected_time}: #{@worker_name} #{@text}")
146
- @worker.say(@text, Delayed::Worker::DEFAULT_LOG_LEVEL)
154
+ @worker.say(@text, Delayed::Worker.default_log_level)
147
155
  end
148
156
  end
149
157
  end
@@ -4,32 +4,45 @@ describe 'YAML' do
4
4
  it 'autoloads classes' do
5
5
  expect do
6
6
  yaml = "--- !ruby/class Autoloaded::Clazz\n"
7
- expect(YAML.load(yaml)).to eq(Autoloaded::Clazz)
7
+ expect(load_with_delayed_visitor(yaml)).to eq(Autoloaded::Clazz)
8
8
  end.not_to raise_error
9
9
  end
10
10
 
11
11
  it 'autoloads the class of a struct' do
12
12
  expect do
13
13
  yaml = "--- !ruby/class Autoloaded::Struct\n"
14
- expect(YAML.load(yaml)).to eq(Autoloaded::Struct)
14
+ expect(load_with_delayed_visitor(yaml)).to eq(Autoloaded::Struct)
15
15
  end.not_to raise_error
16
16
  end
17
17
 
18
18
  it 'autoloads the class for the instance of a struct' do
19
19
  expect do
20
20
  yaml = '--- !ruby/struct:Autoloaded::InstanceStruct {}'
21
- expect(YAML.load(yaml).class).to eq(Autoloaded::InstanceStruct)
21
+ expect(load_with_delayed_visitor(yaml).class).to eq(Autoloaded::InstanceStruct)
22
+ end.not_to raise_error
23
+ end
24
+
25
+ it 'autoloads the class of an anonymous struct' do
26
+ expect do
27
+ yaml = "--- !ruby/struct\nn: 1\n"
28
+ object = YAML.load(yaml)
29
+ expect(object).to be_kind_of(Struct)
30
+ expect(object.n).to eq(1)
22
31
  end.not_to raise_error
23
32
  end
24
33
 
25
34
  it 'autoloads the class for the instance' do
26
35
  expect do
27
36
  yaml = "--- !ruby/object:Autoloaded::InstanceClazz {}\n"
28
- expect(YAML.load(yaml).class).to eq(Autoloaded::InstanceClazz)
37
+ expect(load_with_delayed_visitor(yaml).class).to eq(Autoloaded::InstanceClazz)
29
38
  end.not_to raise_error
30
39
  end
31
40
 
32
41
  it 'does not throw an uninitialized constant Syck::Syck when using YAML.load with poorly formed yaml' do
33
42
  expect { YAML.load(YAML.dump('foo: *bar')) }.not_to raise_error
34
43
  end
44
+
45
+ def load_with_delayed_visitor(yaml)
46
+ YAML.load_dj(yaml)
47
+ end
35
48
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delayed_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.3
4
+ version: 4.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Keepers
@@ -15,7 +15,7 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2014-09-04 00:00:00.000000000 Z
18
+ date: 2014-09-24 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activesupport