delayed_job 3.0.3 → 3.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.
- data/README.textile +1 -16
- data/contrib/delayed_job_multiple.monitrc +11 -0
- data/lib/delayed/backend/base.rb +2 -1
- data/lib/delayed/backend/shared_spec.rb +149 -169
- data/lib/delayed/performable_method.rb +2 -2
- data/lib/delayed/psych_ext.rb +1 -5
- data/lib/delayed/recipes.rb +7 -3
- data/lib/delayed/serialization/active_record.rb +1 -5
- data/lib/delayed/worker.rb +5 -2
- data/spec/lifecycle_spec.rb +5 -5
- data/spec/message_sending_spec.rb +45 -38
- data/spec/performable_mailer_spec.rb +10 -10
- data/spec/performable_method_spec.rb +17 -17
- data/spec/spec_helper.rb +4 -0
- data/spec/test_backend_spec.rb +2 -2
- data/spec/worker_spec.rb +25 -4
- data/spec/yaml_ext_spec.rb +18 -24
- metadata +6 -7
- data/spec/fixtures/bad_alias.yml +0 -1
@@ -9,8 +9,8 @@ module Delayed
|
|
9
9
|
def initialize(object, method_name, args)
|
10
10
|
raise NoMethodError, "undefined method `#{method_name}' for #{object.inspect}" unless object.respond_to?(method_name, true)
|
11
11
|
|
12
|
-
if
|
13
|
-
raise(ArgumentError, 'Jobs cannot be created for records before they\'ve been persisted')
|
12
|
+
if object.respond_to?(:new_record?) && object.new_record?
|
13
|
+
raise(ArgumentError, 'Jobs cannot be created for records before they\'ve been persisted')
|
14
14
|
end
|
15
15
|
|
16
16
|
self.object = object
|
data/lib/delayed/psych_ext.rb
CHANGED
@@ -99,11 +99,7 @@ module Psych
|
|
99
99
|
payload = Hash[*object.children.map { |c| accept c }]
|
100
100
|
id = payload["attributes"][klass.primary_key]
|
101
101
|
begin
|
102
|
-
|
103
|
-
klass.unscoped.find(id)
|
104
|
-
else # Rails 2
|
105
|
-
klass.with_exclusive_scope { klass.find(id) }
|
106
|
-
end
|
102
|
+
klass.unscoped.find(id)
|
107
103
|
rescue ActiveRecord::RecordNotFound
|
108
104
|
raise Delayed::DeserializationError
|
109
105
|
end
|
data/lib/delayed/recipes.rb
CHANGED
@@ -31,20 +31,24 @@ Capistrano::Configuration.instance.load do
|
|
31
31
|
def roles
|
32
32
|
fetch(:delayed_job_server_role, :app)
|
33
33
|
end
|
34
|
+
|
35
|
+
def delayed_job_command
|
36
|
+
fetch(:delayed_job_command, "script/delayed_job")
|
37
|
+
end
|
34
38
|
|
35
39
|
desc "Stop the delayed_job process"
|
36
40
|
task :stop, :roles => lambda { roles } do
|
37
|
-
run "cd #{current_path};#{rails_env}
|
41
|
+
run "cd #{current_path};#{rails_env} #{delayed_job_command} stop"
|
38
42
|
end
|
39
43
|
|
40
44
|
desc "Start the delayed_job process"
|
41
45
|
task :start, :roles => lambda { roles } do
|
42
|
-
run "cd #{current_path};#{rails_env}
|
46
|
+
run "cd #{current_path};#{rails_env} #{delayed_job_command} start #{args}"
|
43
47
|
end
|
44
48
|
|
45
49
|
desc "Restart the delayed_job process"
|
46
50
|
task :restart, :roles => lambda { roles } do
|
47
|
-
run "cd #{current_path};#{rails_env}
|
51
|
+
run "cd #{current_path};#{rails_env} #{delayed_job_command} restart #{args}"
|
48
52
|
end
|
49
53
|
end
|
50
54
|
end
|
@@ -3,11 +3,7 @@ if defined?(ActiveRecord)
|
|
3
3
|
yaml_as "tag:ruby.yaml.org,2002:ActiveRecord"
|
4
4
|
|
5
5
|
def self.yaml_new(klass, tag, val)
|
6
|
-
|
7
|
-
klass.unscoped.find(val['attributes'][klass.primary_key])
|
8
|
-
else # Rails 2
|
9
|
-
klass.with_exclusive_scope { klass.find(val['attributes'][klass.primary_key]) }
|
10
|
-
end
|
6
|
+
klass.unscoped.find(val['attributes'][klass.primary_key])
|
11
7
|
rescue ActiveRecord::RecordNotFound
|
12
8
|
raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass} , primary key: #{val['attributes'][klass.primary_key]} "
|
13
9
|
end
|
data/lib/delayed/worker.rb
CHANGED
@@ -20,6 +20,9 @@ module Delayed
|
|
20
20
|
:default_priority, :sleep_delay, :logger, :delay_jobs, :queues,
|
21
21
|
:read_ahead, :plugins, :destroy_failed_jobs
|
22
22
|
|
23
|
+
# Named queue into which jobs are enqueued by default
|
24
|
+
cattr_accessor :default_queue_name
|
25
|
+
|
23
26
|
cattr_reader :backend
|
24
27
|
|
25
28
|
# name_prefix is ignored if name is set directly
|
@@ -185,7 +188,7 @@ module Delayed
|
|
185
188
|
say "#{job.name} completed after %.4f" % runtime
|
186
189
|
return true # did work
|
187
190
|
rescue DeserializationError => error
|
188
|
-
job.last_error = "
|
191
|
+
job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
|
189
192
|
failed(job)
|
190
193
|
rescue Exception => error
|
191
194
|
self.class.lifecycle.run_callbacks(:error, self, job){ handle_failed_job(job, error) }
|
@@ -226,7 +229,7 @@ module Delayed
|
|
226
229
|
protected
|
227
230
|
|
228
231
|
def handle_failed_job(job, error)
|
229
|
-
job.last_error = "
|
232
|
+
job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
|
230
233
|
say "#{job.name} failed with #{error.class.name}: #{error.message} - #{job.attempts} failed attempts", Logger::ERROR
|
231
234
|
reschedule(job)
|
232
235
|
end
|
data/spec/lifecycle_spec.rb
CHANGED
@@ -12,7 +12,7 @@ describe Delayed::Lifecycle do
|
|
12
12
|
lifecycle.before(:execute, &callback)
|
13
13
|
end
|
14
14
|
|
15
|
-
it
|
15
|
+
it "executes before wrapped block" do
|
16
16
|
callback.should_receive(:call).with(*arguments).ordered
|
17
17
|
behavior.should_receive(:inside!).ordered
|
18
18
|
lifecycle.run_callbacks :execute, *arguments, &wrapped_block
|
@@ -24,7 +24,7 @@ describe Delayed::Lifecycle do
|
|
24
24
|
lifecycle.after(:execute, &callback)
|
25
25
|
end
|
26
26
|
|
27
|
-
it
|
27
|
+
it "executes after wrapped block" do
|
28
28
|
behavior.should_receive(:inside!).ordered
|
29
29
|
callback.should_receive(:call).with(*arguments).ordered
|
30
30
|
lifecycle.run_callbacks :execute, *arguments, &wrapped_block
|
@@ -40,14 +40,14 @@ describe Delayed::Lifecycle do
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
it
|
43
|
+
it "wraps a block" do
|
44
44
|
behavior.should_receive(:before!).ordered
|
45
45
|
behavior.should_receive(:inside!).ordered
|
46
46
|
behavior.should_receive(:after!).ordered
|
47
47
|
lifecycle.run_callbacks :execute, *arguments, &wrapped_block
|
48
48
|
end
|
49
49
|
|
50
|
-
it "
|
50
|
+
it "executes multiple callbacks in order" do
|
51
51
|
behavior.should_receive(:one).ordered
|
52
52
|
behavior.should_receive(:two).ordered
|
53
53
|
behavior.should_receive(:three).ordered
|
@@ -60,7 +60,7 @@ describe Delayed::Lifecycle do
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
-
it "
|
63
|
+
it "raises if callback is executed with wrong number of parameters" do
|
64
64
|
lifecycle.before(:execute, &callback)
|
65
65
|
expect { lifecycle.run_callbacks(:execute, 1,2,3) {} }.to raise_error(ArgumentError, /1 parameter/)
|
66
66
|
end
|
@@ -7,39 +7,39 @@ describe Delayed::MessageSending do
|
|
7
7
|
handle_asynchronously :tell!
|
8
8
|
end
|
9
9
|
|
10
|
-
it "
|
11
|
-
Story.new.
|
12
|
-
Story.new.
|
10
|
+
it "aliases original method" do
|
11
|
+
expect(Story.new).to respond_to(:tell_without_delay!)
|
12
|
+
expect(Story.new).to respond_to(:tell_with_delay!)
|
13
13
|
end
|
14
14
|
|
15
|
-
it "
|
15
|
+
it "creates a PerformableMethod" do
|
16
16
|
story = Story.create
|
17
|
-
|
17
|
+
expect {
|
18
18
|
job = story.tell!(1)
|
19
|
-
job.payload_object.class.
|
20
|
-
job.payload_object.method_name.
|
21
|
-
job.payload_object.args.
|
22
|
-
}.
|
19
|
+
expect(job.payload_object.class).to eq(Delayed::PerformableMethod)
|
20
|
+
expect(job.payload_object.method_name).to eq(:tell_without_delay!)
|
21
|
+
expect(job.payload_object.args).to eq([1])
|
22
|
+
}.to change { Delayed::Job.count }
|
23
23
|
end
|
24
24
|
|
25
|
-
describe
|
25
|
+
describe "with options" do
|
26
26
|
class Fable
|
27
27
|
cattr_accessor :importance
|
28
28
|
def tell;end
|
29
29
|
handle_asynchronously :tell, :priority => Proc.new { self.importance }
|
30
30
|
end
|
31
31
|
|
32
|
-
it
|
32
|
+
it "sets the priority based on the Fable importance" do
|
33
33
|
Fable.importance = 10
|
34
34
|
job = Fable.new.tell
|
35
|
-
job.priority.
|
35
|
+
expect(job.priority).to eq(10)
|
36
36
|
|
37
37
|
Fable.importance = 20
|
38
38
|
job = Fable.new.tell
|
39
|
-
job.priority.
|
39
|
+
expect(job.priority).to eq(20)
|
40
40
|
end
|
41
41
|
|
42
|
-
describe
|
42
|
+
describe "using a proc with parameters" do
|
43
43
|
class Yarn
|
44
44
|
attr_accessor :importance
|
45
45
|
def spin
|
@@ -47,12 +47,12 @@ describe Delayed::MessageSending do
|
|
47
47
|
handle_asynchronously :spin, :priority => Proc.new {|y| y.importance }
|
48
48
|
end
|
49
49
|
|
50
|
-
it
|
50
|
+
it "sets the priority based on the Fable importance" do
|
51
51
|
job = Yarn.new.tap {|y| y.importance = 10 }.spin
|
52
|
-
job.priority.
|
52
|
+
expect(job.priority).to eq(10)
|
53
53
|
|
54
54
|
job = Yarn.new.tap {|y| y.importance = 20 }.spin
|
55
|
-
job.priority.
|
55
|
+
expect(job.priority).to eq(20)
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
@@ -67,47 +67,54 @@ describe Delayed::MessageSending do
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
it "
|
71
|
-
|
70
|
+
it "creates a new PerformableMethod job" do
|
71
|
+
expect {
|
72
72
|
job = "hello".delay.count('l')
|
73
|
-
job.payload_object.class.
|
74
|
-
job.payload_object.method_name.
|
75
|
-
job.payload_object.args.
|
76
|
-
}.
|
73
|
+
expect(job.payload_object.class).to eq(Delayed::PerformableMethod)
|
74
|
+
expect(job.payload_object.method_name).to eq(:count)
|
75
|
+
expect(job.payload_object.args).to eq(['l'])
|
76
|
+
}.to change { Delayed::Job.count }.by(1)
|
77
77
|
end
|
78
78
|
|
79
|
-
it "
|
79
|
+
it "sets default priority" do
|
80
80
|
Delayed::Worker.default_priority = 99
|
81
81
|
job = FairyTail.delay.to_s
|
82
|
-
job.priority.
|
82
|
+
expect(job.priority).to eq(99)
|
83
83
|
Delayed::Worker.default_priority = 0
|
84
84
|
end
|
85
85
|
|
86
|
-
it "
|
86
|
+
it "sets default queue name" do
|
87
|
+
Delayed::Worker.default_queue_name = 'abbazabba'
|
88
|
+
job = FairyTail.delay.to_s
|
89
|
+
expect(job.queue).to eq('abbazabba')
|
90
|
+
Delayed::Worker.default_queue_name = nil
|
91
|
+
end
|
92
|
+
|
93
|
+
it "sets job options" do
|
87
94
|
run_at = Time.parse('2010-05-03 12:55 AM')
|
88
95
|
job = FairyTail.delay(:priority => 20, :run_at => run_at).to_s
|
89
|
-
job.run_at.
|
90
|
-
job.priority.
|
96
|
+
expect(job.run_at).to eq(run_at)
|
97
|
+
expect(job.priority).to eq(20)
|
91
98
|
end
|
92
99
|
|
93
|
-
it "
|
100
|
+
it "does not delay the job when delay_jobs is false" do
|
94
101
|
Delayed::Worker.delay_jobs = false
|
95
102
|
fairy_tail = FairyTail.new
|
96
|
-
|
97
|
-
|
103
|
+
expect {
|
104
|
+
expect {
|
98
105
|
fairy_tail.delay.tell
|
99
|
-
}.
|
100
|
-
}.
|
106
|
+
}.to change(fairy_tail, :happy_ending).from(nil).to(true)
|
107
|
+
}.not_to change { Delayed::Job.count }
|
101
108
|
end
|
102
109
|
|
103
|
-
it "
|
110
|
+
it "does delay the job when delay_jobs is true" do
|
104
111
|
Delayed::Worker.delay_jobs = true
|
105
112
|
fairy_tail = FairyTail.new
|
106
|
-
|
107
|
-
|
113
|
+
expect {
|
114
|
+
expect {
|
108
115
|
fairy_tail.delay.tell
|
109
|
-
}.
|
110
|
-
}.
|
116
|
+
}.not_to change(fairy_tail, :happy_ending)
|
117
|
+
}.to change { Delayed::Job.count }.by(1)
|
111
118
|
end
|
112
119
|
end
|
113
120
|
end
|
@@ -9,27 +9,27 @@ end
|
|
9
9
|
|
10
10
|
describe ActionMailer::Base do
|
11
11
|
describe "delay" do
|
12
|
-
it "
|
13
|
-
|
12
|
+
it "enqueues a PerformableEmail job" do
|
13
|
+
expect {
|
14
14
|
job = MyMailer.delay.signup('john@example.com')
|
15
|
-
job.payload_object.class.
|
16
|
-
job.payload_object.method_name.
|
17
|
-
job.payload_object.args.
|
18
|
-
}.
|
15
|
+
expect(job.payload_object.class).to eq(Delayed::PerformableMailer)
|
16
|
+
expect(job.payload_object.method_name).to eq(:signup)
|
17
|
+
expect(job.payload_object.args).to eq(['john@example.com'])
|
18
|
+
}.to change { Delayed::Job.count }.by(1)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
describe "delay on a mail object" do
|
23
|
-
it "
|
24
|
-
|
23
|
+
it "raises an exception" do
|
24
|
+
expect {
|
25
25
|
MyMailer.signup('john@example.com').delay
|
26
|
-
}.
|
26
|
+
}.to raise_error(RuntimeError)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
describe Delayed::PerformableMailer do
|
31
31
|
describe "perform" do
|
32
|
-
it "
|
32
|
+
it "calls the method and #deliver on the mailer" do
|
33
33
|
email = mock('email', :deliver => true)
|
34
34
|
mailer_class = mock('MailerClass', :signup => email)
|
35
35
|
mailer = Delayed::PerformableMailer.new(mailer_class, :signup, ['john@example.com'])
|
@@ -11,37 +11,37 @@ describe Delayed::PerformableMethod do
|
|
11
11
|
@method.object = nil
|
12
12
|
end
|
13
13
|
|
14
|
-
it "
|
15
|
-
|
14
|
+
it "does nothing if object is nil" do
|
15
|
+
expect{@method.perform}.not_to raise_error
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
it "
|
19
|
+
it "calls the method on the object" do
|
20
20
|
@method.object.should_receive(:count).with('o')
|
21
21
|
@method.perform
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
it "
|
26
|
-
|
25
|
+
it "raises a NoMethodError if target method doesn't exist" do
|
26
|
+
expect {
|
27
27
|
Delayed::PerformableMethod.new(Object, :method_that_does_not_exist, [])
|
28
|
-
}.
|
28
|
+
}.to raise_error(NoMethodError)
|
29
29
|
end
|
30
30
|
|
31
|
-
it "
|
31
|
+
it "does not raise NoMethodError if target method is private" do
|
32
32
|
clazz = Class.new do
|
33
33
|
def private_method
|
34
34
|
end
|
35
35
|
private :private_method
|
36
36
|
end
|
37
|
-
|
37
|
+
expect {
|
38
38
|
Delayed::PerformableMethod.new(clazz.new, :private_method, [])
|
39
|
-
}.
|
39
|
+
}.not_to raise_error(NoMethodError)
|
40
40
|
end
|
41
41
|
|
42
42
|
describe "hooks" do
|
43
43
|
%w(enqueue before after success).each do |hook|
|
44
|
-
it "
|
44
|
+
it "delegates #{hook} hook to object" do
|
45
45
|
story = Story.create
|
46
46
|
story.should_receive(hook).with(an_instance_of(Delayed::Job))
|
47
47
|
story.delay.tell.invoke_job
|
@@ -49,7 +49,7 @@ describe Delayed::PerformableMethod do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
%w(before after success).each do |hook|
|
52
|
-
it "
|
52
|
+
it "delegates #{hook} hook to object when delay_jobs = false" do
|
53
53
|
Delayed::Worker.delay_jobs = false
|
54
54
|
story = Story.create
|
55
55
|
story.should_receive(hook).with(an_instance_of(Delayed::Job))
|
@@ -57,28 +57,28 @@ describe Delayed::PerformableMethod do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
it "
|
60
|
+
it "delegates error hook to object" do
|
61
61
|
story = Story.create
|
62
62
|
story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
|
63
63
|
story.should_receive(:tell).and_raise(RuntimeError)
|
64
|
-
|
64
|
+
expect{story.delay.tell.invoke_job}.to raise_error
|
65
65
|
end
|
66
66
|
|
67
|
-
it "
|
67
|
+
it "delegates error hook to object when delay_jobs = false" do
|
68
68
|
Delayed::Worker.delay_jobs = false
|
69
69
|
story = Story.create
|
70
70
|
story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
|
71
71
|
story.should_receive(:tell).and_raise(RuntimeError)
|
72
|
-
|
72
|
+
expect{story.delay.tell}.to raise_error
|
73
73
|
end
|
74
74
|
|
75
|
-
it "
|
75
|
+
it "delegates failure hook to object" do
|
76
76
|
method = Delayed::PerformableMethod.new("object", :size, [])
|
77
77
|
method.object.should_receive(:failure)
|
78
78
|
method.failure
|
79
79
|
end
|
80
80
|
|
81
|
-
it "
|
81
|
+
it "delegates failure hook to object when delay_jobs = false" do
|
82
82
|
Delayed::Worker.delay_jobs = false
|
83
83
|
method = Delayed::PerformableMethod.new("object", :size, [])
|
84
84
|
method.object.should_receive(:failure)
|
data/spec/spec_helper.rb
CHANGED
data/spec/test_backend_spec.rb
CHANGED
@@ -4,10 +4,10 @@ describe Delayed::Backend::Test::Job do
|
|
4
4
|
it_should_behave_like 'a delayed_job backend'
|
5
5
|
|
6
6
|
describe "#reload" do
|
7
|
-
it
|
7
|
+
it "causes the payload object to be reloaded" do
|
8
8
|
job = "foo".delay.length
|
9
9
|
o = job.payload_object
|
10
|
-
o.object_id.
|
10
|
+
expect(o.object_id).not_to eq(job.reload.payload_object.object_id)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
data/spec/worker_spec.rb
CHANGED
@@ -7,13 +7,34 @@ describe Delayed::Worker do
|
|
7
7
|
Delayed::Worker.backend = @clazz
|
8
8
|
end
|
9
9
|
|
10
|
-
it "
|
11
|
-
Delayed::Job.
|
10
|
+
it "sets the Delayed::Job constant to the backend" do
|
11
|
+
expect(Delayed::Job).to eq(@clazz)
|
12
12
|
end
|
13
13
|
|
14
|
-
it "
|
14
|
+
it "sets backend with a symbol" do
|
15
15
|
Delayed::Worker.backend = :test
|
16
|
-
Delayed::Worker.backend.
|
16
|
+
expect(Delayed::Worker.backend).to eq(Delayed::Backend::Test::Job)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "worker read-ahead" do
|
21
|
+
before do
|
22
|
+
@read_ahead = Delayed::Worker.read_ahead
|
23
|
+
end
|
24
|
+
|
25
|
+
after do
|
26
|
+
Delayed::Worker.read_ahead = @read_ahead
|
27
|
+
end
|
28
|
+
|
29
|
+
it "reads five jobs" do
|
30
|
+
Delayed::Job.should_receive(:find_available).with(anything, 5, anything).and_return([])
|
31
|
+
Delayed::Job.reserve(Delayed::Worker.new)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "reads a configurable number of jobs" do
|
35
|
+
Delayed::Worker.read_ahead = 15
|
36
|
+
Delayed::Job.should_receive(:find_available).with(anything, Delayed::Worker.read_ahead, anything).and_return([])
|
37
|
+
Delayed::Job.reserve(Delayed::Worker.new)
|
17
38
|
end
|
18
39
|
end
|
19
40
|
end
|