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 CHANGED
@@ -1,2 +1,3 @@
1
+ .rvmrc
1
2
  *.gem
2
3
  *.swp
@@ -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.
@@ -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
- # config/initializers/delayed_job.rb
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 @#send_later(method, params)@ on any object and it will be processed in the background.
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.send_later :deliver_signup, @user
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. Changes
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
- * 1.2.0: Added #send_later to Object for simpler job creation
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 = true
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.3
1
+ 2.0.4
@@ -26,7 +26,7 @@ Benchmark.bm(10) do |x|
26
26
  Delayed::Worker.backend = backend
27
27
 
28
28
  n = 10000
29
- n.times { "foo".send_later :length }
29
+ n.times { "foo".delay.length }
30
30
 
31
31
  x.report(backend.to_s) { Delayed::Worker.new(:quiet => true).work_off(n) }
32
32
  end
@@ -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.3"
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-04-16}
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.6}
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::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
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 = now
67
- self.locked_by = worker
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
@@ -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 || 0
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
@@ -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
- Delayed::Job.enqueue Delayed::PerformableMethod.new(self, method.to_sym, args)
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
- Delayed::Job.enqueue(Delayed::PerformableMethod.new(self, method.to_sym, args), 0, time)
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}_with_send_later#{punctuation}", "#{aliased_method}_without_send_later#{punctuation}"
35
+ with_method, without_method = "#{aliased_method}_with_delay#{punctuation}", "#{aliased_method}_without_delay#{punctuation}"
15
36
  define_method(with_method) do |*args|
16
- send_later(without_method, *args)
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, :send_later
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) }
@@ -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
@@ -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 "*** Starting job worker #{name}"
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
- # TODO: warn if runtime > max_run_time ?
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 "* [JOB] PERMANENTLY removing #{job.name} because of #{job.attempts} consecutive failures.", Logger::INFO
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 "* [JOB] Running on_permanent_failure hook"
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 "* [JOB] #{name} failed with #{error.class.name}: #{error.message} - #{job.attempts} failed attempts", Logger::ERROR
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 "* [Worker(#{name})] acquired lock on #{job.name}"
173
+ say "acquired lock on #{job.name}"
173
174
  true
174
175
  else
175
- say "* [Worker(#{name})] failed to acquire exclusive lock for #{job.name}", Logger::WARN
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.send_later(:tell)
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.send_later(:tell)
51
+ job = story.delay.tell
52
52
 
53
53
  job.payload_object.class.should == Delayed::PerformableMethod
54
- job.payload_object.object.should == "LOAD;MongoStory;#{story.id}"
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.send_later(:read, story)
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 == ["LOAD;MongoStory;#{story.id}"]
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.send_later(:create)
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 => "...").send_later(:save)
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
- @job = @backend.enqueue Delayed::PerformableMethod.new(@@text, :length, {})
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
@@ -1,25 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'random ruby objects' do
3
+ describe Object do
4
4
  before { Delayed::Job.delete_all }
5
5
 
6
- it "should respond_to :send_later method" do
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 == :whatever_without_send_later
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 "send_at" do
38
- it "should queue a new job" do
39
- lambda do
40
- "string".send_at(1.hour.from_now, :length)
41
- end.should change { Delayed::Job.count }.by(1)
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 schedule the job in the future" do
45
- time = 1.hour.from_now.utc.to_time
46
- job = "string".send_at(time, :length)
47
- job.run_at.to_i.should == time.to_i
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 store payload as PerformableMethod" do
51
- job = "string".send_at(1.hour.from_now, :count, 'r')
52
- job.payload_object.class.should == Delayed::PerformableMethod
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
@@ -4,6 +4,8 @@ require 'rubygems'
4
4
  require 'spec'
5
5
  require 'logger'
6
6
 
7
+ gem 'rails', '~>2.3.5'
8
+
7
9
  require 'delayed_job'
8
10
  require 'sample_jobs'
9
11
 
@@ -11,7 +11,7 @@ describe "A story" do
11
11
  end
12
12
 
13
13
  it "should not return its result if it storytelling is delayed" do
14
- @story.send_later(:tell).should_not == 'Once upon a time...'
14
+ @story.delay.tell.should_not == 'Once upon a time...'
15
15
  end
16
16
 
17
17
  end
@@ -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.run(@job)
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
- - 3
9
- version: 2.0.3
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-04-16 00:00:00 -04:00
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.6
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