delayed_job 4.0.6 → 4.1.8

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.
@@ -3,20 +3,24 @@ require 'delayed/compatibility'
3
3
  require 'delayed/exceptions'
4
4
  require 'delayed/message_sending'
5
5
  require 'delayed/performable_method'
6
-
7
- if defined?(ActionMailer)
8
- require 'action_mailer/version'
9
- require 'delayed/performable_mailer'
10
- end
11
-
12
6
  require 'delayed/yaml_ext'
13
7
  require 'delayed/lifecycle'
14
8
  require 'delayed/plugin'
15
9
  require 'delayed/plugins/clear_locks'
16
10
  require 'delayed/backend/base'
11
+ require 'delayed/backend/job_preparer'
17
12
  require 'delayed/worker'
18
13
  require 'delayed/deserialization_error'
19
14
  require 'delayed/railtie' if defined?(Rails::Railtie)
20
15
 
16
+ ActiveSupport.on_load(:action_mailer) do
17
+ require 'delayed/performable_mailer'
18
+ ActionMailer::Base.extend(Delayed::DelayMail)
19
+ end
20
+
21
+ module Delayed
22
+ autoload :PerformableMailer, 'delayed/performable_mailer'
23
+ end
24
+
21
25
  Object.send(:include, Delayed::MessageSending)
22
- Module.send(:include, Delayed::MessageSending::ClassMethods)
26
+ Module.send(:include, Delayed::MessageSendingClassMethods)
@@ -6,6 +6,6 @@ class DelayedJobGenerator < Rails::Generators::Base
6
6
 
7
7
  def create_executable_file
8
8
  template 'script', "#{Delayed::Compatibility.executable_prefix}/delayed_job"
9
- chmod "#{Delayed::Compatibility.executable_prefix}/delayed_job", 0755
9
+ chmod "#{Delayed::Compatibility.executable_prefix}/delayed_job", 0o755
10
10
  end
11
11
  end
@@ -1,7 +1,6 @@
1
1
  # Make sure this file does not get required manually
2
2
  module Autoloaded
3
3
  class Clazz
4
- def perform
5
- end
4
+ def perform; end
6
5
  end
7
6
  end
@@ -1,6 +1,5 @@
1
1
  module Autoloaded
2
2
  class InstanceClazz
3
- def perform
4
- end
3
+ def perform; end
5
4
  end
6
5
  end
@@ -1,6 +1,6 @@
1
1
  module Autoloaded
2
- class InstanceStruct < ::Struct.new(nil)
3
- def perform
4
- end
2
+ InstanceStruct = ::Struct.new(nil)
3
+ class InstanceStruct
4
+ def perform; end
5
5
  end
6
6
  end
@@ -1,7 +1,7 @@
1
1
  # Make sure this file does not get required manually
2
2
  module Autoloaded
3
- class Struct < ::Struct.new(nil)
4
- def perform
5
- end
3
+ Struct = ::Struct.new(nil)
4
+ class Struct
5
+ def perform; end
6
6
  end
7
7
  end
@@ -0,0 +1,2 @@
1
+ # Fake "daemons" file on the spec load path to allow spec/delayed/command_spec.rb
2
+ # to test the Delayed::Command class without actually adding daemons as a dependency.
@@ -87,11 +87,6 @@ module Delayed
87
87
  Time.current
88
88
  end
89
89
 
90
- def update_attributes(attrs = {})
91
- attrs.each { |k, v| send(:"#{k}=", v) }
92
- save
93
- end
94
-
95
90
  def destroy
96
91
  self.class.all.delete(self)
97
92
  end
@@ -2,6 +2,129 @@ require 'helper'
2
2
  require 'delayed/command'
3
3
 
4
4
  describe Delayed::Command do
