delayed_job 3.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +29 -15
- data/lib/delayed/backend/shared_spec.rb +10 -10
- data/lib/delayed/psych_ext.rb +63 -6
- data/lib/delayed/serialization/active_record.rb +1 -1
- data/lib/delayed/syck_ext.rb +1 -1
- data/lib/delayed/yaml_ext.rb +1 -1
- data/spec/lifecycle_spec.rb +0 -40
- data/spec/message_sending_spec.rb +20 -23
- data/spec/performable_mailer_spec.rb +10 -12
- data/spec/yaml_ext_spec.rb +2 -2
- metadata +4 -4
data/README.textile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
h1. Delayed::Job
|
1
|
+
h1. Delayed::Job "!http://travis-ci.org/collectiveidea/delayed_job.png!":http://travis-ci.org/collectiveidea/delayed_job "!https://gemnasium.com/collectiveidea/delayed_job.png?travis!":https://gemnasium.com/collectiveidea/delayed_job
|
2
2
|
|
3
3
|
Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background.
|
4
4
|
|
@@ -18,6 +18,8 @@ h2. Installation
|
|
18
18
|
|
19
19
|
delayed_job 3.0.0 only supports Rails 3.0+. See the "2.0 branch":https://github.com/collectiveidea/delayed_job/tree/v2.0 for Rails 2.
|
20
20
|
|
21
|
+
delayed_job supports multiple backends for storing the job queue. "See the wiki for other backends":http://wiki.github.com/collectiveidea/delayed_job/backends.
|
22
|
+
|
21
23
|
If you plan to use delayed_job with Active Record, add @delayed_job_active_record@ to your @Gemfile@.
|
22
24
|
|
23
25
|
<pre>
|
@@ -32,11 +34,7 @@ gem 'delayed_job_mongoid'
|
|
32
34
|
|
33
35
|
Run @bundle install@ to install the backend and delayed_job gems.
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
delayed_job supports multiple backends for storing the job queue. "See the wiki for other backends":http://wiki.github.com/collectiveidea/delayed_job/backends besides Active Record.
|
38
|
-
|
39
|
-
The default is Active Record, which requires a jobs table. You can create that table by running the following command:
|
37
|
+
The Active Record backend requires a jobs table. You can create that table by running the following command:
|
40
38
|
|
41
39
|
<pre>
|
42
40
|
$ script/rails generate delayed_job:active_record
|
@@ -80,7 +78,7 @@ device = Device.new
|
|
80
78
|
device.deliver
|
81
79
|
</pre>
|
82
80
|
|
83
|
-
handle_asynchronously can take as options anything you can pass to delay. In addition the values can be Proc objects allowing call time evaluation of the value. For some examples:
|
81
|
+
handle_asynchronously can take as options anything you can pass to delay. In addition, the values can be Proc objects allowing call time evaluation of the value. For some examples:
|
84
82
|
|
85
83
|
<pre>
|
86
84
|
class LongTasks
|
@@ -127,6 +125,19 @@ Notifier.delay.signup(@user)
|
|
127
125
|
|
128
126
|
Remove the @.deliver@ method to make it work. It's not ideal, but it's the best we could do for now.
|
129
127
|
|
128
|
+
h3. Named Queues
|
129
|
+
|
130
|
+
DJ 3 introduces Resque-style named queues while still retaining DJ-style priority. The goal is to provide a system for grouping tasks to be worked by separate pools of workers, which may be scaled and controlled individually.
|
131
|
+
|
132
|
+
Jobs can be assigned to a queue by setting the @queue@ option:
|
133
|
+
|
134
|
+
<pre>object.delay(:queue => 'tracking').method
|
135
|
+
|
136
|
+
Delayed::Job.enqueue job, :queue => 'tracking'
|
137
|
+
|
138
|
+
handle_asynchronously :tweet_later, :queue => 'tweets'
|
139
|
+
</pre>
|
140
|
+
|
130
141
|
h2. Running Jobs
|
131
142
|
|
132
143
|
@script/delayed_job@ can be used to manage a background process which will start working off jobs.
|
@@ -142,12 +153,23 @@ $ RAILS_ENV=production script/delayed_job stop
|
|
142
153
|
# Runs two workers in separate processes.
|
143
154
|
$ RAILS_ENV=production script/delayed_job -n 2 start
|
144
155
|
$ RAILS_ENV=production script/delayed_job stop
|
156
|
+
|
157
|
+
# Set the --queue or --queues option to work from a particular queue.
|
158
|
+
$ RAILS_ENV=production script/delayed_job --queue=tracking start
|
159
|
+
$ RAILS_ENV=production script/delayed_job --queues=mailers,tasks start
|
145
160
|
</pre>
|
146
161
|
|
147
162
|
Workers can be running on any computer, as long as they have access to the database and their clock is in sync. Keep in mind that each worker will check the database at least every 5 seconds.
|
148
163
|
|
149
164
|
You can also invoke @rake jobs:work@ which will start working off jobs. You can cancel the rake task with @CTRL-C@.
|
150
165
|
|
166
|
+
Work off queues by setting the @QUEUE@ or @QUEUES@ environment variable.
|
167
|
+
|
168
|
+
<pre>
|
169
|
+
QUEUE=tracking rake jobs:work
|
170
|
+
QUEUES=mailers,tasks rake jobs:work
|
171
|
+
</pre>
|
172
|
+
|
151
173
|
h2. Custom Jobs
|
152
174
|
|
153
175
|
Jobs are simple ruby objects with a method called perform. Any object which responds to perform can be stuffed into the jobs table. Job objects are serialized to yaml so that they can later be resurrected by the job runner.
|
@@ -251,14 +273,6 @@ h2. Mailing List
|
|
251
273
|
|
252
274
|
Join us on the "mailing list":http://groups.google.com/group/delayed_job
|
253
275
|
|
254
|
-
h2. Build Status
|
255
|
-
|
256
|
-
"!http://travis-ci.org/collectiveidea/delayed_job.png!":http://travis-ci.org/collectiveidea/delayed_job
|
257
|
-
|
258
|
-
h2. Dependency Status
|
259
|
-
|
260
|
-
"!https://gemnasium.com/collectiveidea/delayed_job.png?travis!":https://gemnasium.com/collectiveidea/delayed_job
|
261
|
-
|
262
276
|
h2. How to contribute
|
263
277
|
|
264
278
|
If you find what looks like a bug:
|
@@ -237,8 +237,8 @@ shared_examples_for 'a delayed_job backend' do
|
|
237
237
|
end
|
238
238
|
|
239
239
|
it "should be the instance method that will be called if its a performable method object" do
|
240
|
-
|
241
|
-
|
240
|
+
job = Story.create(:text => "...").delay.save
|
241
|
+
job.name.should == 'Story#save'
|
242
242
|
end
|
243
243
|
|
244
244
|
it "should parse from handler on deserialization error" do
|
@@ -415,10 +415,10 @@ shared_examples_for 'a delayed_job backend' do
|
|
415
415
|
begin
|
416
416
|
old_max_run_time = Delayed::Worker.max_run_time
|
417
417
|
Delayed::Worker.max_run_time = 1.second
|
418
|
-
|
419
|
-
worker.run(
|
420
|
-
|
421
|
-
|
418
|
+
job = Delayed::Job.create :payload_object => LongRunningJob.new
|
419
|
+
worker.run(job)
|
420
|
+
job.reload.last_error.should =~ /expired/
|
421
|
+
job.attempts.should == 1
|
422
422
|
ensure
|
423
423
|
Delayed::Worker.max_run_time = old_max_run_time
|
424
424
|
end
|
@@ -467,11 +467,11 @@ shared_examples_for 'a delayed_job backend' do
|
|
467
467
|
end
|
468
468
|
|
469
469
|
it 'should re-schedule with handler provided time if present' do
|
470
|
-
|
471
|
-
worker.run(
|
472
|
-
|
470
|
+
job = Delayed::Job.enqueue(CustomRescheduleJob.new(99.minutes))
|
471
|
+
worker.run(job)
|
472
|
+
job.reload
|
473
473
|
|
474
|
-
(Delayed::Job.db_time_now + 99.minutes -
|
474
|
+
(Delayed::Job.db_time_now + 99.minutes - job.run_at).abs.should < 1
|
475
475
|
end
|
476
476
|
|
477
477
|
it "should not fail when the triggered error doesn't have a message" do
|
data/lib/delayed/psych_ext.rb
CHANGED
@@ -23,19 +23,77 @@ module Psych
|
|
23
23
|
module Visitors
|
24
24
|
class YAMLTree
|
25
25
|
def visit_Class(klass)
|
26
|
-
|
27
|
-
register(klass, @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK))
|
28
|
-
@emitter.end_mapping
|
26
|
+
@emitter.scalar klass.name, nil, '!ruby/class', false, false, Nodes::Scalar::SINGLE_QUOTED
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
32
30
|
class ToRuby
|
31
|
+
def visit_Psych_Nodes_Scalar(o)
|
32
|
+
@st[o.anchor] = o.value if o.anchor
|
33
|
+
|
34
|
+
if klass = Psych.load_tags[o.tag]
|
35
|
+
instance = klass.allocate
|
36
|
+
|
37
|
+
if instance.respond_to?(:init_with)
|
38
|
+
coder = Psych::Coder.new(o.tag)
|
39
|
+
coder.scalar = o.value
|
40
|
+
instance.init_with coder
|
41
|
+
end
|
42
|
+
|
43
|
+
return instance
|
44
|
+
end
|
45
|
+
|
46
|
+
return o.value if o.quoted
|
47
|
+
return @ss.tokenize(o.value) unless o.tag
|
48
|
+
|
49
|
+
case o.tag
|
50
|
+
when '!binary', 'tag:yaml.org,2002:binary'
|
51
|
+
o.value.unpack('m').first
|
52
|
+
when '!str', 'tag:yaml.org,2002:str'
|
53
|
+
o.value
|
54
|
+
when "!ruby/object:DateTime"
|
55
|
+
require 'date'
|
56
|
+
@ss.parse_time(o.value).to_datetime
|
57
|
+
when "!ruby/object:Complex"
|
58
|
+
Complex(o.value)
|
59
|
+
when "!ruby/object:Rational"
|
60
|
+
Rational(o.value)
|
61
|
+
when "!ruby/class", "!ruby/module"
|
62
|
+
resolve_class o.value
|
63
|
+
when "tag:yaml.org,2002:float", "!float"
|
64
|
+
Float(@ss.tokenize(o.value))
|
65
|
+
when "!ruby/regexp"
|
66
|
+
o.value =~ /^\/(.*)\/([mixn]*)$/
|
67
|
+
source = $1
|
68
|
+
options = 0
|
69
|
+
lang = nil
|
70
|
+
($2 || '').split('').each do |option|
|
71
|
+
case option
|
72
|
+
when 'x' then options |= Regexp::EXTENDED
|
73
|
+
when 'i' then options |= Regexp::IGNORECASE
|
74
|
+
when 'm' then options |= Regexp::MULTILINE
|
75
|
+
when 'n' then options |= Regexp::NOENCODING
|
76
|
+
else lang = option
|
77
|
+
end
|
78
|
+
end
|
79
|
+
Regexp.new(*[source, options, lang].compact)
|
80
|
+
when "!ruby/range"
|
81
|
+
args = o.value.split(/([.]{2,3})/, 2).map { |s|
|
82
|
+
accept Nodes::Scalar.new(s)
|
83
|
+
}
|
84
|
+
args.push(args.delete_at(1) == '...')
|
85
|
+
Range.new(*args)
|
86
|
+
when /^!ruby\/sym(bol)?:?(.*)?$/
|
87
|
+
o.value.to_sym
|
88
|
+
else
|
89
|
+
@ss.tokenize o.value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
33
93
|
def visit_Psych_Nodes_Mapping_with_class(object)
|
34
94
|
return revive(Psych.load_tags[object.tag], object) if Psych.load_tags[object.tag]
|
35
95
|
|
36
96
|
case object.tag
|
37
|
-
when /^!ruby\/class:?(.*)?$/
|
38
|
-
resolve_class $1
|
39
97
|
when /^!ruby\/ActiveRecord:(.+)$/
|
40
98
|
klass = resolve_class($1)
|
41
99
|
payload = Hash[*object.children.map { |c| accept c }]
|
@@ -72,4 +130,3 @@ module Psych
|
|
72
130
|
end
|
73
131
|
end
|
74
132
|
end
|
75
|
-
|
@@ -9,7 +9,7 @@ if defined?(ActiveRecord)
|
|
9
9
|
klass.with_exclusive_scope { klass.find(val['attributes'][klass.primary_key]) }
|
10
10
|
end
|
11
11
|
rescue ActiveRecord::RecordNotFound
|
12
|
-
raise Delayed::DeserializationError
|
12
|
+
raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass} , primary key: #{val['attributes'][klass.primary_key]} "
|
13
13
|
end
|
14
14
|
|
15
15
|
def to_yaml_properties
|
data/lib/delayed/syck_ext.rb
CHANGED
data/lib/delayed/yaml_ext.rb
CHANGED
data/spec/lifecycle_spec.rb
CHANGED
@@ -64,44 +64,4 @@ describe Delayed::Lifecycle 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
|
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
67
|
end
|
@@ -3,8 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Delayed::MessageSending do
|
4
4
|
describe "handle_asynchronously" do
|
5
5
|
class Story
|
6
|
-
def tell!(arg)
|
7
|
-
end
|
6
|
+
def tell!(arg);end
|
8
7
|
handle_asynchronously :tell!
|
9
8
|
end
|
10
9
|
|
@@ -17,19 +16,16 @@ describe Delayed::MessageSending do
|
|
17
16
|
story = Story.new
|
18
17
|
lambda {
|
19
18
|
job = story.tell!(1)
|
20
|
-
job.payload_object.class.should
|
21
|
-
job.payload_object.method_name.should
|
22
|
-
job.payload_object.args.should
|
19
|
+
job.payload_object.class.should == Delayed::PerformableMethod
|
20
|
+
job.payload_object.method_name.should == :tell_without_delay!
|
21
|
+
job.payload_object.args.should == [1]
|
23
22
|
}.should change { Delayed::Job.count }
|
24
23
|
end
|
25
24
|
|
26
25
|
describe 'with options' do
|
27
26
|
class Fable
|
28
|
-
|
29
|
-
|
30
|
-
end
|
31
|
-
def tell
|
32
|
-
end
|
27
|
+
cattr_accessor :importance
|
28
|
+
def tell;end
|
33
29
|
handle_asynchronously :tell, :priority => Proc.new { self.importance }
|
34
30
|
end
|
35
31
|
|
@@ -43,7 +39,7 @@ describe Delayed::MessageSending do
|
|
43
39
|
job.priority.should == 20
|
44
40
|
end
|
45
41
|
|
46
|
-
describe 'using a proc with
|
42
|
+
describe 'using a proc with parameters' do
|
47
43
|
class Yarn
|
48
44
|
attr_accessor :importance
|
49
45
|
def spin
|
@@ -63,36 +59,37 @@ describe Delayed::MessageSending do
|
|
63
59
|
end
|
64
60
|
|
65
61
|
context "delay" do
|
62
|
+
class FairyTail
|
63
|
+
attr_accessor :happy_ending
|
64
|
+
def self.princesses;end
|
65
|
+
def tell
|
66
|
+
@happy_ending = true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
66
70
|
it "should create a new PerformableMethod job" do
|
67
71
|
lambda {
|
68
72
|
job = "hello".delay.count('l')
|
69
|
-
job.payload_object.class.should
|
70
|
-
job.payload_object.method_name.should
|
71
|
-
job.payload_object.args.should
|
73
|
+
job.payload_object.class.should == Delayed::PerformableMethod
|
74
|
+
job.payload_object.method_name.should == :count
|
75
|
+
job.payload_object.args.should == ['l']
|
72
76
|
}.should change { Delayed::Job.count }.by(1)
|
73
77
|
end
|
74
78
|
|
75
79
|
it "should set default priority" do
|
76
80
|
Delayed::Worker.default_priority = 99
|
77
|
-
job =
|
81
|
+
job = FairyTail.delay.to_s
|
78
82
|
job.priority.should == 99
|
79
83
|
Delayed::Worker.default_priority = 0
|
80
84
|
end
|
81
85
|
|
82
86
|
it "should set job options" do
|
83
87
|
run_at = Time.parse('2010-05-03 12:55 AM')
|
84
|
-
job =
|
88
|
+
job = FairyTail.delay(:priority => 20, :run_at => run_at).to_s
|
85
89
|
job.run_at.should == run_at
|
86
90
|
job.priority.should == 20
|
87
91
|
end
|
88
92
|
|
89
|
-
class FairyTail
|
90
|
-
attr_accessor :happy_ending
|
91
|
-
def tell
|
92
|
-
@happy_ending = true
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
93
|
it "should not delay the job when delay_jobs is false" do
|
97
94
|
Delayed::Worker.delay_jobs = false
|
98
95
|
fairy_tail = FairyTail.new
|
@@ -12,9 +12,9 @@ describe ActionMailer::Base do
|
|
12
12
|
it "should enqueue a PerformableEmail job" do
|
13
13
|
lambda {
|
14
14
|
job = MyMailer.delay.signup('john@example.com')
|
15
|
-
job.payload_object.class.should
|
16
|
-
job.payload_object.method_name.should
|
17
|
-
job.payload_object.args.should
|
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
18
|
}.should change { Delayed::Job.count }.by(1)
|
19
19
|
end
|
20
20
|
end
|
@@ -29,16 +29,14 @@ describe ActionMailer::Base do
|
|
29
29
|
|
30
30
|
describe Delayed::PerformableMailer do
|
31
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
32
|
it "should call the method and #deliver on the mailer" do
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
+
|
37
|
+
mailer_class.should_receive(:signup).with('john@example.com')
|
38
|
+
email.should_receive(:deliver)
|
39
|
+
mailer.perform
|
42
40
|
end
|
43
41
|
end
|
44
42
|
end
|
data/spec/yaml_ext_spec.rb
CHANGED
@@ -3,14 +3,14 @@ require 'spec_helper'
|
|
3
3
|
describe "YAML" do
|
4
4
|
it "should autoload classes" do
|
5
5
|
lambda {
|
6
|
-
yaml = "--- !ruby/class
|
6
|
+
yaml = "--- !ruby/class Autoloaded::Clazz\n"
|
7
7
|
YAML.load(yaml).should == Autoloaded::Clazz
|
8
8
|
}.should_not raise_error
|
9
9
|
end
|
10
10
|
|
11
11
|
it "should autoload the class of a struct" do
|
12
12
|
lambda {
|
13
|
-
yaml = "--- !ruby/class
|
13
|
+
yaml = "--- !ruby/class Autoloaded::Struct\n"
|
14
14
|
YAML.load(yaml).should == Autoloaded::Struct
|
15
15
|
}.should_not raise_error
|
16
16
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delayed_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 3
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 3.0.
|
9
|
+
- 1
|
10
|
+
version: 3.0.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Matt Griffin
|
@@ -20,7 +20,7 @@ autorequire:
|
|
20
20
|
bindir: bin
|
21
21
|
cert_chain: []
|
22
22
|
|
23
|
-
date:
|
23
|
+
date: 2012-01-24 00:00:00 Z
|
24
24
|
dependencies:
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
version_requirements: &id001 !ruby/object:Gem::Requirement
|