delayed_job 2.1.4 → 3.0.0.pre
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 +4 -1
- data/lib/delayed/backend/base.rb +32 -14
- data/lib/delayed/backend/shared_spec.rb +98 -17
- data/lib/delayed/command.rb +17 -27
- data/lib/delayed/lifecycle.rb +84 -0
- data/lib/delayed/performable_method.rb +3 -1
- data/lib/delayed/plugin.rb +13 -0
- data/lib/delayed/plugins/clear_locks.rb +15 -0
- data/lib/delayed/psych_ext.rb +65 -0
- data/lib/delayed/railtie.rb +0 -2
- data/lib/delayed/serialization/active_record.rb +5 -1
- data/lib/delayed/syck_ext.rb +34 -0
- data/lib/delayed/tasks.rb +1 -1
- data/lib/delayed/worker.rb +68 -27
- data/lib/delayed/yaml_ext.rb +5 -36
- data/lib/delayed_job.rb +10 -1
- data/lib/generators/delayed_job/delayed_job_generator.rb +1 -24
- data/spec/autoloaded/instance_clazz.rb +6 -0
- data/spec/autoloaded/instance_struct.rb +6 -0
- data/spec/delayed/backend/test.rb +112 -0
- data/spec/delayed/serialization/test.rb +0 -0
- data/spec/lifecycle_spec.rb +107 -0
- data/spec/message_sending_spec.rb +2 -2
- data/spec/performable_method_spec.rb +26 -1
- data/spec/sample_jobs.rb +6 -0
- data/spec/spec_helper.rb +17 -28
- data/spec/test_backend_spec.rb +13 -0
- data/spec/worker_spec.rb +2 -20
- data/spec/yaml_ext_spec.rb +19 -19
- metadata +107 -133
- data/lib/delayed/backend/active_record.rb +0 -82
- data/lib/generators/delayed_job/templates/migration.rb +0 -21
- data/spec/active_record_job_spec.rb +0 -36
- data/spec/database.yml +0 -4
data/README.textile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
h1. Delayed::Job
|
2
2
|
|
3
|
-
|
3
|
+
Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background.
|
4
4
|
|
5
5
|
It is a direct extraction from Shopify where the job table is responsible for a multitude of core tasks. Amongst those tasks are:
|
6
6
|
|
@@ -12,6 +12,9 @@ It is a direct extraction from Shopify where the job table is responsible for a
|
|
12
12
|
* batch imports
|
13
13
|
* spam checks
|
14
14
|
|
15
|
+
"Follow us on Twitter":https://twitter.com/delayedjob to get updates and notices about new releases.
|
16
|
+
|
17
|
+
|
15
18
|
h2. Installation
|
16
19
|
|
17
20
|
delayed_job 2.1 only supports Rails 3.0+. See the "2.0 branch":https://github.com/collectiveidea/delayed_job/tree/v2.0 for Rails 2.
|
data/lib/delayed/backend/base.rb
CHANGED
@@ -23,13 +23,18 @@ module Delayed
|
|
23
23
|
unless options[:payload_object].respond_to?(:perform)
|
24
24
|
raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
if Delayed::Worker.delay_jobs
|
28
|
-
self.
|
29
|
-
|
28
|
+
self.new(options).tap do |job|
|
29
|
+
Delayed::Worker.lifecycle.run_callbacks(:enqueue, job) do
|
30
|
+
job.hook(:enqueue)
|
31
|
+
job.save
|
32
|
+
end
|
30
33
|
end
|
31
34
|
else
|
32
|
-
options[:payload_object].
|
35
|
+
Delayed::Job.new(:payload_object => options[:payload_object]).tap do |job|
|
36
|
+
job.invoke_job
|
37
|
+
end
|
33
38
|
end
|
34
39
|
end
|
35
40
|
|
@@ -83,14 +88,18 @@ module Delayed
|
|
83
88
|
end
|
84
89
|
|
85
90
|
def invoke_job
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
91
|
+
Delayed::Worker.lifecycle.run_callbacks(:invoke_job, self) do
|
92
|
+
begin
|
93
|
+
hook :before
|
94
|
+
payload_object.perform
|
95
|
+
hook :success
|
96
|
+
rescue Exception => e
|
97
|
+
hook :error, e
|
98
|
+
raise e
|
99
|
+
ensure
|
100
|
+
hook :after
|
101
|
+
end
|
102
|
+
end
|
94
103
|
end
|
95
104
|
|
96
105
|
# Unlock this job (note: not saved to DB)
|
@@ -113,16 +122,25 @@ module Delayed
|
|
113
122
|
payload_object.reschedule_at(self.class.db_time_now, attempts) :
|
114
123
|
self.class.db_time_now + (attempts ** 4) + 5
|
115
124
|
end
|
116
|
-
|
125
|
+
|
117
126
|
def max_attempts
|
118
127
|
payload_object.max_attempts if payload_object.respond_to?(:max_attempts)
|
119
128
|
end
|
120
|
-
|
129
|
+
|
130
|
+
def fail!
|
131
|
+
update_attributes(:failed_at => self.class.db_time_now)
|
132
|
+
end
|
133
|
+
|
121
134
|
protected
|
122
135
|
|
123
136
|
def set_default_run_at
|
124
137
|
self.run_at ||= self.class.db_time_now
|
125
138
|
end
|
139
|
+
|
140
|
+
# Call during reload operation to clear out internal state
|
141
|
+
def reset
|
142
|
+
@payload_object = nil
|
143
|
+
end
|
126
144
|
end
|
127
145
|
end
|
128
146
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require File.expand_path('../../../../spec/sample_jobs', __FILE__)
|
2
2
|
|
3
|
+
require 'active_support/core_ext'
|
4
|
+
|
3
5
|
shared_examples_for 'a delayed_job backend' do
|
4
6
|
let(:worker) { Delayed::Worker.new }
|
5
7
|
|
@@ -26,6 +28,13 @@ shared_examples_for 'a delayed_job backend' do
|
|
26
28
|
job.run_at.should be_within(1).of(later)
|
27
29
|
end
|
28
30
|
|
31
|
+
describe "#reload" do
|
32
|
+
it 'should cause the payload to be reloaded' do
|
33
|
+
job = described_class.enqueue :payload_object => SimpleJob.new
|
34
|
+
job.payload_object.object_id.should_not == job.reload.payload_object.object_id
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
29
38
|
describe "enqueue" do
|
30
39
|
context "with a hash" do
|
31
40
|
it "should raise ArgumentError when handler doesn't respond_to :perform" do
|
@@ -47,6 +56,11 @@ shared_examples_for 'a delayed_job backend' do
|
|
47
56
|
job = described_class.enqueue :payload_object => SimpleJob.new, :run_at => later
|
48
57
|
job.run_at.should be_within(1).of(later)
|
49
58
|
end
|
59
|
+
|
60
|
+
it "should be able to set queue" do
|
61
|
+
job = described_class.enqueue :payload_object => SimpleJob.new, :queue => 'tracking'
|
62
|
+
job.queue.should == 'tracking'
|
63
|
+
end
|
50
64
|
end
|
51
65
|
|
52
66
|
context "with multiple arguments" do
|
@@ -58,12 +72,6 @@ shared_examples_for 'a delayed_job backend' do
|
|
58
72
|
described_class.enqueue SimpleJob.new
|
59
73
|
described_class.count.should == 1
|
60
74
|
end
|
61
|
-
|
62
|
-
it "should not increase count after enqueuing items when delay_jobs is false" do
|
63
|
-
Delayed::Worker.delay_jobs = false
|
64
|
-
described_class.enqueue SimpleJob.new
|
65
|
-
described_class.count.should == 0
|
66
|
-
end
|
67
75
|
|
68
76
|
it "should be able to set priority [DEPRECATED]" do
|
69
77
|
silence_warnings do
|
@@ -91,6 +99,27 @@ shared_examples_for 'a delayed_job backend' do
|
|
91
99
|
lambda { job.invoke_job }.should change { M::ModuleJob.runs }.from(0).to(1)
|
92
100
|
end
|
93
101
|
end
|
102
|
+
|
103
|
+
context "with delay_jobs = false" do
|
104
|
+
before(:each) do
|
105
|
+
Delayed::Worker.delay_jobs = false
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should not increase count after enqueuing items" do
|
109
|
+
described_class.enqueue SimpleJob.new
|
110
|
+
described_class.count.should == 0
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should invoke the enqueued job' do
|
114
|
+
job = SimpleJob.new
|
115
|
+
job.should_receive(:perform)
|
116
|
+
described_class.enqueue job
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should return a job, not the result of invocation' do
|
120
|
+
described_class.enqueue(SimpleJob.new).should be_instance_of(described_class)
|
121
|
+
end
|
122
|
+
end
|
94
123
|
end
|
95
124
|
|
96
125
|
describe "callbacks" do
|
@@ -141,7 +170,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
141
170
|
end
|
142
171
|
|
143
172
|
it "should raise a DeserializationError when the YAML.load raises argument error" do
|
144
|
-
job = described_class.
|
173
|
+
job = described_class.new :handler => "--- !ruby/struct:GoingToRaiseArgError {}"
|
145
174
|
YAML.should_receive(:load).and_raise(ArgumentError)
|
146
175
|
lambda { job.payload_object }.should raise_error(Delayed::DeserializationError)
|
147
176
|
end
|
@@ -169,7 +198,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
169
198
|
|
170
199
|
it "should reserve jobs scheduled for the past when time zones are involved" do
|
171
200
|
Time.zone = 'US/Eastern'
|
172
|
-
job = create_job :run_at => described_class.db_time_now - 1.minute
|
201
|
+
job = create_job :run_at => described_class.db_time_now - 1.minute
|
173
202
|
described_class.reserve(worker).should == job
|
174
203
|
end
|
175
204
|
|
@@ -187,7 +216,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
187
216
|
end
|
188
217
|
|
189
218
|
it "should reserve expired jobs" do
|
190
|
-
job = create_job(:locked_by => worker
|
219
|
+
job = create_job(:locked_by => 'some other worker', :locked_at => described_class.db_time_now - Delayed::Worker.max_run_time - 1.minute)
|
191
220
|
described_class.reserve(worker).should == job
|
192
221
|
end
|
193
222
|
|
@@ -215,8 +244,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
215
244
|
it "should parse from handler on deserialization error" do
|
216
245
|
job = Story.create(:text => "...").delay.text
|
217
246
|
job.payload_object.object.destroy
|
218
|
-
job
|
219
|
-
job.name.should == 'Delayed::PerformableMethod'
|
247
|
+
job.reload.name.should == 'Delayed::PerformableMethod'
|
220
248
|
end
|
221
249
|
end
|
222
250
|
|
@@ -289,20 +317,73 @@ shared_examples_for 'a delayed_job backend' do
|
|
289
317
|
@job.id.should_not be_nil
|
290
318
|
end
|
291
319
|
end
|
292
|
-
|
320
|
+
|
321
|
+
context "named queues" do
|
322
|
+
context "when worker has one queue set" do
|
323
|
+
before(:each) do
|
324
|
+
worker.queues = ['large']
|
325
|
+
end
|
326
|
+
|
327
|
+
it "should only work off jobs which are from its queue" do
|
328
|
+
SimpleJob.runs.should == 0
|
329
|
+
|
330
|
+
create_job(:queue => "large")
|
331
|
+
create_job(:queue => "small")
|
332
|
+
worker.work_off
|
333
|
+
|
334
|
+
SimpleJob.runs.should == 1
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
context "when worker has two queue set" do
|
339
|
+
before(:each) do
|
340
|
+
worker.queues = ['large', 'small']
|
341
|
+
end
|
342
|
+
|
343
|
+
it "should only work off jobs which are from its queue" do
|
344
|
+
SimpleJob.runs.should == 0
|
345
|
+
|
346
|
+
create_job(:queue => "large")
|
347
|
+
create_job(:queue => "small")
|
348
|
+
create_job(:queue => "medium")
|
349
|
+
create_job
|
350
|
+
worker.work_off
|
351
|
+
|
352
|
+
SimpleJob.runs.should == 2
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
context "when worker does not have queue set" do
|
357
|
+
before(:each) do
|
358
|
+
worker.queues = []
|
359
|
+
end
|
360
|
+
|
361
|
+
it "should work off all jobs" do
|
362
|
+
SimpleJob.runs.should == 0
|
363
|
+
|
364
|
+
create_job(:queue => "one")
|
365
|
+
create_job(:queue => "two")
|
366
|
+
create_job
|
367
|
+
worker.work_off
|
368
|
+
|
369
|
+
SimpleJob.runs.should == 3
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
293
374
|
context "max_attempts" do
|
294
375
|
before(:each) do
|
295
376
|
@job = described_class.enqueue SimpleJob.new
|
296
377
|
end
|
297
|
-
|
378
|
+
|
298
379
|
it 'should not be defined' do
|
299
380
|
@job.max_attempts.should be_nil
|
300
381
|
end
|
301
|
-
|
382
|
+
|
302
383
|
it 'should use the max_retries value on the payload when defined' do
|
303
384
|
@job.payload_object.stub!(:max_attempts).and_return(99)
|
304
385
|
@job.max_attempts.should == 99
|
305
|
-
end
|
386
|
+
end
|
306
387
|
end
|
307
388
|
|
308
389
|
describe "yaml serialization" do
|
@@ -310,7 +391,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
310
391
|
story = Story.create(:text => 'hello')
|
311
392
|
job = story.delay.tell
|
312
393
|
story.update_attributes :text => 'goodbye'
|
313
|
-
|
394
|
+
job.reload.payload_object.object.text.should == 'goodbye'
|
314
395
|
end
|
315
396
|
|
316
397
|
it "should raise deserialization error for destroyed records" do
|
@@ -318,7 +399,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
318
399
|
job = story.delay.tell
|
319
400
|
story.destroy
|
320
401
|
lambda {
|
321
|
-
|
402
|
+
job.reload.payload_object
|
322
403
|
}.should raise_error(Delayed::DeserializationError)
|
323
404
|
end
|
324
405
|
end
|
data/lib/delayed/command.rb
CHANGED
@@ -5,17 +5,16 @@ require 'optparse'
|
|
5
5
|
module Delayed
|
6
6
|
class Command
|
7
7
|
attr_accessor :worker_count
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(args)
|
10
|
-
@files_to_reopen = []
|
11
10
|
@options = {
|
12
11
|
:quiet => true,
|
13
12
|
:pid_dir => "#{Rails.root}/tmp/pids"
|
14
13
|
}
|
15
|
-
|
14
|
+
|
16
15
|
@worker_count = 1
|
17
16
|
@monitor = false
|
18
|
-
|
17
|
+
|
19
18
|
opts = OptionParser.new do |opts|
|
20
19
|
opts.banner = "Usage: #{File.basename($0)} [options] start|stop|restart|run"
|
21
20
|
|
@@ -50,20 +49,20 @@ module Delayed
|
|
50
49
|
opts.on('-p', '--prefix NAME', "String to be prefixed to worker process names") do |prefix|
|
51
50
|
@options[:prefix] = prefix
|
52
51
|
end
|
52
|
+
opts.on('--queues=queues', "Specify which queue DJ must look up for jobs") do |queues|
|
53
|
+
@options[:queues] = queues.split(',')
|
54
|
+
end
|
55
|
+
opts.on('--queue=queue', "Specify which queue DJ must look up for jobs") do |queue|
|
56
|
+
@options[:queues] = queue.split(',')
|
57
|
+
end
|
53
58
|
end
|
54
59
|
@args = opts.parse!(args)
|
55
60
|
end
|
56
|
-
|
57
|
-
def daemonize
|
58
|
-
Delayed::Worker.backend.before_fork
|
59
61
|
|
60
|
-
|
61
|
-
@files_to_reopen << file unless file.closed?
|
62
|
-
end
|
63
|
-
|
62
|
+
def daemonize
|
64
63
|
dir = @options[:pid_dir]
|
65
64
|
Dir.mkdir(dir) unless File.exists?(dir)
|
66
|
-
|
65
|
+
|
67
66
|
if @worker_count > 1 && @options[:identifier]
|
68
67
|
raise ArgumentError, 'Cannot specify both --number-of-workers and --identifier'
|
69
68
|
elsif @worker_count == 1 && @options[:identifier]
|
@@ -76,29 +75,21 @@ module Delayed
|
|
76
75
|
end
|
77
76
|
end
|
78
77
|
end
|
79
|
-
|
78
|
+
|
80
79
|
def run_process(process_name, dir)
|
80
|
+
Delayed::Worker.before_fork
|
81
81
|
Daemons.run_proc(process_name, :dir => dir, :dir_mode => :normal, :monitor => @monitor, :ARGV => @args) do |*args|
|
82
82
|
$0 = File.join(@options[:prefix], process_name) if @options[:prefix]
|
83
83
|
run process_name
|
84
84
|
end
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
def run(worker_name = nil)
|
88
88
|
Dir.chdir(Rails.root)
|
89
|
-
|
90
|
-
|
91
|
-
@files_to_reopen.each do |file|
|
92
|
-
begin
|
93
|
-
file.reopen file.path, "a+"
|
94
|
-
file.sync = true
|
95
|
-
rescue ::Exception
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
89
|
+
|
90
|
+
Delayed::Worker.after_fork
|
99
91
|
Delayed::Worker.logger = Logger.new(File.join(Rails.root, 'log', 'delayed_job.log'))
|
100
|
-
|
101
|
-
|
92
|
+
|
102
93
|
worker = Delayed::Worker.new(@options)
|
103
94
|
worker.name_prefix = "#{worker_name} "
|
104
95
|
worker.start
|
@@ -107,6 +98,5 @@ module Delayed
|
|
107
98
|
STDERR.puts e.message
|
108
99
|
exit 1
|
109
100
|
end
|
110
|
-
|
111
101
|
end
|
112
102
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Delayed
|
2
|
+
class InvalidCallback < Exception; end
|
3
|
+
|
4
|
+
class Lifecycle
|
5
|
+
EVENTS = {
|
6
|
+
:enqueue => [:job],
|
7
|
+
:execute => [:worker],
|
8
|
+
:loop => [:worker],
|
9
|
+
:perform => [:worker, :job],
|
10
|
+
:error => [:worker, :job],
|
11
|
+
:failure => [:worker, :job],
|
12
|
+
:invoke_job => [:job]
|
13
|
+
}
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@callbacks = EVENTS.keys.inject({}) { |hash, e| hash[e] = Callback.new; hash }
|
17
|
+
end
|
18
|
+
|
19
|
+
def before(event, &block)
|
20
|
+
add(:before, event, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def after(event, &block)
|
24
|
+
add(:after, event, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def around(event, &block)
|
28
|
+
add(:around, event, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def run_callbacks(event, *args, &block)
|
32
|
+
missing_callback(event) unless @callbacks.has_key?(event)
|
33
|
+
|
34
|
+
unless EVENTS[event].size == args.size
|
35
|
+
raise ArgumentError, "Callback #{event} expects #{EVENTS[event].size} parameter(s): #{EVENTS[event].join(', ')}"
|
36
|
+
end
|
37
|
+
|
38
|
+
@callbacks[event].execute(*args, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def add(type, event, &block)
|
44
|
+
missing_callback(event) unless @callbacks.has_key?(event)
|
45
|
+
|
46
|
+
@callbacks[event].add(type, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def missing_callback(event)
|
50
|
+
raise InvalidCallback, "Unknown callback event: #{event}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Callback
|
55
|
+
def initialize
|
56
|
+
@before = []
|
57
|
+
@after = []
|
58
|
+
|
59
|
+
# Identity proc. Avoids special cases when there is no existing around chain.
|
60
|
+
@around = lambda { |*args, &block| block.call(*args) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def execute(*args, &block)
|
64
|
+
@before.each { |c| c.call(*args) }
|
65
|
+
result = @around.call(*args, &block)
|
66
|
+
@after.each { |c| c.call(*args) }
|
67
|
+
result
|
68
|
+
end
|
69
|
+
|
70
|
+
def add(type, &callback)
|
71
|
+
case type
|
72
|
+
when :before
|
73
|
+
@before << callback
|
74
|
+
when :after
|
75
|
+
@after << callback
|
76
|
+
when :around
|
77
|
+
chain = @around # use a local variable so that the current chain is closed over in the following lambda
|
78
|
+
@around = lambda { |*a, &block| chain.call(*a) { |*b| callback.call(*b, &block) } }
|
79
|
+
else
|
80
|
+
raise InvalidCallback, "Invalid callback type: #{type}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'active_support/core_ext/module/delegation'
|
2
2
|
|
3
3
|
module Delayed
|
4
|
-
class PerformableMethod
|
4
|
+
class PerformableMethod
|
5
|
+
attr_accessor :object, :method_name, :args
|
6
|
+
|
5
7
|
delegate :method, :to => :object
|
6
8
|
|
7
9
|
def initialize(object, method_name, args)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Delayed
|
2
|
+
class Plugin
|
3
|
+
class_attribute :callback_block
|
4
|
+
|
5
|
+
def self.callbacks(&block)
|
6
|
+
self.callback_block = block
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
self.class.callback_block.call(Delayed::Worker.lifecycle) if self.class.callback_block
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Delayed
|
2
|
+
module Plugins
|
3
|
+
class ClearLocks < Plugin
|
4
|
+
callbacks do |lifecycle|
|
5
|
+
lifecycle.around(:execute) do |worker, &block|
|
6
|
+
begin
|
7
|
+
block.call(worker)
|
8
|
+
ensure
|
9
|
+
Delayed::Job.clear_locks!(worker.name)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class ActiveRecord::Base
|
2
|
+
# serialize to YAML
|
3
|
+
def encode_with(coder)
|
4
|
+
coder["attributes"] = @attributes
|
5
|
+
coder.tag = ['!ruby/ActiveRecord', self.class.name].join(':')
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Delayed::PerformableMethod
|
10
|
+
# serialize to YAML
|
11
|
+
def encode_with(coder)
|
12
|
+
coder.map = {
|
13
|
+
"object" => object,
|
14
|
+
"method_name" => method_name,
|
15
|
+
"args" => args
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Psych
|
21
|
+
module Visitors
|
22
|
+
class YAMLTree
|
23
|
+
def visit_Class(klass)
|
24
|
+
tag = ['!ruby/class', klass.name].join(':')
|
25
|
+
register(klass, @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK))
|
26
|
+
@emitter.end_mapping
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class ToRuby
|
31
|
+
def visit_Psych_Nodes_Mapping_with_class(object)
|
32
|
+
return revive(Psych.load_tags[object.tag], object) if Psych.load_tags[object.tag]
|
33
|
+
|
34
|
+
case object.tag
|
35
|
+
when /^!ruby\/class:?(.*)?$/
|
36
|
+
resolve_class $1
|
37
|
+
when /^!ruby\/ActiveRecord:(.+)$/
|
38
|
+
klass = resolve_class($1)
|
39
|
+
payload = Hash[*object.children.map { |c| accept c }]
|
40
|
+
id = payload["attributes"][klass.primary_key]
|
41
|
+
begin
|
42
|
+
if ActiveRecord::VERSION::MAJOR == 3
|
43
|
+
klass.unscoped.find(id)
|
44
|
+
else # Rails 2
|
45
|
+
klass.with_exclusive_scope { klass.find(id) }
|
46
|
+
end
|
47
|
+
rescue ActiveRecord::RecordNotFound
|
48
|
+
raise Delayed::DeserializationError
|
49
|
+
end
|
50
|
+
else
|
51
|
+
visit_Psych_Nodes_Mapping_without_class(object)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
alias_method_chain :visit_Psych_Nodes_Mapping, :class
|
55
|
+
|
56
|
+
def resolve_class_with_constantize(klass_name)
|
57
|
+
klass_name.constantize
|
58
|
+
rescue
|
59
|
+
resolve_class_without_constantize(klass_name)
|
60
|
+
end
|
61
|
+
alias_method_chain :resolve_class, :constantize
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/lib/delayed/railtie.rb
CHANGED
@@ -2,7 +2,11 @@ class ActiveRecord::Base
|
|
2
2
|
yaml_as "tag:ruby.yaml.org,2002:ActiveRecord"
|
3
3
|
|
4
4
|
def self.yaml_new(klass, tag, val)
|
5
|
-
|
5
|
+
if ActiveRecord::VERSION::MAJOR == 3
|
6
|
+
klass.unscoped.find(val['attributes'][klass.primary_key])
|
7
|
+
else # Rails 2
|
8
|
+
klass.with_exclusive_scope { klass.find(val['attributes'][klass.primary_key]) }
|
9
|
+
end
|
6
10
|
rescue ActiveRecord::RecordNotFound
|
7
11
|
raise Delayed::DeserializationError
|
8
12
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Module
|
2
|
+
yaml_as "tag:ruby.yaml.org,2002:module"
|
3
|
+
|
4
|
+
def self.yaml_new(klass, tag, val)
|
5
|
+
klass
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_yaml(options = {})
|
9
|
+
YAML.quick_emit(nil, options) do |out|
|
10
|
+
out.scalar(taguri, name, :plain)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def yaml_tag_read_class(name)
|
15
|
+
# Constantize the object so that ActiveSupport can attempt
|
16
|
+
# its auto loading magic. Will raise LoadError if not successful.
|
17
|
+
name.constantize
|
18
|
+
name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Class
|
23
|
+
yaml_as "tag:ruby.yaml.org,2002:class"
|
24
|
+
remove_method :to_yaml if respond_to?(:to_yaml) && method(:to_yaml).owner == Class # use Module's to_yaml
|
25
|
+
end
|
26
|
+
|
27
|
+
class Struct
|
28
|
+
def self.yaml_tag_read_class(name)
|
29
|
+
# Constantize the object so that ActiveSupport can attempt
|
30
|
+
# its auto loading magic. Will raise LoadError if not successful.
|
31
|
+
name.constantize
|
32
|
+
"Struct::#{ name }"
|
33
|
+
end
|
34
|
+
end
|
data/lib/delayed/tasks.rb
CHANGED
@@ -6,6 +6,6 @@ namespace :jobs do
|
|
6
6
|
|
7
7
|
desc "Start a delayed_job worker."
|
8
8
|
task :work => :environment do
|
9
|
-
Delayed::Worker.new(:min_priority => ENV['MIN_PRIORITY'], :max_priority => ENV['MAX_PRIORITY'], :quiet => false).start
|
9
|
+
Delayed::Worker.new(:min_priority => ENV['MIN_PRIORITY'], :max_priority => ENV['MAX_PRIORITY'], :queues => (ENV['QUEUES'] || ENV['QUEUE'] || '').split(','), :quiet => false).start
|
10
10
|
end
|
11
11
|
end
|