5
+ let(:options) { [] }
6
+ let(:logger) { double('Logger') }
7
+
8
+ subject { Delayed::Command.new options }
9
+
10
+ before do
11
+ allow(Delayed::Worker).to receive(:after_fork)
12
+ allow(Dir).to receive(:chdir)
13
+ allow(Logger).to receive(:new).and_return(logger)
14
+ allow_any_instance_of(Delayed::Worker).to receive(:start)
15
+ allow(Delayed::Worker).to receive(:logger=)
16
+ allow(Delayed::Worker).to receive(:logger).and_return(nil, logger)
17
+ end
18
+
19
+ shared_examples_for 'uses --log-dir option' do
20
+ context 'when --log-dir is specified' do
21
+ let(:options) { ['--log-dir=/custom/log/dir'] }
22
+
23
+ it 'creates the delayed_job.log in the specified directory' do
24
+ expect(Logger).to receive(:new).with('/custom/log/dir/delayed_job.log')
25
+ subject.run
26
+ end
27
+ end
28
+ end
29
+
30
+ describe 'run' do
31
+ it 'sets the Delayed::Worker logger' do
32
+ expect(Delayed::Worker).to receive(:logger=).with(logger)
33
+ subject.run
34
+ end
35
+
36
+ context 'when Rails root is defined' do
37
+ let(:rails_root) { Pathname.new '/rails/root' }
38
+ let(:rails) { double('Rails', :root => rails_root) }
39
+
40
+ before do
41
+ stub_const('Rails', rails)
42
+ end
43
+
44
+ it 'runs the Delayed::Worker process in Rails.root' do
45
+ expect(Dir).to receive(:chdir).with(rails_root)
46
+ subject.run
47
+ end
48
+
49
+ context 'when --log-dir is not specified' do
50
+ it 'creates the delayed_job.log in Rails.root/log' do
51
+ expect(Logger).to receive(:new).with('/rails/root/log/delayed_job.log')
52
+ subject.run
53
+ end
54
+ end
55
+
56
+ include_examples 'uses --log-dir option'
57
+ end
58
+
59
+ context 'when Rails root is not defined' do
60
+ let(:rails_without_root) { double('Rails') }
61
+
62
+ before do
63
+ stub_const('Rails', rails_without_root)
64
+ end
65
+
66
+ it 'runs the Delayed::Worker process in $PWD' do
67
+ expect(Dir).to receive(:chdir).with(Delayed::Command::DIR_PWD)
68
+ subject.run
69
+ end
70
+
71
+ context 'when --log-dir is not specified' do
72
+ it 'creates the delayed_job.log in $PWD/log' do
73
+ expect(Logger).to receive(:new).with("#{Delayed::Command::DIR_PWD}/log/delayed_job.log")
74
+ subject.run
75
+ end
76
+ end
77
+
78
+ include_examples 'uses --log-dir option'
79
+ end
80
+
81
+ context 'when an error is raised' do
82
+ let(:test_error) { Class.new(StandardError) }
83
+
84
+ before do
85
+ allow(Delayed::Worker).to receive(:new).and_raise(test_error.new('An error'))
86
+ allow(subject).to receive(:exit_with_error_status)
87
+ allow(STDERR).to receive(:puts)
88
+ end
89
+
90
+ it 'prints the error message to STDERR' do
91
+ expect(STDERR).to receive(:puts).with('An error')
92
+ subject.run
93
+ end
94
+
95
+ it 'exits with an error status' do
96
+ expect(subject).to receive(:exit_with_error_status)
97
+ subject.run
98
+ end
99
+
100
+ context 'when Rails logger is not defined' do
101
+ let(:rails) { double('Rails') }
102
+
103
+ before do
104
+ stub_const('Rails', rails)
105
+ end
106
+
107
+ it 'does not attempt to use the Rails logger' do
108
+ subject.run
109
+ end
110
+ end
111
+
112
+ context 'when Rails logger is defined' do
113
+ let(:rails_logger) { double('Rails logger') }
114
+ let(:rails) { double('Rails', :logger => rails_logger) }
115
+
116
+ before do
117
+ stub_const('Rails', rails)
118
+ end
119
+
120
+ it 'logs the error to the Rails logger' do
121
+ expect(rails_logger).to receive(:fatal).with(test_error)
122
+ subject.run
123
+ end
124
+ end
125
+ end
126
+ end
127
+
5
128
  describe 'parsing --pool argument' do
