delayed_job_unique_key 0.0.1
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/MIT-LICENSE +20 -0
- data/README.textile +246 -0
- data/contrib/delayed_job.monitrc +14 -0
- data/contrib/delayed_job_multiple.monitrc +23 -0
- data/lib/delayed/backend/base.rb +152 -0
- data/lib/delayed/backend/shared_spec.rb +566 -0
- data/lib/delayed/command.rb +101 -0
- data/lib/delayed/deserialization_error.rb +4 -0
- data/lib/delayed/lifecycle.rb +84 -0
- data/lib/delayed/message_sending.rb +54 -0
- data/lib/delayed/performable_mailer.rb +21 -0
- data/lib/delayed/performable_method.rb +33 -0
- data/lib/delayed/plugin.rb +15 -0
- data/lib/delayed/plugins/clear_locks.rb +15 -0
- data/lib/delayed/psych_ext.rb +75 -0
- data/lib/delayed/railtie.rb +16 -0
- data/lib/delayed/recipes.rb +50 -0
- data/lib/delayed/serialization/active_record.rb +19 -0
- data/lib/delayed/syck_ext.rb +34 -0
- data/lib/delayed/tasks.rb +11 -0
- data/lib/delayed/worker.rb +222 -0
- data/lib/delayed/yaml_ext.rb +10 -0
- data/lib/delayed_job.rb +22 -0
- data/lib/generators/delayed_job/delayed_job_generator.rb +11 -0
- data/lib/generators/delayed_job/templates/script +5 -0
- data/recipes/delayed_job.rb +1 -0
- data/spec/autoloaded/clazz.rb +7 -0
- data/spec/autoloaded/instance_clazz.rb +6 -0
- data/spec/autoloaded/instance_struct.rb +6 -0
- data/spec/autoloaded/struct.rb +7 -0
- data/spec/delayed/backend/test.rb +113 -0
- data/spec/delayed/serialization/test.rb +0 -0
- data/spec/fixtures/bad_alias.yml +1 -0
- data/spec/lifecycle_spec.rb +107 -0
- data/spec/message_sending_spec.rb +116 -0
- data/spec/performable_mailer_spec.rb +46 -0
- data/spec/performable_method_spec.rb +89 -0
- data/spec/sample_jobs.rb +75 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/test_backend_spec.rb +13 -0
- data/spec/worker_spec.rb +19 -0
- data/spec/yaml_ext_spec.rb +41 -0
- metadata +197 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class DelayedJobGenerator < Rails::Generators::Base
|
4
|
+
|
5
|
+
self.source_paths << File.join(File.dirname(__FILE__), 'templates')
|
6
|
+
|
7
|
+
def create_script_file
|
8
|
+
template 'script', 'script/delayed_job'
|
9
|
+
chmod 'script/delayed_job', 0755
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'delayed', 'recipes'))
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
# An in-memory backend suitable only for testing. Tries to behave as if it were an ORM.
|
4
|
+
module Delayed
|
5
|
+
module Backend
|
6
|
+
module Test
|
7
|
+
class Job
|
8
|
+
attr_accessor :id
|
9
|
+
attr_accessor :priority
|
10
|
+
attr_accessor :attempts
|
11
|
+
attr_accessor :handler
|
12
|
+
attr_accessor :last_error
|
13
|
+
attr_accessor :run_at
|
14
|
+
attr_accessor :locked_at
|
15
|
+
attr_accessor :locked_by
|
16
|
+
attr_accessor :failed_at
|
17
|
+
attr_accessor :queue
|
18
|
+
attr_accessor :unique_key
|
19
|
+
|
20
|
+
include Delayed::Backend::Base
|
21
|
+
|
22
|
+
cattr_accessor :id
|
23
|
+
self.id = 0
|
24
|
+
|
25
|
+
def initialize(hash = {})
|
26
|
+
self.attempts = 0
|
27
|
+
self.priority = 0
|
28
|
+
self.id = (self.class.id += 1)
|
29
|
+
hash.each{|k,v| send(:"#{k}=", v)}
|
30
|
+
end
|
31
|
+
|
32
|
+
@jobs = []
|
33
|
+
def self.all
|
34
|
+
@jobs
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.count
|
38
|
+
all.size
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.delete_all
|
42
|
+
all.clear
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.create(attrs = {})
|
46
|
+
new(attrs).tap do |o|
|
47
|
+
o.save
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.create!(*args); create(*args); end
|
52
|
+
|
53
|
+
def self.clear_locks!(worker_name)
|
54
|
+
all.select{|j| j.locked_by == worker_name}.each {|j| j.locked_by = nil; j.locked_at = nil}
|
55
|
+
end
|
56
|
+
|
57
|
+
# Find a few candidate jobs to run (in case some immediately get locked by others).
|
58
|
+
def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)
|
59
|
+
jobs = all.select do |j|
|
60
|
+
j.run_at <= db_time_now &&
|
61
|
+
(j.locked_at.nil? || j.locked_at < db_time_now - max_run_time || j.locked_by == worker_name) &&
|
62
|
+
j.failed_at.nil?
|
63
|
+
end
|
64
|
+
|
65
|
+
jobs = jobs.select{|j| Worker.queues.include?(j.queue)} if Worker.queues.any?
|
66
|
+
jobs = jobs.select{|j| j.priority >= Worker.min_priority} if Worker.min_priority
|
67
|
+
jobs = jobs.select{|j| j.priority <= Worker.max_priority} if Worker.max_priority
|
68
|
+
jobs.sort_by{|j| [j.priority, j.run_at]}[0..limit-1]
|
69
|
+
end
|
70
|
+
|
71
|
+
# Lock this job for this worker.
|
72
|
+
# Returns true if we have the lock, false otherwise.
|
73
|
+
def lock_exclusively!(max_run_time, worker)
|
74
|
+
now = self.class.db_time_now
|
75
|
+
if locked_by != worker
|
76
|
+
# We don't own this job so we will update the locked_by name and the locked_at
|
77
|
+
self.locked_at = now
|
78
|
+
self.locked_by = worker
|
79
|
+
end
|
80
|
+
|
81
|
+
return true
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.db_time_now
|
85
|
+
Time.current
|
86
|
+
end
|
87
|
+
|
88
|
+
def update_attributes(attrs = {})
|
89
|
+
attrs.each{|k,v| send(:"#{k}=", v)}
|
90
|
+
save
|
91
|
+
end
|
92
|
+
|
93
|
+
def destroy
|
94
|
+
self.class.all.delete(self)
|
95
|
+
end
|
96
|
+
|
97
|
+
def save
|
98
|
+
self.run_at ||= Time.current
|
99
|
+
|
100
|
+
self.class.all << self unless self.class.all.include?(self)
|
101
|
+
true
|
102
|
+
end
|
103
|
+
|
104
|
+
def save!; save; end
|
105
|
+
|
106
|
+
def reload
|
107
|
+
reset
|
108
|
+
self
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
foo: *bar
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Delayed::Lifecycle do
|
4
|
+
let(:lifecycle) { Delayed::Lifecycle.new }
|
5
|
+
let(:callback) { lambda {|*args|} }
|
6
|
+
let(:arguments) { [1] }
|
7
|
+
let(:behavior) { mock(Object, :before! => nil, :after! => nil, :inside! => nil) }
|
8
|
+
let(:wrapped_block) { Proc.new { behavior.inside! } }
|
9
|
+
|
10
|
+
describe "before callbacks" do
|
11
|
+
before(:each) do
|
12
|
+
lifecycle.before(:execute, &callback)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should execute before wrapped block' do
|
16
|
+
callback.should_receive(:call).with(*arguments).ordered
|
17
|
+
behavior.should_receive(:inside!).ordered
|
18
|
+
lifecycle.run_callbacks :execute, *arguments, &wrapped_block
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "after callbacks" do
|
23
|
+
before(:each) do
|
24
|
+
lifecycle.after(:execute, &callback)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should execute after wrapped block' do
|
28
|
+
behavior.should_receive(:inside!).ordered
|
29
|
+
callback.should_receive(:call).with(*arguments).ordered
|
30
|
+
lifecycle.run_callbacks :execute, *arguments, &wrapped_block
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "around callbacks" do
|
35
|
+
before(:each) do
|
36
|
+
lifecycle.around(:execute) do |*args, &block|
|
37
|
+
behavior.before!
|
38
|
+
block.call(*args)
|
39
|
+
behavior.after!
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should before and after wrapped block' do
|
44
|
+
behavior.should_receive(:before!).ordered
|
45
|
+
behavior.should_receive(:inside!).ordered
|
46
|
+
behavior.should_receive(:after!).ordered
|
47
|
+
lifecycle.run_callbacks :execute, *arguments, &wrapped_block
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should execute multiple callbacks in order" do
|
51
|
+
behavior.should_receive(:one).ordered
|
52
|
+
behavior.should_receive(:two).ordered
|
53
|
+
behavior.should_receive(:three).ordered
|
54
|
+
|
55
|
+
lifecycle.around(:execute) { |*args, &block| behavior.one; block.call(*args) }
|
56
|
+
lifecycle.around(:execute) { |*args, &block| behavior.two; block.call(*args) }
|
57
|
+
lifecycle.around(:execute) { |*args, &block| behavior.three; block.call(*args) }
|
58
|
+
|
59
|
+
lifecycle.run_callbacks(:execute, *arguments, &wrapped_block)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should raise if callback is executed with wrong number of parameters" do
|
64
|
+
lifecycle.before(:execute, &callback)
|
65
|
+
expect { lifecycle.run_callbacks(:execute, 1,2,3) {} }.to raise_error(ArgumentError, /1 parameter/)
|
66
|
+
end
|
67
|
+
|
68
|
+
# # This is a spectacularly crappy way to test callbacks. What's a better way?
|
69
|
+
# describe 'arguments callbacks' do
|
70
|
+
# subject do
|
71
|
+
# class Testarguments < Delayed::arguments
|
72
|
+
# def before_execute; end
|
73
|
+
# def before_loop; end
|
74
|
+
# def before_perform; end
|
75
|
+
#
|
76
|
+
# set_callback :execute, :before, :before_execute
|
77
|
+
# set_callback :loop, :before, :before_loop
|
78
|
+
# set_callback :perform, :before, :before_perform
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# Testarguments.new.tap { |w| w.stop }
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# it "should trigger for execute event" do
|
85
|
+
# subject.should_receive(:before_execute).with()
|
86
|
+
# subject.start
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# it "should trigger for loop event" do
|
90
|
+
# subject.should_receive(:before_loop).with()
|
91
|
+
# subject.start
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# it "should trigger for perform event" do
|
95
|
+
# "foo".delay.length
|
96
|
+
# subject.should_receive(:before_perform).with()
|
97
|
+
# subject.start
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# describe 'job callbacks' do
|
102
|
+
# it "should trigger for enqueue event" do
|
103
|
+
# pending 'figure out how to test this'
|
104
|
+
# end
|
105
|
+
# end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Delayed::MessageSending do
|
4
|
+
describe "handle_asynchronously" do
|
5
|
+
class Story
|
6
|
+
def tell!(arg)
|
7
|
+
end
|
8
|
+
handle_asynchronously :tell!
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should alias original method" do
|
12
|
+
Story.new.should respond_to(:tell_without_delay!)
|
13
|
+
Story.new.should respond_to(:tell_with_delay!)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should create a PerformableMethod" do
|
17
|
+
story = Story.new
|
18
|
+
lambda {
|
19
|
+
job = story.tell!(1)
|
20
|
+
job.payload_object.class.should == Delayed::PerformableMethod
|
21
|
+
job.payload_object.method_name.should == :tell_without_delay!
|
22
|
+
job.payload_object.args.should == [1]
|
23
|
+
}.should change { Delayed::Job.count }
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'with options' do
|
27
|
+
class Fable
|
28
|
+
class << self
|
29
|
+
attr_accessor :importance
|
30
|
+
end
|
31
|
+
def tell
|
32
|
+
end
|
33
|
+
handle_asynchronously :tell, :priority => Proc.new { self.importance }
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should set the priority based on the Fable importance' do
|
37
|
+
Fable.importance = 10
|
38
|
+
job = Fable.new.tell
|
39
|
+
job.priority.should == 10
|
40
|
+
|
41
|
+
Fable.importance = 20
|
42
|
+
job = Fable.new.tell
|
43
|
+
job.priority.should == 20
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'using a proc with parament' do
|
47
|
+
class Yarn
|
48
|
+
attr_accessor :importance
|
49
|
+
def spin
|
50
|
+
end
|
51
|
+
handle_asynchronously :spin, :priority => Proc.new {|y| y.importance }
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should set the priority based on the Fable importance' do
|
55
|
+
job = Yarn.new.tap {|y| y.importance = 10 }.spin
|
56
|
+
job.priority.should == 10
|
57
|
+
|
58
|
+
job = Yarn.new.tap {|y| y.importance = 20 }.spin
|
59
|
+
job.priority.should == 20
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "delay" do
|
66
|
+
it "should create a new PerformableMethod job" do
|
67
|
+
lambda {
|
68
|
+
job = "hello".delay.count('l')
|
69
|
+
job.payload_object.class.should == Delayed::PerformableMethod
|
70
|
+
job.payload_object.method_name.should == :count
|
71
|
+
job.payload_object.args.should == ['l']
|
72
|
+
}.should change { Delayed::Job.count }.by(1)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should set default priority" do
|
76
|
+
Delayed::Worker.default_priority = 99
|
77
|
+
job = Object.delay.to_s
|
78
|
+
job.priority.should == 99
|
79
|
+
Delayed::Worker.default_priority = 0
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should set job options" do
|
83
|
+
run_at = Time.parse('2010-05-03 12:55 AM')
|
84
|
+
job = Object.delay(:priority => 20, :run_at => run_at).to_s
|
85
|
+
job.run_at.should == run_at
|
86
|
+
job.priority.should == 20
|
87
|
+
end
|
88
|
+
|
89
|
+
class FairyTail
|
90
|
+
attr_accessor :happy_ending
|
91
|
+
def tell
|
92
|
+
@happy_ending = true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should not delay the job when delay_jobs is false" do
|
97
|
+
Delayed::Worker.delay_jobs = false
|
98
|
+
fairy_tail = FairyTail.new
|
99
|
+
lambda {
|
100
|
+
lambda {
|
101
|
+
fairy_tail.delay.tell
|
102
|
+
}.should change(fairy_tail, :happy_ending).from(nil).to(true)
|
103
|
+
}.should_not change { Delayed::Job.count }
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should delay the job when delay_jobs is true" do
|
107
|
+
Delayed::Worker.delay_jobs = true
|
108
|
+
fairy_tail = FairyTail.new
|
109
|
+
lambda {
|
110
|
+
lambda {
|
111
|
+
fairy_tail.delay.tell
|
112
|
+
}.should_not change(fairy_tail, :happy_ending)
|
113
|
+
}.should change { Delayed::Job.count }.by(1)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'action_mailer'
|
4
|
+
class MyMailer < ActionMailer::Base
|
5
|
+
def signup(email)
|
6
|
+
mail :to => email, :subject => "Delaying Emails"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ActionMailer::Base do
|
11
|
+
describe "delay" do
|
12
|
+
it "should enqueue a PerformableEmail job" do
|
13
|
+
lambda {
|
14
|
+
job = MyMailer.delay.signup('john@example.com')
|
15
|
+
job.payload_object.class.should == Delayed::PerformableMailer
|
16
|
+
job.payload_object.method_name.should == :signup
|
17
|
+
job.payload_object.args.should == ['john@example.com']
|
18
|
+
}.should change { Delayed::Job.count }.by(1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "delay on a mail object" do
|
23
|
+
it "should raise an exception" do
|
24
|
+
lambda {
|
25
|
+
MyMailer.signup('john@example.com').delay
|
26
|
+
}.should raise_error(RuntimeError)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe Delayed::PerformableMailer do
|
31
|
+
describe "perform" do
|
32
|
+
before do
|
33
|
+
@email = mock('email', :deliver => true)
|
34
|
+
@mailer_class = mock('MailerClass', :signup => @email)
|
35
|
+
@mailer = Delayed::PerformableMailer.new(@mailer_class, :signup, ['john@example.com'])
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should call the method and #deliver on the mailer" do
|
39
|
+
@mailer_class.should_receive(:signup).with('john@example.com')
|
40
|
+
@email.should_receive(:deliver)
|
41
|
+
@mailer.perform
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Delayed::PerformableMethod do
|
4
|
+
describe "perform" do
|
5
|
+
before do
|
6
|
+
@method = Delayed::PerformableMethod.new("foo", :count, ['o'])
|
7
|
+
end
|
8
|
+
|
9
|
+
context "with the persisted record cannot be found" do
|
10
|
+
before do
|
11
|
+
@method.object = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be a no-op if object is nil" do
|
15
|
+
lambda { @method.perform }.should_not raise_error
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should call the method on the object" do
|
20
|
+
@method.object.should_receive(:count).with('o')
|
21
|
+
@method.perform
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should raise a NoMethodError if target method doesn't exist" do
|
26
|
+
lambda {
|
27
|
+
Delayed::PerformableMethod.new(Object, :method_that_does_not_exist, [])
|
28
|
+
}.should raise_error(NoMethodError)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should not raise NoMethodError if target method is private" do
|
32
|
+
clazz = Class.new do
|
33
|
+
def private_method
|
34
|
+
end
|
35
|
+
private :private_method
|
36
|
+
end
|
37
|
+
lambda {
|
38
|
+
Delayed::PerformableMethod.new(clazz.new, :private_method, [])
|
39
|
+
}.should_not raise_error(NoMethodError)
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "hooks" do
|
43
|
+
%w(enqueue before after success).each do |hook|
|
44
|
+
it "should delegate #{hook} hook to object" do
|
45
|
+
story = Story.new
|
46
|
+
story.should_receive(hook).with(an_instance_of(Delayed::Job))
|
47
|
+
story.delay.tell.invoke_job
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
%w(before after success).each do |hook|
|
52
|
+
it "should delegate #{hook} hook to object when delay_jobs = false" do
|
53
|
+
Delayed::Worker.delay_jobs = false
|
54
|
+
story = Story.new
|
55
|
+
story.should_receive(hook).with(an_instance_of(Delayed::Job))
|
56
|
+
story.delay.tell
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should delegate error hook to object" do
|
61
|
+
story = Story.new
|
62
|
+
story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
|
63
|
+
story.should_receive(:tell).and_raise(RuntimeError)
|
64
|
+
lambda { story.delay.tell.invoke_job }.should raise_error
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should delegate error hook to object when delay_jobs = false" do
|
68
|
+
Delayed::Worker.delay_jobs = false
|
69
|
+
story = Story.new
|
70
|
+
story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
|
71
|
+
story.should_receive(:tell).and_raise(RuntimeError)
|
72
|
+
lambda { story.delay.tell }.should raise_error
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should delegate failure hook to object" do
|
76
|
+
method = Delayed::PerformableMethod.new("object", :size, [])
|
77
|
+
method.object.should_receive(:failure)
|
78
|
+
method.failure
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should delegate failure hook to object when delay_jobs = false" do
|
82
|
+
Delayed::Worker.delay_jobs = false
|
83
|
+
method = Delayed::PerformableMethod.new("object", :size, [])
|
84
|
+
method.object.should_receive(:failure)
|
85
|
+
method.failure
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
data/spec/sample_jobs.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
class NamedJob < Struct.new(:perform)
|
2
|
+
def display_name
|
3
|
+
'named_job'
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class SimpleJob
|
8
|
+
cattr_accessor :runs; self.runs = 0
|
9
|
+
def perform; @@runs += 1; end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ErrorJob
|
13
|
+
cattr_accessor :runs; self.runs = 0
|
14
|
+
def perform; raise 'did not work'; end
|
15
|
+
end
|
16
|
+
|
17
|
+
class CustomRescheduleJob < Struct.new(:offset)
|
18
|
+
cattr_accessor :runs; self.runs = 0
|
19
|
+
def perform; raise 'did not work'; end
|
20
|
+
def reschedule_at(time, attempts); time + offset; end
|
21
|
+
end
|
22
|
+
|
23
|
+
class LongRunningJob
|
24
|
+
def perform; sleep 250; end
|
25
|
+
end
|
26
|
+
|
27
|
+
class OnPermanentFailureJob < SimpleJob
|
28
|
+
def failure; end
|
29
|
+
def max_attempts; 1; end
|
30
|
+
end
|
31
|
+
|
32
|
+
module M
|
33
|
+
class ModuleJob
|
34
|
+
cattr_accessor :runs; self.runs = 0
|
35
|
+
def perform; @@runs += 1; end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class CallbackJob
|
40
|
+
cattr_accessor :messages
|
41
|
+
|
42
|
+
def enqueue(job)
|
43
|
+
self.class.messages << 'enqueue'
|
44
|
+
end
|
45
|
+
|
46
|
+
def before(job)
|
47
|
+
self.class.messages << 'before'
|
48
|
+
end
|
49
|
+
|
50
|
+
def perform
|
51
|
+
self.class.messages << 'perform'
|
52
|
+
end
|
53
|
+
|
54
|
+
def after(job)
|
55
|
+
self.class.messages << 'after'
|
56
|
+
end
|
57
|
+
|
58
|
+
def success(job)
|
59
|
+
self.class.messages << 'success'
|
60
|
+
end
|
61
|
+
|
62
|
+
def error(job, error)
|
63
|
+
self.class.messages << "error: #{error.class}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def failure(job)
|
67
|
+
self.class.messages << 'failure'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class EnqueueJobMod < SimpleJob
|
72
|
+
def enqueue(job)
|
73
|
+
job.run_at = 20.minutes.from_now
|
74
|
+
end
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
require 'rails'
|
7
|
+
require 'action_mailer'
|
8
|
+
require 'active_support/dependencies'
|
9
|
+
require 'active_record'
|
10
|
+
|
11
|
+
require 'delayed_job'
|
12
|
+
require 'delayed/backend/shared_spec'
|
13
|
+
|
14
|
+
Delayed::Worker.logger = Logger.new('/tmp/dj.log')
|
15
|
+
ENV['RAILS_ENV'] = 'test'
|
16
|
+
|
17
|
+
Delayed::Worker.backend = :test
|
18
|
+
|
19
|
+
# Add this directory so the ActiveSupport autoloading works
|
20
|
+
ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
|
21
|
+
|
22
|
+
# Add this to simulate Railtie initializer being executed
|
23
|
+
ActionMailer::Base.send(:extend, Delayed::DelayMail)
|
24
|
+
|
25
|
+
|
26
|
+
# Used to test interactions between DJ and an ORM
|
27
|
+
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
|
28
|
+
ActiveRecord::Base.logger = Delayed::Worker.logger
|
29
|
+
ActiveRecord::Migration.verbose = false
|
30
|
+
|
31
|
+
ActiveRecord::Schema.define do
|
32
|
+
create_table :stories, :primary_key => :story_id, :force => true do |table|
|
33
|
+
table.string :text
|
34
|
+
table.boolean :scoped, :default => true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Story < ActiveRecord::Base
|
39
|
+
set_primary_key :story_id
|
40
|
+
def tell; text; end
|
41
|
+
def whatever(n, _); tell*n; end
|
42
|
+
default_scope where(:scoped => true)
|
43
|
+
|
44
|
+
handle_asynchronously :whatever
|
45
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Delayed::Backend::Test::Job do
|
4
|
+
it_should_behave_like 'a delayed_job backend'
|
5
|
+
|
6
|
+
describe "#reload" do
|
7
|
+
it 'should cause the payload object to be reloaded' do
|
8
|
+
job = "foo".delay.length
|
9
|
+
o = job.payload_object
|
10
|
+
o.object_id.should_not == job.reload.payload_object.object_id
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/spec/worker_spec.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Delayed::Worker do
|
4
|
+
describe "backend=" do
|
5
|
+
before do
|
6
|
+
@clazz = Class.new
|
7
|
+
Delayed::Worker.backend = @clazz
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should set the Delayed::Job constant to the backend" do
|
11
|
+
Delayed::Job.should == @clazz
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should set backend with a symbol" do
|
15
|
+
Delayed::Worker.backend = :test
|
16
|
+
Delayed::Worker.backend.should == Delayed::Backend::Test::Job
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|