opsb-delayed_job 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +2 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.textile +213 -0
  4. data/Rakefile +46 -0
  5. data/VERSION +1 -0
  6. data/benchmarks.rb +33 -0
  7. data/contrib/delayed_job.monitrc +14 -0
  8. data/contrib/delayed_job_multiple.monitrc +23 -0
  9. data/delayed_job.gemspec +115 -0
  10. data/generators/delayed_job/delayed_job_generator.rb +22 -0
  11. data/generators/delayed_job/templates/migration.rb +21 -0
  12. data/generators/delayed_job/templates/script +5 -0
  13. data/init.rb +1 -0
  14. data/lib/delayed/backend/active_record.rb +90 -0
  15. data/lib/delayed/backend/base.rb +111 -0
  16. data/lib/delayed/backend/data_mapper.rb +125 -0
  17. data/lib/delayed/backend/mongo_mapper.rb +110 -0
  18. data/lib/delayed/command.rb +101 -0
  19. data/lib/delayed/message_sending.rb +22 -0
  20. data/lib/delayed/performable_method.rb +62 -0
  21. data/lib/delayed/railtie.rb +10 -0
  22. data/lib/delayed/recipes.rb +31 -0
  23. data/lib/delayed/tasks.rb +15 -0
  24. data/lib/delayed/worker.rb +183 -0
  25. data/lib/delayed_job.rb +14 -0
  26. data/rails/init.rb +5 -0
  27. data/recipes/delayed_job.rb +1 -0
  28. data/spec/backend/active_record_job_spec.rb +46 -0
  29. data/spec/backend/data_mapper_job_spec.rb +16 -0
  30. data/spec/backend/mongo_mapper_job_spec.rb +94 -0
  31. data/spec/backend/shared_backend_spec.rb +265 -0
  32. data/spec/delayed_method_spec.rb +59 -0
  33. data/spec/performable_method_spec.rb +42 -0
  34. data/spec/sample_jobs.rb +25 -0
  35. data/spec/setup/active_record.rb +33 -0
  36. data/spec/setup/data_mapper.rb +8 -0
  37. data/spec/setup/mongo_mapper.rb +17 -0
  38. data/spec/spec_helper.rb +26 -0
  39. data/spec/story_spec.rb +17 -0
  40. data/spec/worker_spec.rb +216 -0
  41. data/tasks/jobs.rake +1 -0
  42. metadata +256 -0
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ class StoryReader
4
+ def read(story)
5
+ "Epilog: #{story.tell}"
6
+ end
7
+ end
8
+
9
+ describe Delayed::PerformableMethod do
10
+
11
+ it "should ignore ActiveRecord::RecordNotFound errors because they are permanent" do
12
+ story = Story.create :text => 'Once upon...'
13
+ p = Delayed::PerformableMethod.new(story, :tell, [])
14
+ story.destroy
15
+ lambda { p.perform }.should_not raise_error
16
+ end
17
+
18
+ it "should store the object as string if its an active record" do
19
+ story = Story.create :text => 'Once upon...'
20
+ p = Delayed::PerformableMethod.new(story, :tell, [])
21
+ p.class.should == Delayed::PerformableMethod
22
+ p.object.should == "LOAD;Story;#{story.id}"
23
+ p.method.should == :tell
24
+ p.args.should == []
25
+ p.perform.should == 'Once upon...'
26
+ end
27
+
28
+ it "should allow class methods to be called on ActiveRecord models" do
29
+ p = Delayed::PerformableMethod.new(Story, :count, [])
30
+ lambda { p.send(:load, p.object) }.should_not raise_error
31
+ end
32
+
33
+ it "should store arguments as string if they are active record objects" do
34
+ story = Story.create :text => 'Once upon...'
35
+ reader = StoryReader.new
36
+ p = Delayed::PerformableMethod.new(reader, :read, [story])
37
+ p.class.should == Delayed::PerformableMethod
38
+ p.method.should == :read
39
+ p.args.should == ["LOAD;Story;#{story.id}"]
40
+ p.perform.should == 'Epilog: Once upon...'
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ class SimpleJob
2
+ cattr_accessor :runs; self.runs = 0
3
+ def perform; @@runs += 1; end
4
+ end
5
+
6
+ class ErrorJob
7
+ cattr_accessor :runs; self.runs = 0
8
+ def perform; raise 'did not work'; end
9
+ end
10
+
11
+ class LongRunningJob
12
+ def perform; sleep 250; end
13
+ end
14
+
15
+ class OnPermanentFailureJob < SimpleJob
16
+ def on_permanent_failure
17
+ end
18
+ end
19
+
20
+ module M
21
+ class ModuleJob
22
+ cattr_accessor :runs; self.runs = 0
23
+ def perform; @@runs += 1; end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ require 'active_record'
2
+
3
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
4
+ ActiveRecord::Base.logger = Delayed::Worker.logger
5
+ ActiveRecord::Migration.verbose = false
6
+
7
+ ActiveRecord::Schema.define do
8
+ create_table :delayed_jobs, :force => true do |table|
9
+ table.integer :priority, :default => 0
10
+ table.integer :attempts, :default => 0
11
+ table.text :handler
12
+ table.text :last_error
13
+ table.datetime :run_at
14
+ table.datetime :locked_at
15
+ table.datetime :failed_at
16
+ table.string :locked_by
17
+ table.timestamps
18
+ end
19
+
20
+ add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
21
+
22
+ create_table :stories, :force => true do |table|
23
+ table.string :text
24
+ end
25
+ end
26
+
27
+ # Purely useful for test cases...
28
+ class Story < ActiveRecord::Base
29
+ def tell; text; end
30
+ def whatever(n, _); tell*n; end
31
+
32
+ handle_asynchronously :whatever
33
+ end
@@ -0,0 +1,8 @@
1
+ require 'dm-core'
2
+ require 'dm-validations'
3
+
4
+ require 'delayed/backend/data_mapper'
5
+
6
+ DataMapper.logger = Delayed::Worker.logger
7
+ DataMapper.setup(:default, "sqlite3::memory:")
8
+ DataMapper.auto_migrate!
@@ -0,0 +1,17 @@
1
+ require 'mongo_mapper'
2
+
3
+ MongoMapper.config = {
4
+ RAILS_ENV => {'database' => 'delayed_job'}
5
+ }
6
+ MongoMapper.connect RAILS_ENV
7
+
8
+ unless defined?(Story)
9
+ class Story
10
+ include ::MongoMapper::Document
11
+ def tell; text; end
12
+ def whatever(n, _); tell*n; end
13
+ def self.count; end
14
+
15
+ handle_asynchronously :whatever
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rubygems'
4
+ require 'spec'
5
+ require 'logger'
6
+
7
+ require 'delayed_job'
8
+ require 'sample_jobs'
9
+
10
+ Delayed::Worker.logger = Logger.new('/tmp/dj.log')
11
+ RAILS_ENV = 'test'
12
+
13
+ # determine the available backends
14
+ BACKENDS = []
15
+ Dir.glob("#{File.dirname(__FILE__)}/setup/*.rb") do |backend|
16
+ begin
17
+ backend = File.basename(backend, '.rb')
18
+ require "setup/#{backend}"
19
+ require "backend/#{backend}_job_spec"
20
+ BACKENDS << backend.to_sym
21
+ rescue LoadError
22
+ puts "Unable to load #{backend} backend! #{$!}"
23
+ end
24
+ end
25
+
26
+ Delayed::Worker.backend = BACKENDS.first
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe "A story" do
4
+
5
+ before(:all) do
6
+ @story = Story.create :text => "Once upon a time..."
7
+ end
8
+
9
+ it "should be shared" do
10
+ @story.tell.should == 'Once upon a time...'
11
+ end
12
+
13
+ it "should not return its result if it storytelling is delayed" do
14
+ @story.send_later(:tell).should_not == 'Once upon a time...'
15
+ end
16
+
17
+ end
@@ -0,0 +1,216 @@
1
+ require 'spec_helper'
2
+
3
+ describe Delayed::Worker do
4
+ def job_create(opts = {})
5
+ Delayed::Job.create(opts.merge(:payload_object => SimpleJob.new))
6
+ end
7
+
8
+ describe "backend=" do
9
+ it "should set the Delayed::Job constant to the backend" do
10
+ @clazz = Class.new
11
+ Delayed::Worker.backend = @clazz
12
+ Delayed::Job.should == @clazz
13
+ end
14
+
15
+ it "should set backend with a symbol" do
16
+ Delayed::Worker.backend = Class.new
17
+ Delayed::Worker.backend = :active_record
18
+ Delayed::Worker.backend.should == Delayed::Backend::ActiveRecord::Job
19
+ end
20
+ end
21
+
22
+ BACKENDS.each do |backend|
23
+ describe "with the #{backend} backend" do
24
+ before do
25
+ Delayed::Worker.backend = backend
26
+ Delayed::Job.delete_all
27
+
28
+ @worker = Delayed::Worker.new(:max_priority => nil, :min_priority => nil, :quiet => true)
29
+
30
+ SimpleJob.runs = 0
31
+ end
32
+
33
+ describe "running a job" do
34
+ it "should fail after Worker.max_run_time" do
35
+ begin
36
+ old_max_run_time = Delayed::Worker.max_run_time
37
+ Delayed::Worker.max_run_time = 1.second
38
+ @job = Delayed::Job.create :payload_object => LongRunningJob.new
39
+ @worker.run(@job)
40
+ @job.reload.last_error.should =~ /expired/
41
+ @job.attempts.should == 1
42
+ ensure
43
+ Delayed::Worker.max_run_time = old_max_run_time
44
+ end
45
+ end
46
+ end
47
+
48
+ context "worker prioritization" do
49
+ before(:each) do
50
+ @worker = Delayed::Worker.new(:max_priority => 5, :min_priority => -5, :quiet => true)
51
+ end
52
+
53
+ it "should only work_off jobs that are >= min_priority" do
54
+ SimpleJob.runs.should == 0
55
+
56
+ job_create(:priority => -10)
57
+ job_create(:priority => 0)
58
+ @worker.work_off
59
+
60
+ SimpleJob.runs.should == 1
61
+ end
62
+
63
+ it "should only work_off jobs that are <= max_priority" do
64
+ SimpleJob.runs.should == 0
65
+
66
+ job_create(:priority => 10)
67
+ job_create(:priority => 0)
68
+
69
+ @worker.work_off
70
+
71
+ SimpleJob.runs.should == 1
72
+ end
73
+ end
74
+
75
+ context "while running with locked and expired jobs" do
76
+ before(:each) do
77
+ @worker.name = 'worker1'
78
+ end
79
+
80
+ it "should not run jobs locked by another worker" do
81
+ job_create(:locked_by => 'other_worker', :locked_at => (Delayed::Job.db_time_now - 1.minutes))
82
+ lambda { @worker.work_off }.should_not change { SimpleJob.runs }
83
+ end
84
+
85
+ it "should run open jobs" do
86
+ job_create
87
+ lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1)
88
+ end
89
+
90
+ it "should run expired jobs" do
91
+ expired_time = Delayed::Job.db_time_now - (1.minutes + Delayed::Worker.max_run_time)
92
+ job_create(:locked_by => 'other_worker', :locked_at => expired_time)
93
+ lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1)
94
+ end
95
+
96
+ it "should run own jobs" do
97
+ job_create(:locked_by => @worker.name, :locked_at => (Delayed::Job.db_time_now - 1.minutes))
98
+ lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1)
99
+ end
100
+ end
101
+
102
+ describe "failed jobs" do
103
+ before do
104
+ # reset defaults
105
+ Delayed::Worker.destroy_failed_jobs = true
106
+ Delayed::Worker.max_attempts = 25
107
+
108
+ @job = Delayed::Job.enqueue ErrorJob.new
109
+ end
110
+
111
+ it "should record last_error when destroy_failed_jobs = false, max_attempts = 1" do
112
+ Delayed::Worker.destroy_failed_jobs = false
113
+ Delayed::Worker.max_attempts = 1
114
+ @worker.run(@job)
115
+ @job.reload
116
+ @job.last_error.should =~ /did not work/
117
+ @job.last_error.should =~ /worker_spec.rb/
118
+ @job.attempts.should == 1
119
+ @job.failed_at.should_not be_nil
120
+ end
121
+
122
+ it "should re-schedule jobs after failing" do
123
+ @worker.run(@job)
124
+ @job.reload
125
+ @job.last_error.should =~ /did not work/
126
+ @job.last_error.should =~ /sample_jobs.rb:8:in `perform'/
127
+ @job.attempts.should == 1
128
+ @job.run_at.should > Delayed::Job.db_time_now - 10.minutes
129
+ @job.run_at.should < Delayed::Job.db_time_now + 10.minutes
130
+ end
131
+ end
132
+
133
+ context "reschedule" do
134
+ before do
135
+ @job = Delayed::Job.create :payload_object => SimpleJob.new
136
+ end
137
+
138
+ share_examples_for "any failure more than Worker.max_attempts times" do
139
+ context "when the job's payload has an #on_permanent_failure hook" do
140
+ before do
141
+ @job = Delayed::Job.create :payload_object => OnPermanentFailureJob.new
142
+ @job.payload_object.should respond_to :on_permanent_failure
143
+ end
144
+
145
+ it "should run that hook" do
146
+ @job.payload_object.should_receive :on_permanent_failure
147
+ Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
148
+ end
149
+ end
150
+
151
+ context "when the job's payload has no #on_permanent_failure hook" do
152
+ # It's a little tricky to test this in a straightforward way,
153
+ # because putting a should_not_receive expectation on
154
+ # @job.payload_object.on_permanent_failure makes that object
155
+ # incorrectly return true to
156
+ # payload_object.respond_to? :on_permanent_failure, which is what
157
+ # reschedule uses to decide whether to call on_permanent_failure.
158
+ # So instead, we just make sure that the payload_object as it
159
+ # already stands doesn't respond_to? on_permanent_failure, then
160
+ # shove it through the iterated reschedule loop and make sure we
161
+ # don't get a NoMethodError (caused by calling that nonexistent
162
+ # on_permanent_failure method).
163
+
164
+ before do
165
+ @job.payload_object.should_not respond_to(:on_permanent_failure)
166
+ end
167
+
168
+ it "should not try to run that hook" do
169
+ lambda do
170
+ Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
171
+ end.should_not raise_exception(NoMethodError)
172
+ end
173
+ end
174
+ end
175
+
176
+ context "and we want to destroy jobs" do
177
+ before do
178
+ Delayed::Worker.destroy_failed_jobs = true
179
+ end
180
+
181
+ it_should_behave_like "any failure more than Worker.max_attempts times"
182
+
183
+ it "should be destroyed if it failed more than Worker.max_attempts times" do
184
+ @job.should_receive(:destroy)
185
+ Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
186
+ end
187
+
188
+ it "should not be destroyed if failed fewer than Worker.max_attempts times" do
189
+ @job.should_not_receive(:destroy)
190
+ (Delayed::Worker.max_attempts - 1).times { @worker.reschedule(@job) }
191
+ end
192
+ end
193
+
194
+ context "and we don't want to destroy jobs" do
195
+ before do
196
+ Delayed::Worker.destroy_failed_jobs = false
197
+ end
198
+
199
+ it_should_behave_like "any failure more than Worker.max_attempts times"
200
+
201
+ it "should be failed if it failed more than Worker.max_attempts times" do
202
+ @job.reload.failed_at.should == nil
203
+ Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
204
+ @job.reload.failed_at.should_not == nil
205
+ end
206
+
207
+ it "should not be failed if it failed fewer than Worker.max_attempts times" do
208
+ (Delayed::Worker.max_attempts - 1).times { @worker.reschedule(@job) }
209
+ @job.reload.failed_at.should == nil
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+
216
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'delayed', 'tasks'))
metadata ADDED
@@ -0,0 +1,256 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opsb-delayed_job
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease: false
6
+ segments:
7
+ - 2
8
+ - 0
9
+ - 3
10
+ version: 2.0.3
11
+ platform: ruby
12
+ authors:
13
+ - Brandon Keepers
14
+ - "Tobias L\xC3\xBCtke"
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-04-16 00:00:00 +01:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: daemons
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 3
31
+ segments:
32
+ - 0
33
+ version: "0"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :development
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: sqlite3-ruby
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ type: :development
63
+ version_requirements: *id003
64
+ - !ruby/object:Gem::Dependency
65
+ name: mongo_mapper
66
+ prerelease: false
67
+ requirement: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ type: :development
77
+ version_requirements: *id004
78
+ - !ruby/object:Gem::Dependency
79
+ name: dm-core
80
+ prerelease: false
81
+ requirement: &id005 !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 3
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ type: :development
91
+ version_requirements: *id005
92
+ - !ruby/object:Gem::Dependency
93
+ name: dm-observer
94
+ prerelease: false
95
+ requirement: &id006 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ type: :development
105
+ version_requirements: *id006
106
+ - !ruby/object:Gem::Dependency
107
+ name: dm-aggregates
108
+ prerelease: false
109
+ requirement: &id007 !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ hash: 3
115
+ segments:
116
+ - 0
117
+ version: "0"
118
+ type: :development
119
+ version_requirements: *id007
120
+ - !ruby/object:Gem::Dependency
121
+ name: dm-validations
122
+ prerelease: false
123
+ requirement: &id008 !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ hash: 3
129
+ segments:
130
+ - 0
131
+ version: "0"
132
+ type: :development
133
+ version_requirements: *id008
134
+ - !ruby/object:Gem::Dependency
135
+ name: do_sqlite3
136
+ prerelease: false
137
+ requirement: &id009 !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ hash: 3
143
+ segments:
144
+ - 0
145
+ version: "0"
146
+ type: :development
147
+ version_requirements: *id009
148
+ - !ruby/object:Gem::Dependency
149
+ name: database_cleaner
150
+ prerelease: false
151
+ requirement: &id010 !ruby/object:Gem::Requirement
152
+ none: false
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ hash: 3
157
+ segments:
158
+ - 0
159
+ version: "0"
160
+ type: :development
161
+ version_requirements: *id010
162
+ description: |-
163
+ 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.
164
+
165
+ This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job).
166
+ email: tobi@leetsoft.com
167
+ executables: []
168
+
169
+ extensions: []
170
+
171
+ extra_rdoc_files:
172
+ - README.textile
173
+ files:
174
+ - .gitignore
175
+ - MIT-LICENSE
176
+ - README.textile
177
+ - Rakefile
178
+ - VERSION
179
+ - benchmarks.rb
180
+ - contrib/delayed_job.monitrc
181
+ - contrib/delayed_job_multiple.monitrc
182
+ - delayed_job.gemspec
183
+ - generators/delayed_job/delayed_job_generator.rb
184
+ - generators/delayed_job/templates/migration.rb
185
+ - generators/delayed_job/templates/script
186
+ - init.rb
187
+ - lib/delayed/backend/active_record.rb
188
+ - lib/delayed/backend/base.rb
189
+ - lib/delayed/backend/data_mapper.rb
190
+ - lib/delayed/backend/mongo_mapper.rb
191
+ - lib/delayed/command.rb
192
+ - lib/delayed/message_sending.rb
193
+ - lib/delayed/performable_method.rb
194
+ - lib/delayed/railtie.rb
195
+ - lib/delayed/recipes.rb
196
+ - lib/delayed/tasks.rb
197
+ - lib/delayed/worker.rb
198
+ - lib/delayed_job.rb
199
+ - rails/init.rb
200
+ - recipes/delayed_job.rb
201
+ - spec/backend/active_record_job_spec.rb
202
+ - spec/backend/data_mapper_job_spec.rb
203
+ - spec/backend/mongo_mapper_job_spec.rb
204
+ - spec/backend/shared_backend_spec.rb
205
+ - spec/delayed_method_spec.rb
206
+ - spec/performable_method_spec.rb
207
+ - spec/sample_jobs.rb
208
+ - spec/setup/active_record.rb
209
+ - spec/setup/data_mapper.rb
210
+ - spec/setup/mongo_mapper.rb
211
+ - spec/spec_helper.rb
212
+ - spec/story_spec.rb
213
+ - spec/worker_spec.rb
214
+ - tasks/jobs.rake
215
+ has_rdoc: true
216
+ homepage: http://github.com/collectiveidea/delayed_job
217
+ licenses: []
218
+
219
+ post_install_message:
220
+ rdoc_options:
221
+ - --main
222
+ - README.textile
223
+ - --inline-source
224
+ - --line-numbers
225
+ require_paths:
226
+ - lib
227
+ required_ruby_version: !ruby/object:Gem::Requirement
228
+ none: false
229
+ requirements:
230
+ - - ">="
231
+ - !ruby/object:Gem::Version
232
+ hash: 3
233
+ segments:
234
+ - 0
235
+ version: "0"
236
+ required_rubygems_version: !ruby/object:Gem::Requirement
237
+ none: false
238
+ requirements:
239
+ - - ">="
240
+ - !ruby/object:Gem::Version
241
+ hash: 3
242
+ segments:
243
+ - 0
244
+ version: "0"
245
+ requirements: []
246
+
247
+ rubyforge_project:
248
+ rubygems_version: 1.3.7
249
+ signing_key:
250
+ specification_version: 3
251
+ summary: Database-backed asynchronous priority queue system -- Extracted from Shopify
252
+ test_files:
253
+ - spec/delayed_method_spec.rb
254
+ - spec/performable_method_spec.rb
255
+ - spec/story_spec.rb
256
+ - spec/worker_spec.rb