delayed_job 4.0.3 → 4.0.4

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