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.
@@ -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