6
129
  it 'should parse --pool correctly' do
7
130
  command = Delayed::Command.new(['--pool=*:1', '--pool=test_queue:4', '--pool=mailers,misc:2'])
@@ -36,17 +159,16 @@ describe Delayed::Command do
36
159
  describe 'running worker pools defined by multiple --pool arguments' do
37
160
  it 'should run the correct worker processes' do
38
161
  command = Delayed::Command.new(['--pool=*:1', '--pool=test_queue:4', '--pool=mailers,misc:2'])
39
-
40
- expect(Dir).to receive(:mkdir).with('./tmp/pids').once
162
+ expect(FileUtils).to receive(:mkdir_p).with('./tmp/pids').once
41
163
 
42
164
  [
43
- ['delayed_job.0', {:quiet => true, :pid_dir => './tmp/pids', :queues => []}],
44
- ['delayed_job.1', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
45
- ['delayed_job.2', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
46
- ['delayed_job.3', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
47
- ['delayed_job.4', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
48
- ['delayed_job.5', {:quiet => true, :pid_dir => './tmp/pids', :queues => %w[mailers misc]}],
49
- ['delayed_job.6', {:quiet => true, :pid_dir => './tmp/pids', :queues => %w[mailers misc]}]
165
+ ['delayed_job.0', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => []}],
166
+ ['delayed_job.1', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => ['test_queue']}],
167
+ ['delayed_job.2', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => ['test_queue']}],
168
+ ['delayed_job.3', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => ['test_queue']}],
169
+ ['delayed_job.4', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => ['test_queue']}],
170
+ ['delayed_job.5', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => %w[mailers misc]}],
171
+ ['delayed_job.6', {:quiet => true, :pid_dir => './tmp/pids', :log_dir => './log', :queues => %w[mailers misc]}]
50
172
  ].each do |args|
51
173
  expect(command).to receive(:run_process).with(*args).once
52
174
  end
@@ -14,7 +14,6 @@ require 'logger'
14
14
  require 'rspec'
15
15
 
16
16
  require 'action_mailer'
17
- require 'active_support/dependencies'
18
17
  require 'active_record'
19
18
 
20
19
  require 'delayed_job'
@@ -45,9 +44,6 @@ Delayed::Worker.backend = :test
45
44
  # Add this directory so the ActiveSupport autoloading works
46
45
  ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
47
46
 
48
- # Add this to simulate Railtie initializer being executed
49
- ActionMailer::Base.extend(Delayed::DelayMail)
50
-
51
47
  # Used to test interactions between DJ and an ORM
52
48
  ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
53
49
  ActiveRecord::Base.logger = Delayed::Worker.logger
@@ -1,6 +1,11 @@
1
1
  require 'helper'
2
2
 
3
3
  describe Delayed::MessageSending do
4
+ it 'does not include ClassMethods along with MessageSending' do
5
+ expect { ClassMethods }.to raise_error(NameError)
6
+ expect(defined?(String::ClassMethods)).to eq(nil)
7
+ end
8
+
4
9
  describe 'handle_asynchronously' do
5
10
  class Story
6
11
  def tell!(_arg); end
@@ -19,7 +24,7 @@ describe Delayed::MessageSending do
19
24
  expect(job.payload_object.class).to eq(Delayed::PerformableMethod)
20
25
  expect(job.payload_object.method_name).to eq(:tell_without_delay!)
21
26
  expect(job.payload_object.args).to eq([1])
22
- end.to change { Delayed::Job.count }
27
+ end.to(change { Delayed::Job.count })
23
28
  end
24
29
 
25
30
  describe 'with options' do
@@ -42,8 +47,7 @@ describe Delayed::MessageSending do
42
47
  describe 'using a proc with parameters' do
43
48
  class Yarn
44
49
  attr_accessor :importance
45
- def spin
46
- end
50
+ def spin; end
47
51
  handle_asynchronously :spin, :priority => proc { |y| y.importance }
48
52
  end
49
53
 
@@ -62,6 +66,7 @@ describe Delayed::MessageSending do
62
66
  class FairyTail
