delayed_job 2.0.3 → 2.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/CHANGELOG +50 -0
- data/README.textile +55 -23
- data/Rakefile +6 -7
- data/VERSION +1 -1
- data/benchmarks.rb +1 -1
- data/delayed_job.gemspec +8 -4
- data/lib/delayed/backend/active_record.rb +4 -2
- data/lib/delayed/backend/base.rb +6 -6
- data/lib/delayed/command.rb +8 -2
- data/lib/delayed/message_sending.rb +38 -7
- data/lib/delayed/performable_method.rb +1 -1
- data/lib/delayed/recipes.rb +6 -2
- data/lib/delayed/worker.rb +10 -9
- data/spec/backend/mongo_mapper_job_spec.rb +15 -15
- data/spec/backend/shared_backend_spec.rb +10 -13
- data/spec/delayed_method_spec.rb +22 -35
- data/spec/message_sending_spec.rb +89 -0
- data/spec/performable_method_spec.rb +12 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/story_spec.rb +1 -1
- data/spec/worker_spec.rb +4 -1
- metadata +33 -4
data/.gitignore
CHANGED
data/CHANGELOG
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
2.0.4 - 2010-11-14
|
2
|
+
* Fix issue where dirty tracking prevented job from being properly unlocked
|
3
|
+
* Add delayed_job_args variable for Capistrano recipe to allow configuration of started workers (e.g. "-n 2 --max-priority 10")
|
4
|
+
* Added options to handle_asynchronously
|
5
|
+
* Added Delayed::Worker.default_priority
|
6
|
+
* Allow private methods to be delayed
|
7
|
+
* Fixes for Ruby 1.9
|
8
|
+
* Added -m command line option to start a monitor process
|
9
|
+
* normalize logging in worker
|
10
|
+
* Deprecate #send_later and #send_at in favor of new #delay method
|
11
|
+
* Added @#delay@ to Object that allows you to delay any method and pass options:
|
12
|
+
options = {:priority => 19, :run_at => 5.minutes.from_now}
|
13
|
+
UserMailer.delay(options).deliver_confirmation(@user)
|
14
|
+
|
15
|
+
2.0.3 - 2010-04-16
|
16
|
+
|
17
|
+
* Fix initialization for Rails 2.x
|
18
|
+
|
19
|
+
2.0.2 - 2010-04-08
|
20
|
+
|
21
|
+
* Fixes to Mongo Mapper backend [ "14be7a24":http://github.com/collectiveidea/delayed_job/commit/14be7a24, "dafd5f46":http://github.com/collectiveidea/delayed_job/commit/dafd5f46, "54d40913":http://github.com/collectiveidea/delayed_job/commit/54d40913 ]
|
22
|
+
* DataMapper backend performance improvements [ "93833cce":http://github.com/collectiveidea/delayed_job/commit/93833cce, "e9b1573e":http://github.com/collectiveidea/delayed_job/commit/e9b1573e, "37a16d11":http://github.com/collectiveidea/delayed_job/commit/37a16d11, "803f2bfa":http://github.com/collectiveidea/delayed_job/commit/803f2bfa ]
|
23
|
+
* Fixed Delayed::Command to create tmp/pids directory [ "8ec8ca41":http://github.com/collectiveidea/delayed_job/commit/8ec8ca41 ]
|
24
|
+
* Railtie to perform Rails 3 initialization [ "3e0fc41f":http://github.com/collectiveidea/delayed_job/commit/3e0fc41f ]
|
25
|
+
* Added on_permanent_failure hook [ "d2f14cd6":http://github.com/collectiveidea/delayed_job/commit/d2f14cd6 ]
|
26
|
+
|
27
|
+
2.0.1 - 2010-04-03
|
28
|
+
* Bug fix for using ActiveRecord backend with daemon [martinbtt]
|
29
|
+
|
30
|
+
2.0.0 - 2010-04-03
|
31
|
+
* Multiple backend support (See README for more details)
|
32
|
+
* Added MongoMapper backend [zbelzer, moneypools]
|
33
|
+
* Added DataMapper backend [lpetre]
|
34
|
+
* Reverse priority so the jobs table can be indexed. Lower numbers have higher priority. The default priority is 0, so increase it for jobs that are not important.
|
35
|
+
* Move most of the heavy lifting from Job to Worker (#work_off, #reschedule, #run, #min_priority, #max_priority, #max_run_time, #max_attempts, #worker_name) [albus522]
|
36
|
+
* Remove EvaledJob. Implement your own if you need this functionality.
|
37
|
+
* Only use Time.zone if it is set. Closes #20
|
38
|
+
* Fix for last_error recording when destroy_failed_jobs = false, max_attempts = 1
|
39
|
+
* Implemented worker name_prefix to maintain dynamic nature of pid detection
|
40
|
+
* Some Rails 3 compatibility fixes [fredwu]
|
41
|
+
|
42
|
+
1.8.5 - 2010-03-15
|
43
|
+
|
44
|
+
* Set auto_flushing=true on Rails logger to fix logging in production
|
45
|
+
* Fix error message when trying to send_later on a method that doesn't exist
|
46
|
+
* Don't use rails_env in capistrano if it's not set. closes #22
|
47
|
+
* Delayed job should append to delayed_job.log not overwrite
|
48
|
+
* Version bump to 1.8.5
|
49
|
+
* fixing Time.now to be Time.zone.now if set to honor the app set local TimeZone
|
50
|
+
* Replaced @Worker::SLEEP@, @Job::MAX_ATTEMPTS@, and @Job::MAX_RUN_TIME@ with class methods that can be overridden.
|
data/README.textile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
h1. Delayed::Job
|
2
2
|
|
3
|
-
Delated_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background.
|
3
|
+
Delated_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
|
|
@@ -9,11 +9,13 @@ It is a direct extraction from Shopify where the job table is responsible for a
|
|
9
9
|
* http downloads
|
10
10
|
* updating smart collections
|
11
11
|
* updating solr, our search server, after product changes
|
12
|
-
* batch imports
|
13
|
-
* spam checks
|
12
|
+
* batch imports
|
13
|
+
* spam checks
|
14
14
|
|
15
15
|
h2. Installation
|
16
16
|
|
17
|
+
This version is for Rails 2.x only. For rails 3 support, install delayed_job 2.1.
|
18
|
+
|
17
19
|
To install as a gem, add the following to @config/environment.rb@:
|
18
20
|
|
19
21
|
<pre>
|
@@ -53,8 +55,12 @@ $ rake db:migrate
|
|
53
55
|
|
54
56
|
h3. MongoMapper
|
55
57
|
|
58
|
+
You must use @MongoMapper.setup@ in the initializer:
|
59
|
+
|
56
60
|
<pre>
|
57
|
-
|
61
|
+
config = YAML::load(File.read(Rails.root.join('config/mongo.yml')))
|
62
|
+
MongoMapper.setup(config, Rails.env)
|
63
|
+
|
58
64
|
Delayed::Worker.backend = :mongo_mapper
|
59
65
|
</pre>
|
60
66
|
|
@@ -68,14 +74,14 @@ Delayed::Worker.backend.auto_upgrade!
|
|
68
74
|
|
69
75
|
h2. Queuing Jobs
|
70
76
|
|
71
|
-
Call
|
77
|
+
Call @.delay.method(params)@ on any object and it will be processed in the background.
|
72
78
|
|
73
79
|
<pre>
|
74
80
|
# without delayed_job
|
75
81
|
Notifier.deliver_signup(@user)
|
76
82
|
|
77
83
|
# with delayed_job
|
78
|
-
Notifier.
|
84
|
+
Notifier.delay.deliver_signup @user
|
79
85
|
</pre>
|
80
86
|
|
81
87
|
If a method should always be run in the background, you can call @#handle_asynchronously@ after the method declaration:
|
@@ -92,6 +98,39 @@ device = Device.new
|
|
92
98
|
device.deliver
|
93
99
|
</pre>
|
94
100
|
|
101
|
+
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:
|
102
|
+
|
103
|
+
<pre>
|
104
|
+
class LongTasks
|
105
|
+
def send_mailer
|
106
|
+
# Some other code
|
107
|
+
end
|
108
|
+
handle_asynchronously :send_mailer, :priority => 20
|
109
|
+
|
110
|
+
def in_the_future
|
111
|
+
# Some other code
|
112
|
+
end
|
113
|
+
# 5.minutes.from_now will be evaluated when in_the_future is called
|
114
|
+
handle_asynchronously :in_the_future, :run_at => Proc.new { 5.minutes.from_now }
|
115
|
+
|
116
|
+
def self.when_to_run
|
117
|
+
2.hours.from_now
|
118
|
+
end
|
119
|
+
|
120
|
+
def call_a_class_method
|
121
|
+
# Some other code
|
122
|
+
end
|
123
|
+
handle_asynchronously :call_a_class_method, :run_at => Proc.new { when_to_run }
|
124
|
+
|
125
|
+
attr_reader :how_important
|
126
|
+
|
127
|
+
def call_an_instance_method
|
128
|
+
# Some other code
|
129
|
+
end
|
130
|
+
handle_asynchronously :call_an_instance_method, :priority => Proc.new {|i| i.how_important }
|
131
|
+
end
|
132
|
+
</pre>
|
133
|
+
|
95
134
|
h2. Running Jobs
|
96
135
|
|
97
136
|
@script/delayed_job@ can be used to manage a background process which will start working off jobs. Make sure you've run `script/generate delayed_job`.
|
@@ -107,19 +146,19 @@ $ RAILS_ENV=production script/delayed_job stop
|
|
107
146
|
|
108
147
|
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.
|
109
148
|
|
110
|
-
You can also invoke @rake jobs:work@ which will start working off jobs. You can cancel the rake task with @CTRL-C@.
|
149
|
+
You can also invoke @rake jobs:work@ which will start working off jobs. You can cancel the rake task with @CTRL-C@.
|
111
150
|
|
112
151
|
h2. Custom Jobs
|
113
152
|
|
114
|
-
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.
|
153
|
+
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.
|
115
154
|
|
116
155
|
<pre>
|
117
156
|
class NewsletterJob < Struct.new(:text, :emails)
|
118
157
|
def perform
|
119
158
|
emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
123
162
|
Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.find(:all).collect(&:email))
|
124
163
|
</pre>
|
125
164
|
|
@@ -129,17 +168,17 @@ You can also add an optional on_permanent_failure method which will run if the j
|
|
129
168
|
class ParanoidNewsletterJob < NewsletterJob
|
130
169
|
def perform
|
131
170
|
emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
|
132
|
-
end
|
171
|
+
end
|
133
172
|
|
134
173
|
def on_permanent_failure
|
135
174
|
page_sysadmin_in_the_middle_of_the_night
|
136
175
|
end
|
137
|
-
end
|
176
|
+
end
|
138
177
|
</pre>
|
139
178
|
|
140
179
|
h2. Gory Details
|
141
180
|
|
142
|
-
The library evolves around a delayed_jobs table which looks as follows:
|
181
|
+
The library evolves around a delayed_jobs table which looks as follows:
|
143
182
|
|
144
183
|
<pre>
|
145
184
|
create_table :delayed_jobs, :force => true do |table|
|
@@ -200,14 +239,7 @@ If you want to contribute an enhancement or a fix:
|
|
200
239
|
# Commit the changes without making changes to the Rakefile, VERSION, or any other files that aren't related to your enhancement or fix
|
201
240
|
# Send a pull request.
|
202
241
|
|
203
|
-
h3.
|
204
|
-
|
205
|
-
* 1.7.0: Added failed_at column which can optionally be set after a certain amount of failed job attempts. By default failed job attempts are destroyed after about a month.
|
206
|
-
|
207
|
-
* 1.6.0: Renamed locked_until to locked_at. We now store when we start a given job instead of how long it will be locked by the worker. This allows us to get a reading on how long a job took to execute.
|
208
|
-
|
209
|
-
* 1.5.0: Job runners can now be run in parallel. Two new database columns are needed: locked_until and locked_by. This allows us to use pessimistic locking instead of relying on row level locks. This enables us to run as many worker processes as we need to speed up queue processing.
|
242
|
+
h3. Changelog
|
210
243
|
|
211
|
-
|
244
|
+
See http://wiki.github.com/collectiveidea/delayed_job/changelog for a list of changes.
|
212
245
|
|
213
|
-
* 1.0.0: Initial release
|
data/Rakefile
CHANGED
@@ -13,13 +13,13 @@ Jeweler::Tasks.new do |s|
|
|
13
13
|
s.homepage = "http://github.com/collectiveidea/delayed_job"
|
14
14
|
s.description = "Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background. It is a direct extraction from Shopify where the job table is responsible for a multitude of core tasks.\n\nThis gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job)."
|
15
15
|
s.authors = ["Brandon Keepers", "Tobias Lütke"]
|
16
|
-
|
16
|
+
|
17
17
|
s.has_rdoc = true
|
18
18
|
s.rdoc_options = ["--main", "README.textile", "--inline-source", "--line-numbers"]
|
19
19
|
s.extra_rdoc_files = ["README.textile"]
|
20
|
-
|
20
|
+
|
21
21
|
s.test_files = Dir['spec/*_spec.rb']
|
22
|
-
|
22
|
+
|
23
23
|
s.add_dependency "daemons"
|
24
24
|
s.add_development_dependency "rspec"
|
25
25
|
s.add_development_dependency "sqlite3-ruby"
|
@@ -33,14 +33,13 @@ Jeweler::Tasks.new do |s|
|
|
33
33
|
end
|
34
34
|
|
35
35
|
require 'spec/rake/spectask'
|
36
|
-
|
37
|
-
task :default => :spec
|
38
|
-
|
39
36
|
desc 'Run the specs'
|
40
37
|
Spec::Rake::SpecTask.new(:spec) do |t|
|
41
38
|
t.libs << 'lib'
|
42
39
|
t.pattern = 'spec/*_spec.rb'
|
43
|
-
t.verbose =
|
40
|
+
t.verbose = false
|
44
41
|
end
|
42
|
+
|
45
43
|
task :spec => :check_dependencies
|
44
|
+
task :default => :spec
|
46
45
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.0.
|
1
|
+
2.0.4
|
data/benchmarks.rb
CHANGED
data/delayed_job.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{delayed_job}
|
8
|
-
s.version = "2.0.
|
8
|
+
s.version = "2.0.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Brandon Keepers", "Tobias L\303\274tke"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-11-14}
|
13
13
|
s.description = %q{Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background. It is a direct extraction from Shopify where the job table is responsible for a multitude of core tasks.
|
14
14
|
|
15
15
|
This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job).}
|
@@ -19,6 +19,8 @@ This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job)
|
|
19
19
|
]
|
20
20
|
s.files = [
|
21
21
|
".gitignore",
|
22
|
+
".rvmrc",
|
23
|
+
"CHANGELOG",
|
22
24
|
"MIT-LICENSE",
|
23
25
|
"README.textile",
|
24
26
|
"Rakefile",
|
@@ -50,6 +52,7 @@ This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job)
|
|
50
52
|
"spec/backend/mongo_mapper_job_spec.rb",
|
51
53
|
"spec/backend/shared_backend_spec.rb",
|
52
54
|
"spec/delayed_method_spec.rb",
|
55
|
+
"spec/message_sending_spec.rb",
|
53
56
|
"spec/performable_method_spec.rb",
|
54
57
|
"spec/sample_jobs.rb",
|
55
58
|
"spec/setup/active_record.rb",
|
@@ -63,10 +66,11 @@ This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job)
|
|
63
66
|
s.homepage = %q{http://github.com/collectiveidea/delayed_job}
|
64
67
|
s.rdoc_options = ["--main", "README.textile", "--inline-source", "--line-numbers"]
|
65
68
|
s.require_paths = ["lib"]
|
66
|
-
s.rubygems_version = %q{1.3.
|
69
|
+
s.rubygems_version = %q{1.3.7}
|
67
70
|
s.summary = %q{Database-backed asynchronous priority queue system -- Extracted from Shopify}
|
68
71
|
s.test_files = [
|
69
72
|
"spec/delayed_method_spec.rb",
|
73
|
+
"spec/message_sending_spec.rb",
|
70
74
|
"spec/performable_method_spec.rb",
|
71
75
|
"spec/story_spec.rb",
|
72
76
|
"spec/worker_spec.rb"
|
@@ -76,7 +80,7 @@ This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job)
|
|
76
80
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
77
81
|
s.specification_version = 3
|
78
82
|
|
79
|
-
if Gem::Version.new(Gem::
|
83
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
80
84
|
s.add_runtime_dependency(%q<daemons>, [">= 0"])
|
81
85
|
s.add_development_dependency(%q<rspec>, [">= 0"])
|
82
86
|
s.add_development_dependency(%q<sqlite3-ruby>, [">= 0"])
|
@@ -63,8 +63,10 @@ module Delayed
|
|
63
63
|
self.class.update_all(["locked_at = ?", now], ["id = ? and locked_by = ?", id, worker])
|
64
64
|
end
|
65
65
|
if affected_rows == 1
|
66
|
-
self.locked_at
|
67
|
-
self.locked_by
|
66
|
+
self.locked_at = now
|
67
|
+
self.locked_by = worker
|
68
|
+
self.locked_at_will_change!
|
69
|
+
self.locked_by_will_change!
|
68
70
|
return true
|
69
71
|
else
|
70
72
|
return false
|
data/lib/delayed/backend/base.rb
CHANGED
@@ -16,7 +16,7 @@ module Delayed
|
|
16
16
|
raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
|
17
17
|
end
|
18
18
|
|
19
|
-
priority = args.first ||
|
19
|
+
priority = args.first || Delayed::Worker.default_priority
|
20
20
|
run_at = args[1]
|
21
21
|
self.create(:payload_object => object, :priority => priority.to_i, :run_at => run_at)
|
22
22
|
end
|
@@ -42,10 +42,6 @@ module Delayed
|
|
42
42
|
end
|
43
43
|
alias_method :failed, :failed?
|
44
44
|
|
45
|
-
def payload_object
|
46
|
-
@payload_object ||= deserialize(self['handler'])
|
47
|
-
end
|
48
|
-
|
49
45
|
def name
|
50
46
|
@name ||= begin
|
51
47
|
payload = payload_object
|
@@ -61,6 +57,10 @@ module Delayed
|
|
61
57
|
self['handler'] = object.to_yaml
|
62
58
|
end
|
63
59
|
|
60
|
+
def payload_object
|
61
|
+
@payload_object ||= deserialize(self['handler'])
|
62
|
+
end
|
63
|
+
|
64
64
|
# Moved into its own method so that new_relic can trace it.
|
65
65
|
def invoke_job
|
66
66
|
payload_object.perform
|
@@ -108,4 +108,4 @@ module Delayed
|
|
108
108
|
|
109
109
|
end
|
110
110
|
end
|
111
|
-
end
|
111
|
+
end
|
data/lib/delayed/command.rb
CHANGED
@@ -14,6 +14,7 @@ module Delayed
|
|
14
14
|
}
|
15
15
|
|
16
16
|
@worker_count = 1
|
17
|
+
@monitor = false
|
17
18
|
|
18
19
|
opts = OptionParser.new do |opts|
|
19
20
|
opts.banner = "Usage: #{File.basename($0)} [options] start|stop|restart|run"
|
@@ -40,6 +41,11 @@ module Delayed
|
|
40
41
|
opts.on('-i', '--identifier=n', 'A numeric identifier for the worker.') do |n|
|
41
42
|
@options[:identifier] = n
|
42
43
|
end
|
44
|
+
opts.on('-m', '--monitor', 'Start monitor process.') do
|
45
|
+
@monitor = true
|
46
|
+
end
|
47
|
+
|
48
|
+
|
43
49
|
end
|
44
50
|
@args = opts.parse!(args)
|
45
51
|
end
|
@@ -68,7 +74,7 @@ module Delayed
|
|
68
74
|
end
|
69
75
|
|
70
76
|
def run_process(process_name, dir)
|
71
|
-
Daemons.run_proc(process_name, :dir => dir, :dir_mode => :normal, :ARGV => @args) do |*args|
|
77
|
+
Daemons.run_proc(process_name, :dir => dir, :dir_mode => :normal, :monitor => @monitor, :ARGV => @args) do |*args|
|
72
78
|
run process_name
|
73
79
|
end
|
74
80
|
end
|
@@ -79,7 +85,7 @@ module Delayed
|
|
79
85
|
# Re-open file handles
|
80
86
|
@files_to_reopen.each do |file|
|
81
87
|
begin
|
82
|
-
file.reopen file.path
|
88
|
+
file.reopen file.path, "a+"
|
83
89
|
file.sync = true
|
84
90
|
rescue ::Exception
|
85
91
|
end
|
@@ -1,22 +1,53 @@
|
|
1
1
|
module Delayed
|
2
|
+
class DelayProxy < ActiveSupport::BasicObject
|
3
|
+
def initialize(target, options)
|
4
|
+
@target = target
|
5
|
+
@options = options
|
6
|
+
end
|
7
|
+
|
8
|
+
def method_missing(method, *args)
|
9
|
+
Job.create({
|
10
|
+
:payload_object => PerformableMethod.new(@target, method.to_sym, args),
|
11
|
+
:priority => ::Delayed::Worker.default_priority
|
12
|
+
}.merge(@options))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
2
16
|
module MessageSending
|
17
|
+
def delay(options = {})
|
18
|
+
DelayProxy.new(self, options)
|
19
|
+
end
|
20
|
+
alias __delay__ delay
|
21
|
+
|
3
22
|
def send_later(method, *args)
|
4
|
-
|
23
|
+
warn "[DEPRECATION] `object.send_later(:method)` is deprecated. Use `object.delay.method"
|
24
|
+
__delay__.__send__(method, *args)
|
5
25
|
end
|
6
26
|
|
7
27
|
def send_at(time, method, *args)
|
8
|
-
|
28
|
+
warn "[DEPRECATION] `object.send_at(time, :method)` is deprecated. Use `object.delay(:run_at => time).method"
|
29
|
+
__delay__(:run_at => time).__send__(method, *args)
|
9
30
|
end
|
10
31
|
|
11
32
|
module ClassMethods
|
12
|
-
def handle_asynchronously(method)
|
33
|
+
def handle_asynchronously(method, opts = {})
|
13
34
|
aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1
|
14
|
-
with_method, without_method = "#{aliased_method}
|
35
|
+
with_method, without_method = "#{aliased_method}_with_delay#{punctuation}", "#{aliased_method}_without_delay#{punctuation}"
|
15
36
|
define_method(with_method) do |*args|
|
16
|
-
|
37
|
+
curr_opts = opts.clone
|
38
|
+
curr_opts.each_key do |key|
|
39
|
+
if (val = curr_opts[key]).is_a?(Proc)
|
40
|
+
curr_opts[key] = if val.arity == 1
|
41
|
+
val.call(self)
|
42
|
+
else
|
43
|
+
val.call
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
delay(curr_opts).__send__(without_method, *args)
|
17
48
|
end
|
18
|
-
alias_method_chain method, :
|
49
|
+
alias_method_chain method, :delay
|
19
50
|
end
|
20
51
|
end
|
21
52
|
end
|
22
|
-
end
|
53
|
+
end
|
@@ -16,7 +16,7 @@ module Delayed
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def initialize(object, method, args)
|
19
|
-
raise NoMethodError, "undefined method `#{method}' for #{object.inspect}" unless object.respond_to?(method)
|
19
|
+
raise NoMethodError, "undefined method `#{method}' for #{object.inspect}" unless object.respond_to?(method, true)
|
20
20
|
|
21
21
|
self.object = dump(object)
|
22
22
|
self.args = args.map { |a| dump(a) }
|
data/lib/delayed/recipes.rb
CHANGED
@@ -13,6 +13,10 @@ Capistrano::Configuration.instance.load do
|
|
13
13
|
fetch(:rails_env, false) ? "RAILS_ENV=#{fetch(:rails_env)}" : ''
|
14
14
|
end
|
15
15
|
|
16
|
+
def args
|
17
|
+
fetch(:delayed_job_args, "")
|
18
|
+
end
|
19
|
+
|
16
20
|
desc "Stop the delayed_job process"
|
17
21
|
task :stop, :roles => :app do
|
18
22
|
run "cd #{current_path};#{rails_env} script/delayed_job stop"
|
@@ -20,12 +24,12 @@ Capistrano::Configuration.instance.load do
|
|
20
24
|
|
21
25
|
desc "Start the delayed_job process"
|
22
26
|
task :start, :roles => :app do
|
23
|
-
run "cd #{current_path};#{rails_env} script/delayed_job start"
|
27
|
+
run "cd #{current_path};#{rails_env} script/delayed_job start #{args}"
|
24
28
|
end
|
25
29
|
|
26
30
|
desc "Restart the delayed_job process"
|
27
31
|
task :restart, :roles => :app do
|
28
|
-
run "cd #{current_path};#{rails_env} script/delayed_job restart"
|
32
|
+
run "cd #{current_path};#{rails_env} script/delayed_job restart #{args}"
|
29
33
|
end
|
30
34
|
end
|
31
35
|
end
|
data/lib/delayed/worker.rb
CHANGED
@@ -3,10 +3,11 @@ require 'active_support/core_ext/numeric/time'
|
|
3
3
|
|
4
4
|
module Delayed
|
5
5
|
class Worker
|
6
|
-
cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time, :sleep_delay, :logger
|
6
|
+
cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time, :default_priority, :sleep_delay, :logger
|
7
7
|
self.sleep_delay = 5
|
8
8
|
self.max_attempts = 25
|
9
9
|
self.max_run_time = 4.hours
|
10
|
+
self.default_priority = 0
|
10
11
|
|
11
12
|
# By default failed jobs are destroyed after too many attempts. If you want to keep them around
|
12
13
|
# (perhaps to inspect the reason for the failure), set this to false.
|
@@ -66,7 +67,7 @@ module Delayed
|
|
66
67
|
end
|
67
68
|
|
68
69
|
def start
|
69
|
-
say "
|
70
|
+
say "Starting job worker"
|
70
71
|
|
71
72
|
trap('TERM') { say 'Exiting...'; $exit = true }
|
72
73
|
trap('INT') { say 'Exiting...'; $exit = true }
|
@@ -120,8 +121,7 @@ module Delayed
|
|
120
121
|
Timeout.timeout(self.class.max_run_time.to_i) { job.invoke_job }
|
121
122
|
job.destroy
|
122
123
|
end
|
123
|
-
#
|
124
|
-
say "* [JOB] #{name} completed after %.4f" % runtime
|
124
|
+
say "#{job.name} completed after %.4f" % runtime
|
125
125
|
return true # did work
|
126
126
|
rescue Exception => e
|
127
127
|
handle_failed_job(job, e)
|
@@ -137,10 +137,10 @@ module Delayed
|
|
137
137
|
job.unlock
|
138
138
|
job.save!
|
139
139
|
else
|
140
|
-
say "
|
140
|
+
say "PERMANENTLY removing #{job.name} because of #{job.attempts} consecutive failures.", Logger::INFO
|
141
141
|
|
142
142
|
if job.payload_object.respond_to? :on_permanent_failure
|
143
|
-
say "
|
143
|
+
say "Running on_permanent_failure hook"
|
144
144
|
job.payload_object.on_permanent_failure
|
145
145
|
end
|
146
146
|
|
@@ -149,6 +149,7 @@ module Delayed
|
|
149
149
|
end
|
150
150
|
|
151
151
|
def say(text, level = Logger::INFO)
|
152
|
+
text = "[Worker(#{name})] #{text}"
|
152
153
|
puts text unless @quiet
|
153
154
|
logger.add level, "#{Time.now.strftime('%FT%T%z')}: #{text}" if logger
|
154
155
|
end
|
@@ -157,7 +158,7 @@ module Delayed
|
|
157
158
|
|
158
159
|
def handle_failed_job(job, error)
|
159
160
|
job.last_error = error.message + "\n" + error.backtrace.join("\n")
|
160
|
-
say "
|
161
|
+
say "#{job.name} failed with #{error.class.name}: #{error.message} - #{job.attempts} failed attempts", Logger::ERROR
|
161
162
|
reschedule(job)
|
162
163
|
end
|
163
164
|
|
@@ -169,10 +170,10 @@ module Delayed
|
|
169
170
|
# this leads to a more even distribution of jobs across the worker processes
|
170
171
|
job = Delayed::Job.find_available(name, 5, self.class.max_run_time).detect do |job|
|
171
172
|
if job.lock_exclusively!(self.class.max_run_time, name)
|
172
|
-
say "
|
173
|
+
say "acquired lock on #{job.name}"
|
173
174
|
true
|
174
175
|
else
|
175
|
-
say "
|
176
|
+
say "failed to acquire exclusive lock for #{job.name}", Logger::WARN
|
176
177
|
false
|
177
178
|
end
|
178
179
|
end
|
@@ -6,11 +6,11 @@ describe Delayed::Backend::MongoMapper::Job do
|
|
6
6
|
before(:all) do
|
7
7
|
@backend = Delayed::Backend::MongoMapper::Job
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
before(:each) do
|
11
11
|
MongoMapper.database.collections.each(&:remove)
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
it_should_behave_like 'a backend'
|
15
15
|
|
16
16
|
describe "indexes" do
|
@@ -22,36 +22,36 @@ describe Delayed::Backend::MongoMapper::Job do
|
|
22
22
|
@backend.collection.index_information.detect { |index| index[0] == 'locked_by_1' }.should_not be_nil
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
describe "delayed method" do
|
27
27
|
class MongoStoryReader
|
28
28
|
def read(story)
|
29
29
|
"Epilog: #{story.tell}"
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
class MongoStory
|
34
34
|
include ::MongoMapper::Document
|
35
35
|
key :text, String
|
36
|
-
|
36
|
+
|
37
37
|
def tell
|
38
38
|
text
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
it "should ignore not found errors because they are permanent" do
|
43
43
|
story = MongoStory.create :text => 'Once upon a time...'
|
44
|
-
job = story.
|
44
|
+
job = story.delay.tell
|
45
45
|
story.destroy
|
46
46
|
lambda { job.invoke_job }.should_not raise_error
|
47
47
|
end
|
48
48
|
|
49
49
|
it "should store the object as string" do
|
50
50
|
story = MongoStory.create :text => 'Once upon a time...'
|
51
|
-
job = story.
|
51
|
+
job = story.delay.tell
|
52
52
|
|
53
53
|
job.payload_object.class.should == Delayed::PerformableMethod
|
54
|
-
job.payload_object.object.should ==
|
54
|
+
job.payload_object.object.should == story
|
55
55
|
job.payload_object.method.should == :tell
|
56
56
|
job.payload_object.args.should == []
|
57
57
|
job.payload_object.perform.should == 'Once upon a time...'
|
@@ -59,19 +59,19 @@ describe Delayed::Backend::MongoMapper::Job do
|
|
59
59
|
|
60
60
|
it "should store arguments as string" do
|
61
61
|
story = MongoStory.create :text => 'Once upon a time...'
|
62
|
-
job = MongoStoryReader.new.
|
62
|
+
job = MongoStoryReader.new.delay.read(story)
|
63
63
|
job.payload_object.class.should == Delayed::PerformableMethod
|
64
64
|
job.payload_object.method.should == :read
|
65
|
-
job.payload_object.args.should == [
|
65
|
+
job.payload_object.args.should == [story]
|
66
66
|
job.payload_object.perform.should == 'Epilog: Once upon a time...'
|
67
67
|
end
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
describe "before_fork" do
|
71
71
|
after do
|
72
72
|
MongoMapper.connection.connect_to_master
|
73
73
|
end
|
74
|
-
|
74
|
+
|
75
75
|
it "should disconnect" do
|
76
76
|
lambda do
|
77
77
|
Delayed::Backend::MongoMapper::Job.before_fork
|
@@ -83,12 +83,12 @@ describe Delayed::Backend::MongoMapper::Job do
|
|
83
83
|
before do
|
84
84
|
MongoMapper.connection.close
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
it "should call reconnect" do
|
88
88
|
lambda do
|
89
89
|
Delayed::Backend::MongoMapper::Job.after_fork
|
90
90
|
end.should change { !!MongoMapper.connection.connected? }.from(false).to(true)
|
91
91
|
end
|
92
92
|
end
|
93
|
-
|
93
|
+
|
94
94
|
end
|
@@ -6,6 +6,7 @@ shared_examples_for 'a backend' do
|
|
6
6
|
before do
|
7
7
|
Delayed::Worker.max_priority = nil
|
8
8
|
Delayed::Worker.min_priority = nil
|
9
|
+
Delayed::Worker.default_priority = 99
|
9
10
|
SimpleJob.runs = 0
|
10
11
|
end
|
11
12
|
|
@@ -32,6 +33,11 @@ shared_examples_for 'a backend' do
|
|
32
33
|
@job.priority.should == 5
|
33
34
|
end
|
34
35
|
|
36
|
+
it "should use default priority when it is not set" do
|
37
|
+
@job = @backend.enqueue SimpleJob.new
|
38
|
+
@job.priority.should == 99
|
39
|
+
end
|
40
|
+
|
35
41
|
it "should be able to set run_at when enqueuing items" do
|
36
42
|
later = @backend.db_time_now + 5.minutes
|
37
43
|
@job = @backend.enqueue SimpleJob.new, 5, later
|
@@ -173,12 +179,12 @@ shared_examples_for 'a backend' do
|
|
173
179
|
end
|
174
180
|
|
175
181
|
it "should be the method that will be called if its a performable method object" do
|
176
|
-
@job = Story.
|
182
|
+
@job = Story.delay.create
|
177
183
|
@job.name.should == "Story.create"
|
178
184
|
end
|
179
185
|
|
180
186
|
it "should be the instance method that will be called if its a performable method object" do
|
181
|
-
@job = Story.create(:text => "...").
|
187
|
+
@job = Story.create(:text => "...").delay.save
|
182
188
|
@job.name.should == 'Story#save'
|
183
189
|
end
|
184
190
|
end
|
@@ -244,18 +250,9 @@ shared_examples_for 'a backend' do
|
|
244
250
|
end
|
245
251
|
|
246
252
|
context "large handler" do
|
247
|
-
@@text = %{Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus eu vehicula augue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque odio lectus, volutpat sed dictum rutrum, interdum aliquam neque. Vivamus quis velit nisi, quis dictum purus. Duis magna nisi, faucibus nec molestie vitae, dictum eget odio. Nunc nulla mauris, vestibulum at dapibus nec, dapibus et lectus. Nullam sapien lacus, consectetur eget mattis in, rhoncus sed ipsum. Nullam nec nibh nisl. Integer ut erat in arcu feugiat semper. Nulla gravida sapien quam. Vestibulum pharetra elementum posuere. Fusce mattis justo auctor nibh facilisis vitae consectetur nibh vehicula.
|
248
|
-
|
249
|
-
Ut at pharetra justo. Donec dictum ornare tortor in feugiat. Sed ac purus sem. Aenean dignissim, erat vel bibendum mollis, elit neque mollis mauris, vitae pretium diam enim non leo. Aliquam aliquet, odio id iaculis varius, metus nibh fermentum sapien, a euismod turpis lectus sit amet turpis. Morbi sapien est, scelerisque in placerat in, varius nec mauris. Aliquam erat volutpat. Quisque suscipit tincidunt libero, sed tincidunt libero iaculis et. Vivamus sed faucibus elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus dignissim sem sed tortor semper et lacinia leo viverra. Nulla nec quam at arcu ullamcorper imperdiet vitae in ligula. Quisque placerat vulputate orci sit amet tempor. Duis sed quam nulla. Cras quis mi nibh, at euismod velit. Etiam nec nunc libero, sed condimentum diam.
|
250
|
-
|
251
|
-
Duis nec mauris in est suscipit viverra a in nibh. Suspendisse nec nulla tortor. Etiam et nulla tellus. Nam feugiat adipiscing commodo. Curabitur scelerisque varius lacus non hendrerit. Vivamus nec enim non turpis auctor tempus sit amet in nisi. Sed ligula nulla, condimentum sed tempor vel, imperdiet id mauris. Quisque mollis ante eu magna tempus porttitor. Integer est libero, consectetur sed tristique a, scelerisque id risus. Donec lacinia justo eget diam fringilla vitae egestas dolor feugiat. Vivamus massa ante, mattis et hendrerit nec, dictum vitae nulla. Pellentesque at nisl et odio suscipit ullamcorper cursus quis enim. Ut nec tellus molestie erat dignissim mollis. Curabitur quis ipsum sapien, sed tincidunt massa. Vestibulum volutpat pretium fringilla.
|
252
|
-
|
253
|
-
Integer at lorem sit amet nibh suscipit euismod et ut ante. Maecenas feugiat hendrerit dolor, eget egestas velit consequat eget. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse ut nunc odio. Vivamus semper, sem vitae sollicitudin auctor, leo mi vulputate augue, eget venenatis libero nunc ut dolor. Phasellus vulputate, metus et dapibus tempus, tellus arcu ullamcorper leo, porttitor dictum lectus turpis blandit sapien. Pellentesque et accumsan justo. Maecenas elit nisi, tincidunt eget consequat a, laoreet et magna. Pellentesque venenatis felis ut massa ultrices bibendum. Duis vulputate tempor leo at bibendum. Curabitur aliquet, turpis sit amet porta porttitor, nibh mi vehicula dolor, suscipit aliquet mi augue quis magna. Praesent tellus turpis, malesuada at ultricies id, feugiat a urna. Curabitur sed mi magna.
|
254
|
-
|
255
|
-
Quisque adipiscing dignissim mollis. Aenean blandit, diam porttitor bibendum bibendum, leo neque tempus risus, in rutrum dolor elit a lorem. Aenean sollicitudin scelerisque ullamcorper. Nunc tristique ultricies nunc et imperdiet. Duis vitae egestas mauris. Suspendisse odio nisi, accumsan vel volutpat nec, aliquam vitae odio. Praesent elementum fermentum suscipit. Quisque quis tellus eu tellus bibendum luctus a quis nunc. Praesent dictum velit sed lacus dapibus ut ultricies mauris facilisis. Vivamus bibendum, ipsum sit amet facilisis consequat, leo lectus aliquam augue, eu consectetur magna nunc gravida sapien. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis tempor nisl ac odio molestie ut tincidunt purus varius. Nunc quis lorem nibh, vestibulum cursus lorem. Nunc sit amet est ut magna suscipit tempor vitae a augue.}
|
256
|
-
|
257
253
|
before do
|
258
|
-
|
254
|
+
text = "Lorem ipsum dolor sit amet. " * 1000
|
255
|
+
@job = @backend.enqueue Delayed::PerformableMethod.new(text, :length, {})
|
259
256
|
end
|
260
257
|
|
261
258
|
it "should have an id" do
|
data/spec/delayed_method_spec.rb
CHANGED
@@ -1,25 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe Object do
|
4
4
|
before { Delayed::Job.delete_all }
|
5
5
|
|
6
|
-
it "should
|
7
|
-
Object.new.respond_to?(:send_later)
|
8
|
-
end
|
9
|
-
|
10
|
-
it "should raise a ArgumentError if send_later is called but the target method doesn't exist" do
|
11
|
-
lambda { Object.new.send_later(:method_that_deos_not_exist) }.should raise_error(NoMethodError)
|
12
|
-
end
|
13
|
-
|
14
|
-
it "should add a new entry to the job table when send_later is called on it" do
|
15
|
-
lambda { Object.new.send_later(:to_s) }.should change { Delayed::Job.count }.by(1)
|
16
|
-
end
|
17
|
-
|
18
|
-
it "should add a new entry to the job table when send_later is called on the class" do
|
19
|
-
lambda { Object.send_later(:to_s) }.should change { Delayed::Job.count }.by(1)
|
20
|
-
end
|
21
|
-
|
22
|
-
it "should call send later on methods which are wrapped with handle_asynchronously" do
|
6
|
+
it "should call #delay on methods which are wrapped with handle_asynchronously" do
|
23
7
|
story = Story.create :text => 'Once upon...'
|
24
8
|
|
25
9
|
Delayed::Job.count.should == 0
|
@@ -29,31 +13,34 @@ describe 'random ruby objects' do
|
|
29
13
|
Delayed::Job.count.should == 1
|
30
14
|
job = Delayed::Job.first
|
31
15
|
job.payload_object.class.should == Delayed::PerformableMethod
|
32
|
-
job.payload_object.method.should == :
|
16
|
+
job.payload_object.method.should == :whatever_without_delay
|
33
17
|
job.payload_object.args.should == [1, 5]
|
34
18
|
job.payload_object.perform.should == 'Once upon...'
|
35
19
|
end
|
36
20
|
|
37
|
-
context "
|
38
|
-
it "should
|
39
|
-
lambda
|
40
|
-
|
41
|
-
|
21
|
+
context "delay" do
|
22
|
+
it "should raise a ArgumentError if target method doesn't exist" do
|
23
|
+
lambda { Object.new.delay.method_that_does_not_exist }.should raise_error(NoMethodError)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should add a new entry to the job table when delay is called on it" do
|
27
|
+
lambda { Object.new.delay.to_s }.should change { Delayed::Job.count }.by(1)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should add a new entry to the job table when delay is called on the class" do
|
31
|
+
lambda { Object.delay.to_s }.should change { Delayed::Job.count }.by(1)
|
42
32
|
end
|
43
33
|
|
44
|
-
it "should
|
45
|
-
|
46
|
-
job =
|
47
|
-
job.run_at.
|
34
|
+
it "should set job options" do
|
35
|
+
run_at = 1.day.from_now
|
36
|
+
job = Object.delay(:priority => 20, :run_at => run_at).to_s
|
37
|
+
job.run_at.should == run_at
|
38
|
+
job.priority.should == 20
|
48
39
|
end
|
49
40
|
|
50
|
-
it "should
|
51
|
-
job =
|
52
|
-
job.payload_object.
|
53
|
-
job.payload_object.method.should == :count
|
54
|
-
job.payload_object.args.should == ['r']
|
55
|
-
job.payload_object.perform.should == 1
|
41
|
+
it "should save args for original method" do
|
42
|
+
job = 3.delay.+(5)
|
43
|
+
job.payload_object.args.should == [5]
|
56
44
|
end
|
57
45
|
end
|
58
|
-
|
59
46
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Delayed::MessageSending do
|
4
|
+
describe "handle_asynchronously" do
|
5
|
+
class Story < ActiveRecord::Base
|
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.create!
|
18
|
+
lambda {
|
19
|
+
job = story.tell!(1)
|
20
|
+
job.payload_object.class.should == Delayed::PerformableMethod
|
21
|
+
job.payload_object.method.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.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
|
+
end
|
89
|
+
end
|
@@ -38,5 +38,16 @@ describe Delayed::PerformableMethod do
|
|
38
38
|
p.method.should == :read
|
39
39
|
p.args.should == ["LOAD;Story;#{story.id}"]
|
40
40
|
p.perform.should == 'Epilog: Once upon...'
|
41
|
-
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should not raise NoMethodError if target method is private" do
|
44
|
+
clazz = Class.new do
|
45
|
+
def private_method
|
46
|
+
end
|
47
|
+
private :private_method
|
48
|
+
end
|
49
|
+
lambda {
|
50
|
+
Delayed::PerformableMethod.new(clazz.new, :private_method, [])
|
51
|
+
}.should_not raise_error(NoMethodError)
|
52
|
+
end
|
42
53
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/story_spec.rb
CHANGED
data/spec/worker_spec.rb
CHANGED
@@ -104,6 +104,7 @@ describe Delayed::Worker do
|
|
104
104
|
# reset defaults
|
105
105
|
Delayed::Worker.destroy_failed_jobs = true
|
106
106
|
Delayed::Worker.max_attempts = 25
|
107
|
+
Delayed::Job.delete_all
|
107
108
|
|
108
109
|
@job = Delayed::Job.enqueue ErrorJob.new
|
109
110
|
end
|
@@ -120,13 +121,15 @@ describe Delayed::Worker do
|
|
120
121
|
end
|
121
122
|
|
122
123
|
it "should re-schedule jobs after failing" do
|
123
|
-
@worker.
|
124
|
+
@worker.work_off
|
124
125
|
@job.reload
|
125
126
|
@job.last_error.should =~ /did not work/
|
126
127
|
@job.last_error.should =~ /sample_jobs.rb:8:in `perform'/
|
127
128
|
@job.attempts.should == 1
|
128
129
|
@job.run_at.should > Delayed::Job.db_time_now - 10.minutes
|
129
130
|
@job.run_at.should < Delayed::Job.db_time_now + 10.minutes
|
131
|
+
@job.locked_at.should be_nil
|
132
|
+
@job.locked_by.should be_nil
|
130
133
|
end
|
131
134
|
end
|
132
135
|
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delayed_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 7
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 2
|
7
8
|
- 0
|
8
|
-
-
|
9
|
-
version: 2.0.
|
9
|
+
- 4
|
10
|
+
version: 2.0.4
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Brandon Keepers
|
@@ -15,16 +16,18 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date: 2010-
|
19
|
+
date: 2010-11-14 00:00:00 -06:00
|
19
20
|
default_executable:
|
20
21
|
dependencies:
|
21
22
|
- !ruby/object:Gem::Dependency
|
22
23
|
name: daemons
|
23
24
|
prerelease: false
|
24
25
|
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
25
27
|
requirements:
|
26
28
|
- - ">="
|
27
29
|
- !ruby/object:Gem::Version
|
30
|
+
hash: 3
|
28
31
|
segments:
|
29
32
|
- 0
|
30
33
|
version: "0"
|
@@ -34,9 +37,11 @@ dependencies:
|
|
34
37
|
name: rspec
|
35
38
|
prerelease: false
|
36
39
|
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
37
41
|
requirements:
|
38
42
|
- - ">="
|
39
43
|
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
40
45
|
segments:
|
41
46
|
- 0
|
42
47
|
version: "0"
|
@@ -46,9 +51,11 @@ dependencies:
|
|
46
51
|
name: sqlite3-ruby
|
47
52
|
prerelease: false
|
48
53
|
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
49
55
|
requirements:
|
50
56
|
- - ">="
|
51
57
|
- !ruby/object:Gem::Version
|
58
|
+
hash: 3
|
52
59
|
segments:
|
53
60
|
- 0
|
54
61
|
version: "0"
|
@@ -58,9 +65,11 @@ dependencies:
|
|
58
65
|
name: mongo_mapper
|
59
66
|
prerelease: false
|
60
67
|
requirement: &id004 !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
61
69
|
requirements:
|
62
70
|
- - ">="
|
63
71
|
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
64
73
|
segments:
|
65
74
|
- 0
|
66
75
|
version: "0"
|
@@ -70,9 +79,11 @@ dependencies:
|
|
70
79
|
name: dm-core
|
71
80
|
prerelease: false
|
72
81
|
requirement: &id005 !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
73
83
|
requirements:
|
74
84
|
- - ">="
|
75
85
|
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
76
87
|
segments:
|
77
88
|
- 0
|
78
89
|
version: "0"
|
@@ -82,9 +93,11 @@ dependencies:
|
|
82
93
|
name: dm-observer
|
83
94
|
prerelease: false
|
84
95
|
requirement: &id006 !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
85
97
|
requirements:
|
86
98
|
- - ">="
|
87
99
|
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
88
101
|
segments:
|
89
102
|
- 0
|
90
103
|
version: "0"
|
@@ -94,9 +107,11 @@ dependencies:
|
|
94
107
|
name: dm-aggregates
|
95
108
|
prerelease: false
|
96
109
|
requirement: &id007 !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
97
111
|
requirements:
|
98
112
|
- - ">="
|
99
113
|
- !ruby/object:Gem::Version
|
114
|
+
hash: 3
|
100
115
|
segments:
|
101
116
|
- 0
|
102
117
|
version: "0"
|
@@ -106,9 +121,11 @@ dependencies:
|
|
106
121
|
name: dm-validations
|
107
122
|
prerelease: false
|
108
123
|
requirement: &id008 !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
109
125
|
requirements:
|
110
126
|
- - ">="
|
111
127
|
- !ruby/object:Gem::Version
|
128
|
+
hash: 3
|
112
129
|
segments:
|
113
130
|
- 0
|
114
131
|
version: "0"
|
@@ -118,9 +135,11 @@ dependencies:
|
|
118
135
|
name: do_sqlite3
|
119
136
|
prerelease: false
|
120
137
|
requirement: &id009 !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
121
139
|
requirements:
|
122
140
|
- - ">="
|
123
141
|
- !ruby/object:Gem::Version
|
142
|
+
hash: 3
|
124
143
|
segments:
|
125
144
|
- 0
|
126
145
|
version: "0"
|
@@ -130,9 +149,11 @@ dependencies:
|
|
130
149
|
name: database_cleaner
|
131
150
|
prerelease: false
|
132
151
|
requirement: &id010 !ruby/object:Gem::Requirement
|
152
|
+
none: false
|
133
153
|
requirements:
|
134
154
|
- - ">="
|
135
155
|
- !ruby/object:Gem::Version
|
156
|
+
hash: 3
|
136
157
|
segments:
|
137
158
|
- 0
|
138
159
|
version: "0"
|
@@ -151,6 +172,8 @@ extra_rdoc_files:
|
|
151
172
|
- README.textile
|
152
173
|
files:
|
153
174
|
- .gitignore
|
175
|
+
- .rvmrc
|
176
|
+
- CHANGELOG
|
154
177
|
- MIT-LICENSE
|
155
178
|
- README.textile
|
156
179
|
- Rakefile
|
@@ -182,6 +205,7 @@ files:
|
|
182
205
|
- spec/backend/mongo_mapper_job_spec.rb
|
183
206
|
- spec/backend/shared_backend_spec.rb
|
184
207
|
- spec/delayed_method_spec.rb
|
208
|
+
- spec/message_sending_spec.rb
|
185
209
|
- spec/performable_method_spec.rb
|
186
210
|
- spec/sample_jobs.rb
|
187
211
|
- spec/setup/active_record.rb
|
@@ -204,28 +228,33 @@ rdoc_options:
|
|
204
228
|
require_paths:
|
205
229
|
- lib
|
206
230
|
required_ruby_version: !ruby/object:Gem::Requirement
|
231
|
+
none: false
|
207
232
|
requirements:
|
208
233
|
- - ">="
|
209
234
|
- !ruby/object:Gem::Version
|
235
|
+
hash: 3
|
210
236
|
segments:
|
211
237
|
- 0
|
212
238
|
version: "0"
|
213
239
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
240
|
+
none: false
|
214
241
|
requirements:
|
215
242
|
- - ">="
|
216
243
|
- !ruby/object:Gem::Version
|
244
|
+
hash: 3
|
217
245
|
segments:
|
218
246
|
- 0
|
219
247
|
version: "0"
|
220
248
|
requirements: []
|
221
249
|
|
222
250
|
rubyforge_project:
|
223
|
-
rubygems_version: 1.3.
|
251
|
+
rubygems_version: 1.3.7
|
224
252
|
signing_key:
|
225
253
|
specification_version: 3
|
226
254
|
summary: Database-backed asynchronous priority queue system -- Extracted from Shopify
|
227
255
|
test_files:
|
228
256
|
- spec/delayed_method_spec.rb
|
257
|
+
- spec/message_sending_spec.rb
|
229
258
|
- spec/performable_method_spec.rb
|
230
259
|
- spec/story_spec.rb
|
231
260
|
- spec/worker_spec.rb
|