63
67
  attr_accessor :happy_ending
64
68
  def self.princesses; end
69
+
65
70
  def tell
66
71
  @happy_ending = true
67
72
  end
@@ -106,7 +111,7 @@ describe Delayed::MessageSending do
106
111
  expect do
107
112
  fairy_tail.delay.tell
108
113
  end.to change(fairy_tail, :happy_ending).from(nil).to(true)
109
- end.not_to change { Delayed::Job.count }
114
+ end.not_to(change { Delayed::Job.count })
110
115
  end
111
116
 
112
117
  it 'does delay the job when delay_jobs is true' do
@@ -118,5 +123,25 @@ describe Delayed::MessageSending do
118
123
  end.not_to change(fairy_tail, :happy_ending)
119
124
  end.to change { Delayed::Job.count }.by(1)
120
125
  end
126
+
127
+ it 'does delay when delay_jobs is a proc returning true' do
128
+ Delayed::Worker.delay_jobs = ->(_job) { true }
129
+ fairy_tail = FairyTail.new
130
+ expect do
131
+ expect do
132
+ fairy_tail.delay.tell
133
+ end.not_to change(fairy_tail, :happy_ending)
134
+ end.to change { Delayed::Job.count }.by(1)
135
+ end
136
+
137
+ it 'does not delay the job when delay_jobs is a proc returning false' do
138
+ Delayed::Worker.delay_jobs = ->(_job) { false }
139
+ fairy_tail = FairyTail.new
140
+ expect do
141
+ expect do
142
+ fairy_tail.delay.tell
143
+ end.to change(fairy_tail, :happy_ending).from(nil).to(true)
144
+ end.not_to(change { Delayed::Job.count })
145
+ end
121
146
  end
122
147
  end
@@ -1,6 +1,5 @@
1
1
  require 'helper'
2
2
 
3
- require 'action_mailer'
4
3
  class MyMailer < ActionMailer::Base
5
4
  def signup(email)
6
5
  mail :to => email, :subject => 'Delaying Emails', :from => 'delayedjob@example.com', :body => 'Delaying Emails Body'
@@ -30,8 +30,7 @@ describe Delayed::PerformableMethod do
30
30
 
31
31
  it 'does not raise NoMethodError if target method is private' do
32
32
  clazz = Class.new do
33
- def private_method
34
- end
33
+ def private_method; end
35
34
  private :private_method
36
35
  end
37
36
  expect { Delayed::PerformableMethod.new(clazz.new, :private_method, []) }.not_to raise_error
@@ -68,7 +67,7 @@ describe Delayed::PerformableMethod do
68
67
  story = Story.create
69
68
  expect(story).to receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
70
69
  expect(story).to receive(:tell).and_raise(RuntimeError)
71
- expect { story.delay.tell.invoke_job }.to raise_error
70
+ expect { story.delay.tell.invoke_job }.to raise_error(RuntimeError)
72
71
  end
73
72
 
74
73
  it 'delegates failure hook to object' do
@@ -98,7 +97,7 @@ describe Delayed::PerformableMethod do
98
97
  story = Story.create
99
98
  expect(story).to receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
100
99
  expect(story).to receive(:tell).and_raise(RuntimeError)
101
- expect { story.delay.tell }.to raise_error
100
+ expect { story.delay.tell }.to raise_error(RuntimeError)
102
101
  end
103
102
 
104
103
  it 'delegates failure hook to object' do
@@ -3,10 +3,32 @@ require 'helper'
3
3
  describe 'Psych::Visitors::ToRuby', :if => defined?(Psych::Visitors::ToRuby) do
4
4
  context BigDecimal do
5
5
  it 'deserializes correctly' do
6
- deserialized = YAML.load("--- !ruby/object:BigDecimal 18:0.1337E2\n...\n")
6
+ deserialized = YAML.load_dj("--- !ruby/object:BigDecimal 18:0.1337E2\n...\n")
7
7
 
8
8
  expect(deserialized).to be_an_instance_of(BigDecimal)
9
9
  expect(deserialized).to eq(BigDecimal('13.37'))
10
10
  end
11
11
  end
12
+
13
+ context 'load_tag handling' do
14
+ # This only broadly works in ruby 2.0 but will cleanly work through load_dj
15
+ # here because this class is so simple it only touches our extention
16
+ YAML.load_tags['!ruby/object:RenamedClass'] = SimpleJob
17
+ # This is how ruby 2.1 and newer works throughout the yaml handling
18
+ YAML.load_tags['!ruby/object:RenamedString'] = 'SimpleJob'
19
+
20
+ it 'deserializes class tag' do
21
+ deserialized = YAML.load_dj("--- !ruby/object:RenamedClass\ncheck: 12\n")
22
+
23
+ expect(deserialized).to be_an_instance_of(SimpleJob)
24
+ expect(deserialized.instance_variable_get(:@check)).to eq(12)
25
+ end
26
+
27
+ it 'deserializes string tag' do
28
+ deserialized = YAML.load_dj("--- !ruby/object:RenamedString\ncheck: 12\n")
29
+
30
+ expect(deserialized).to be_an_instance_of(SimpleJob)
31
+ expect(deserialized.instance_variable_get(:@check)).to eq(12)
32
+ end
33
+ end
12
34
  end
@@ -1,4 +1,5 @@
1
- class NamedJob < Struct.new(:perform)
1
+ NamedJob = Struct.new(:perform)
2
+ class NamedJob
2
3
  def display_name
3
4
  'named_job'
4
5
  end
@@ -22,11 +23,12 @@ class ErrorJob
22
23
  cattr_accessor :runs
23
24
  @runs = 0
24
25
  def perform
25
- raise 'did not work'
26
+ raise Exception, 'did not work'
26
27
  end
27
28
  end
28
29
 
29
- class CustomRescheduleJob < Struct.new(:offset)
30
+ CustomRescheduleJob = Struct.new(:offset)
31
+ class CustomRescheduleJob
30
32
  cattr_accessor :runs
31
33
  @runs = 0
32
34
  def perform
@@ -24,15 +24,23 @@ describe Delayed::Worker do
24
24
  describe 'job_say' do
25
25
  before do
26
26
  @worker = Delayed::Worker.new
27
- @job = double('job', :id => 123, :name => 'ExampleJob')
27
+ @job = double('job', :id => 123, :name => 'ExampleJob', :queue => nil)
28
28
  end
29
29
 
30
30
  it 'logs with job name and id' do
31
+ expect(@job).to receive(:queue)
31
32
  expect(@worker).to receive(:say).
32
33
  with('Job ExampleJob (id=123) message', Delayed::Worker.default_log_level)
33
34
  @worker.job_say(@job, 'message')
34
35
  end
35
36
 
37
+ it 'logs with job name, queue and id' do
38
+ expect(@job).to receive(:queue).and_return('test')
39
+ expect(@worker).to receive(:say).
40
+ with('Job ExampleJob (id=123) (queue=test) message', Delayed::Worker.default_log_level)
41
+ @worker.job_say(@job, 'message')
42
+ end
43
+
36
44
  it 'has a configurable default log level' do
37
45
  Delayed::Worker.default_log_level = 'error'
38
46
 
@@ -154,4 +162,22 @@ describe Delayed::Worker do
154
162
  @worker.say(@text, Delayed::Worker.default_log_level)
155
163
  end
156
164
  end
165
+
166
+ describe 'plugin registration' do
167
+ it 'does not double-register plugins on worker instantiation' do
168
+ performances = 0
169
+ plugin = Class.new(Delayed::Plugin) do
170
+ callbacks do |lifecycle|
171
+ lifecycle.before(:enqueue) { performances += 1 }
172
+ end
173
+ end
174
+ Delayed::Worker.plugins << plugin
175
+
176
+ Delayed::Worker.new
177
+ Delayed::Worker.new
178
+ Delayed::Worker.lifecycle.run_callbacks(:enqueue, nil) {}
179
+
180
+ expect(performances).to eq(1)
181
+ end
182
+ end
157
183
  end