delayed_job 4.0.5 → 4.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/Rakefile +15 -0
- data/delayed_job.gemspec +4 -3
- data/spec/autoloaded/clazz.rb +7 -0
- data/spec/autoloaded/instance_clazz.rb +6 -0
- data/spec/autoloaded/instance_struct.rb +6 -0
- data/spec/autoloaded/struct.rb +7 -0
- data/spec/delayed/backend/test.rb +117 -0
- data/spec/delayed/command_spec.rb +57 -0
- data/spec/delayed/serialization/test.rb +0 -0
- data/spec/helper.rb +85 -0
- data/spec/lifecycle_spec.rb +75 -0
- data/spec/message_sending_spec.rb +122 -0
- data/spec/performable_mailer_spec.rb +43 -0
- data/spec/performable_method_spec.rb +111 -0
- data/spec/psych_ext_spec.rb +12 -0
- data/spec/sample_jobs.rb +109 -0
- data/spec/test_backend_spec.rb +13 -0
- data/spec/worker_spec.rb +157 -0
- data/spec/yaml_ext_spec.rb +48 -0
- metadata +37 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 437dd17835ae0e764f1fbd9ce13c55be2b7ddead
|
4
|
+
data.tar.gz: abb136834dd2e39edb5a6cf5c846613734c3b518
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b89e00ad9d939277b9f2da9e94a1b45310c9b7ea3b7e3f7535f434eb8b9ad46a6589ef541e76501e5c2da07cb982fb337453d6c5b642b2e20274daa1c0321452
|
7
|
+
data.tar.gz: 3cb7e586ac2a1a7ea5967d5e35bf3cd325943bf37e28e913e77a787363c0efa46ab891c07c5f157b88a19f8bfda5444839b9b8071e9bc1feb8bcb715773089f9
|
data/CHANGELOG.md
CHANGED
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
desc 'Run the specs'
|
6
|
+
RSpec::Core::RakeTask.new do |r|
|
7
|
+
r.verbose = false
|
8
|
+
end
|
9
|
+
|
10
|
+
task :test => :spec
|
11
|
+
|
12
|
+
require 'rubocop/rake_task'
|
13
|
+
RuboCop::RakeTask.new
|
14
|
+
|
15
|
+
task :default => [:spec, :rubocop]
|
data/delayed_job.gemspec
CHANGED
@@ -3,12 +3,13 @@ Gem::Specification.new do |spec|
|
|
3
3
|
spec.authors = ['Brandon Keepers', 'Brian Ryckbost', 'Chris Gaffney', 'David Genord II', 'Erik Michaels-Ober', 'Matt Griffin', 'Steve Richert', 'Tobias Lütke']
|
4
4
|
spec.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.'
|
5
5
|
spec.email = ['brian@collectiveidea.com']
|
6
|
-
spec.files = %w[CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md delayed_job.gemspec]
|
7
|
-
spec.files += Dir
|
6
|
+
spec.files = %w[CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md Rakefile delayed_job.gemspec]
|
7
|
+
spec.files += Dir.glob('{contrib,lib,recipes,spec}/**/*')
|
8
8
|
spec.homepage = 'http://github.com/collectiveidea/delayed_job'
|
9
9
|
spec.licenses = ['MIT']
|
10
10
|
spec.name = 'delayed_job'
|
11
11
|
spec.require_paths = ['lib']
|
12
12
|
spec.summary = 'Database-backed asynchronous priority queue system -- Extracted from Shopify'
|
13
|
-
spec.
|
13
|
+
spec.test_files = Dir.glob('spec/**/*')
|
14
|
+
spec.version = '4.0.6'
|
14
15
|
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
# An in-memory backend suitable only for testing. Tries to behave as if it were an ORM.
|
4
|
+
module Delayed
|
5
|
+
module Backend
|
6
|
+
module Test
|
7
|
+
class Job
|
8
|
+
attr_accessor :id
|
9
|
+
attr_accessor :priority
|
10
|
+
attr_accessor :attempts
|
11
|
+
attr_accessor :handler
|
12
|
+
attr_accessor :last_error
|
13
|
+
attr_accessor :run_at
|
14
|
+
attr_accessor :locked_at
|
15
|
+
attr_accessor :locked_by
|
16
|
+
attr_accessor :failed_at
|
17
|
+
attr_accessor :queue
|
18
|
+
|
19
|
+
include Delayed::Backend::Base
|
20
|
+
|
21
|
+
cattr_accessor :id
|
22
|
+
self.id = 0
|
23
|
+
|
24
|
+
def initialize(hash = {})
|
25
|
+
self.attempts = 0
|
26
|
+
self.priority = 0
|
27
|
+
self.id = (self.class.id += 1)
|
28
|
+
hash.each { |k, v| send(:"#{k}=", v) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.all
|
32
|
+
@jobs ||= []
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.count
|
36
|
+
all.size
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.delete_all
|
40
|
+
all.clear
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.create(attrs = {})
|
44
|
+
new(attrs).tap do |o|
|
45
|
+
o.save
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.create!(*args)
|
50
|
+
create(*args)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.clear_locks!(worker_name)
|
54
|
+
all.select { |j| j.locked_by == worker_name }.each do |j|
|
55
|
+
j.locked_by = nil
|
56
|
+
j.locked_at = nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Find a few candidate jobs to run (in case some immediately get locked by others).
|
61
|
+
def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time) # rubocop:disable CyclomaticComplexity, PerceivedComplexity
|
62
|
+
jobs = all.select do |j|
|
63
|
+
j.run_at <= db_time_now &&
|
64
|
+
(j.locked_at.nil? || j.locked_at < db_time_now - max_run_time || j.locked_by == worker_name) &&
|
65
|
+
!j.failed?
|
66
|
+
end
|
67
|
+
jobs.select! { |j| j.priority <= Worker.max_priority } if Worker.max_priority
|
68
|
+
jobs.select! { |j| j.priority >= Worker.min_priority } if Worker.min_priority
|
69
|
+
jobs.select! { |j| Worker.queues.include?(j.queue) } if Worker.queues.any?
|
70
|
+
jobs.sort_by! { |j| [j.priority, j.run_at] }[0..limit - 1]
|
71
|
+
end
|
72
|
+
|
73
|
+
# Lock this job for this worker.
|
74
|
+
# Returns true if we have the lock, false otherwise.
|
75
|
+
def lock_exclusively!(_max_run_time, worker)
|
76
|
+
now = self.class.db_time_now
|
77
|
+
if locked_by != worker
|
78
|
+
# We don't own this job so we will update the locked_by name and the locked_at
|
79
|
+
self.locked_at = now
|
80
|
+
self.locked_by = worker
|
81
|
+
end
|
82
|
+
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.db_time_now
|
87
|
+
Time.current
|
88
|
+
end
|
89
|
+
|
90
|
+
def update_attributes(attrs = {})
|
91
|
+
attrs.each { |k, v| send(:"#{k}=", v) }
|
92
|
+
save
|
93
|
+
end
|
94
|
+
|
95
|
+
def destroy
|
96
|
+
self.class.all.delete(self)
|
97
|
+
end
|
98
|
+
|
99
|
+
def save
|
100
|
+
self.run_at ||= Time.current
|
101
|
+
|
102
|
+
self.class.all << self unless self.class.all.include?(self)
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
106
|
+
def save!
|
107
|
+
save
|
108
|
+
end
|
109
|
+
|
110
|
+
def reload
|
111
|
+
reset
|
112
|
+
self
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'delayed/command'
|
3
|
+
|
4
|
+
describe Delayed::Command do
|
5
|
+
describe 'parsing --pool argument' do
|
6
|
+
it 'should parse --pool correctly' do
|
7
|
+
command = Delayed::Command.new(['--pool=*:1', '--pool=test_queue:4', '--pool=mailers,misc:2'])
|
8
|
+
|
9
|
+
expect(command.worker_pools).to eq [
|
10
|
+
[[], 1],
|
11
|
+
[['test_queue'], 4],
|
12
|
+
[%w[mailers misc], 2]
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should allow * or blank to specify any pools' do
|
17
|
+
command = Delayed::Command.new(['--pool=*:4'])
|
18
|
+
expect(command.worker_pools).to eq [
|
19
|
+
[[], 4],
|
20
|
+
]
|
21
|
+
|
22
|
+
command = Delayed::Command.new(['--pool=:4'])
|
23
|
+
expect(command.worker_pools).to eq [
|
24
|
+
[[], 4],
|
25
|
+
]
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should default to one worker if not specified' do
|
29
|
+
command = Delayed::Command.new(['--pool=mailers'])
|
30
|
+
expect(command.worker_pools).to eq [
|
31
|
+
[['mailers'], 1],
|
32
|
+
]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'running worker pools defined by multiple --pool arguments' do
|
37
|
+
it 'should run the correct worker processes' do
|
38
|
+
command = Delayed::Command.new(['--pool=*:1', '--pool=test_queue:4', '--pool=mailers,misc:2'])
|
39
|
+
|
40
|
+
expect(Dir).to receive(:mkdir).with('./tmp/pids').once
|
41
|
+
|
42
|
+
[
|
43
|
+
['delayed_job.0', {:quiet => true, :pid_dir => './tmp/pids', :queues => []}],
|
44
|
+
['delayed_job.1', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
|
45
|
+
['delayed_job.2', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
|
46
|
+
['delayed_job.3', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
|
47
|
+
['delayed_job.4', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
|
48
|
+
['delayed_job.5', {:quiet => true, :pid_dir => './tmp/pids', :queues => %w[mailers misc]}],
|
49
|
+
['delayed_job.6', {:quiet => true, :pid_dir => './tmp/pids', :queues => %w[mailers misc]}]
|
50
|
+
].each do |args|
|
51
|
+
expect(command).to receive(:run_process).with(*args).once
|
52
|
+
end
|
53
|
+
|
54
|
+
command.daemonize
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
File without changes
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'coveralls'
|
3
|
+
|
4
|
+
SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter]
|
5
|
+
|
6
|
+
SimpleCov.start do
|
7
|
+
add_filter '/spec/'
|
8
|
+
# Each version of ruby and version of rails test different things
|
9
|
+
# This should probably just be removed.
|
10
|
+
minimum_coverage(85.0)
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'logger'
|
14
|
+
require 'rspec'
|
15
|
+
|
16
|
+
require 'action_mailer'
|
17
|
+
require 'active_support/dependencies'
|
18
|
+
require 'active_record'
|
19
|
+
|
20
|
+
require 'delayed_job'
|
21
|
+
require 'delayed/backend/shared_spec'
|
22
|
+
|
23
|
+
if ENV['DEBUG_LOGS']
|
24
|
+
Delayed::Worker.logger = Logger.new(STDOUT)
|
25
|
+
else
|
26
|
+
require 'tempfile'
|
27
|
+
|
28
|
+
tf = Tempfile.new('dj.log')
|
29
|
+
Delayed::Worker.logger = Logger.new(tf.path)
|
30
|
+
tf.unlink
|
31
|
+
end
|
32
|
+
ENV['RAILS_ENV'] = 'test'
|
33
|
+
|
34
|
+
# Trigger AR to initialize
|
35
|
+
ActiveRecord::Base # rubocop:disable Void
|
36
|
+
|
37
|
+
module Rails
|
38
|
+
def self.root
|
39
|
+
'.'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Delayed::Worker.backend = :test
|
44
|
+
|
45
|
+
# Add this directory so the ActiveSupport autoloading works
|
46
|
+
ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
|
47
|
+
|
48
|
+
# Add this to simulate Railtie initializer being executed
|
49
|
+
ActionMailer::Base.extend(Delayed::DelayMail)
|
50
|
+
|
51
|
+
# Used to test interactions between DJ and an ORM
|
52
|
+
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
|
53
|
+
ActiveRecord::Base.logger = Delayed::Worker.logger
|
54
|
+
ActiveRecord::Migration.verbose = false
|
55
|
+
|
56
|
+
ActiveRecord::Schema.define do
|
57
|
+
create_table :stories, :primary_key => :story_id, :force => true do |table|
|
58
|
+
table.string :text
|
59
|
+
table.boolean :scoped, :default => true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Story < ActiveRecord::Base
|
64
|
+
self.primary_key = 'story_id'
|
65
|
+
def tell
|
66
|
+
text
|
67
|
+
end
|
68
|
+
|
69
|
+
def whatever(n, _)
|
70
|
+
tell * n
|
71
|
+
end
|
72
|
+
default_scope { where(:scoped => true) }
|
73
|
+
|
74
|
+
handle_asynchronously :whatever
|
75
|
+
end
|
76
|
+
|
77
|
+
RSpec.configure do |config|
|
78
|
+
config.after(:each) do
|
79
|
+
Delayed::Worker.reset
|
80
|
+
end
|
81
|
+
|
82
|
+
config.expect_with :rspec do |c|
|
83
|
+
c.syntax = :expect
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Delayed::Lifecycle do
|
4
|
+
let(:lifecycle) { Delayed::Lifecycle.new }
|
5
|
+
let(:callback) { lambda { |*_args| } }
|
6
|
+
let(:arguments) { [1] }
|
7
|
+
let(:behavior) { double(Object, :before! => nil, :after! => nil, :inside! => nil) }
|
8
|
+
let(:wrapped_block) { proc { behavior.inside! } }
|
9
|
+
|
10
|
+
describe 'before callbacks' do
|
11
|
+
before(:each) do
|
12
|
+
lifecycle.before(:execute, &callback)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'executes before wrapped block' do
|
16
|
+
expect(callback).to receive(:call).with(*arguments).ordered
|
17
|
+
expect(behavior).to receive(:inside!).ordered
|
18
|
+
lifecycle.run_callbacks :execute, *arguments, &wrapped_block
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'after callbacks' do
|
23
|
+
before(:each) do
|
24
|
+
lifecycle.after(:execute, &callback)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'executes after wrapped block' do
|
28
|
+
expect(behavior).to receive(:inside!).ordered
|
29
|
+
expect(callback).to receive(:call).with(*arguments).ordered
|
30
|
+
lifecycle.run_callbacks :execute, *arguments, &wrapped_block
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'around callbacks' do
|
35
|
+
before(:each) do
|
36
|
+
lifecycle.around(:execute) do |*args, &block|
|
37
|
+
behavior.before!
|
38
|
+
block.call(*args)
|
39
|
+
behavior.after!
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'wraps a block' do
|
44
|
+
expect(behavior).to receive(:before!).ordered
|
45
|
+
expect(behavior).to receive(:inside!).ordered
|
46
|
+
expect(behavior).to receive(:after!).ordered
|
47
|
+
lifecycle.run_callbacks :execute, *arguments, &wrapped_block
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'executes multiple callbacks in order' do
|
51
|
+
expect(behavior).to receive(:one).ordered
|
52
|
+
expect(behavior).to receive(:two).ordered
|
53
|
+
expect(behavior).to receive(:three).ordered
|
54
|
+
|
55
|
+
lifecycle.around(:execute) do |*args, &block|
|
56
|
+
behavior.one
|
57
|
+
block.call(*args)
|
58
|
+
end
|
59
|
+
lifecycle.around(:execute) do |*args, &block|
|
60
|
+
behavior.two
|
61
|
+
block.call(*args)
|
62
|
+
end
|
63
|
+
lifecycle.around(:execute) do |*args, &block|
|
64
|
+
behavior.three
|
65
|
+
block.call(*args)
|
66
|
+
end
|
67
|
+
lifecycle.run_callbacks(:execute, *arguments, &wrapped_block)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'raises if callback is executed with wrong number of parameters' do
|
72
|
+
lifecycle.before(:execute, &callback)
|
73
|
+
expect { lifecycle.run_callbacks(:execute, 1, 2, 3) {} }.to raise_error(ArgumentError, /1 parameter/)
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Delayed::MessageSending do
|
4
|
+
describe 'handle_asynchronously' do
|
5
|
+
class Story
|
6
|
+
def tell!(_arg); end
|
7
|
+
handle_asynchronously :tell!
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'aliases original method' do
|
11
|
+
expect(Story.new).to respond_to(:tell_without_delay!)
|
12
|
+
expect(Story.new).to respond_to(:tell_with_delay!)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'creates a PerformableMethod' do
|
16
|
+
story = Story.create
|
17
|
+
expect do
|
18
|
+
job = story.tell!(1)
|
19
|
+
expect(job.payload_object.class).to eq(Delayed::PerformableMethod)
|
20
|
+
expect(job.payload_object.method_name).to eq(:tell_without_delay!)
|
21
|
+
expect(job.payload_object.args).to eq([1])
|
22
|
+
end.to change { Delayed::Job.count }
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'with options' do
|
26
|
+
class Fable
|
27
|
+
cattr_accessor :importance
|
28
|
+
def tell; end
|
29
|
+
handle_asynchronously :tell, :priority => proc { importance }
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'sets the priority based on the Fable importance' do
|
33
|
+
Fable.importance = 10
|
34
|
+
job = Fable.new.tell
|
35
|
+
expect(job.priority).to eq(10)
|
36
|
+
|
37
|
+
Fable.importance = 20
|
38
|
+
job = Fable.new.tell
|
39
|
+
expect(job.priority).to eq(20)
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'using a proc with parameters' do
|
43
|
+
class Yarn
|
44
|
+
attr_accessor :importance
|
45
|
+
def spin
|
46
|
+
end
|
47
|
+
handle_asynchronously :spin, :priority => proc { |y| y.importance }
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'sets the priority based on the Fable importance' do
|
51
|
+
job = Yarn.new.tap { |y| y.importance = 10 }.spin
|
52
|
+
expect(job.priority).to eq(10)
|
53
|
+
|
54
|
+
job = Yarn.new.tap { |y| y.importance = 20 }.spin
|
55
|
+
expect(job.priority).to eq(20)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'delay' do
|
62
|
+
class FairyTail
|
63
|
+
attr_accessor :happy_ending
|
64
|
+
def self.princesses; end
|
65
|
+
def tell
|
66
|
+
@happy_ending = true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
after do
|
71
|
+
Delayed::Worker.default_queue_name = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'creates a new PerformableMethod job' do
|
75
|
+
expect do
|
76
|
+
job = 'hello'.delay.count('l')
|
77
|
+
expect(job.payload_object.class).to eq(Delayed::PerformableMethod)
|
78
|
+
expect(job.payload_object.method_name).to eq(:count)
|
79
|
+
expect(job.payload_object.args).to eq(['l'])
|
80
|
+
end.to change { Delayed::Job.count }.by(1)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'sets default priority' do
|
84
|
+
Delayed::Worker.default_priority = 99
|
85
|
+
job = FairyTail.delay.to_s
|
86
|
+
expect(job.priority).to eq(99)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'sets default queue name' do
|
90
|
+
Delayed::Worker.default_queue_name = 'abbazabba'
|
91
|
+
job = FairyTail.delay.to_s
|
92
|
+
expect(job.queue).to eq('abbazabba')
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'sets job options' do
|
96
|
+
run_at = Time.parse('2010-05-03 12:55 AM')
|
97
|
+
job = FairyTail.delay(:priority => 20, :run_at => run_at).to_s
|
98
|
+
expect(job.run_at).to eq(run_at)
|
99
|
+
expect(job.priority).to eq(20)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'does not delay the job when delay_jobs is false' do
|
103
|
+
Delayed::Worker.delay_jobs = false
|
104
|
+
fairy_tail = FairyTail.new
|
105
|
+
expect do
|
106
|
+
expect do
|
107
|
+
fairy_tail.delay.tell
|
108
|
+
end.to change(fairy_tail, :happy_ending).from(nil).to(true)
|
109
|
+
end.not_to change { Delayed::Job.count }
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'does delay the job when delay_jobs is true' do
|
113
|
+
Delayed::Worker.delay_jobs = true
|
114
|
+
fairy_tail = FairyTail.new
|
115
|
+
expect do
|
116
|
+
expect do
|
117
|
+
fairy_tail.delay.tell
|
118
|
+
end.not_to change(fairy_tail, :happy_ending)
|
119
|
+
end.to change { Delayed::Job.count }.by(1)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
require 'action_mailer'
|
4
|
+
class MyMailer < ActionMailer::Base
|
5
|
+
def signup(email)
|
6
|
+
mail :to => email, :subject => 'Delaying Emails', :from => 'delayedjob@example.com', :body => 'Delaying Emails Body'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ActionMailer::Base do
|
11
|
+
describe 'delay' do
|
12
|
+
it 'enqueues a PerformableEmail job' do
|
13
|
+
expect do
|
14
|
+
job = MyMailer.delay.signup('john@example.com')
|
15
|
+
expect(job.payload_object.class).to eq(Delayed::PerformableMailer)
|
16
|
+
expect(job.payload_object.method_name).to eq(:signup)
|
17
|
+
expect(job.payload_object.args).to eq(['john@example.com'])
|
18
|
+
end.to change { Delayed::Job.count }.by(1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'delay on a mail object' do
|
23
|
+
it 'raises an exception' do
|
24
|
+
expect do
|
25
|
+
MyMailer.signup('john@example.com').delay
|
26
|
+
end.to raise_error(RuntimeError)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe Delayed::PerformableMailer do
|
31
|
+
describe 'perform' do
|
32
|
+
it 'calls the method and #deliver on the mailer' do
|
33
|
+
email = double('email', :deliver => true)
|
34
|
+
mailer_class = double('MailerClass', :signup => email)
|
35
|
+
mailer = Delayed::PerformableMailer.new(mailer_class, :signup, ['john@example.com'])
|
36
|
+
|
37
|
+
expect(mailer_class).to receive(:signup).with('john@example.com')
|
38
|
+
expect(email).to receive(:deliver)
|
39
|
+
mailer.perform
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Delayed::PerformableMethod do
|
4
|
+
describe 'perform' do
|
5
|
+
before do
|
6
|
+
@method = Delayed::PerformableMethod.new('foo', :count, ['o'])
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'with the persisted record cannot be found' do
|
10
|
+
before do
|
11
|
+
@method.object = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'does nothing if object is nil' do
|
15
|
+
expect { @method.perform }.not_to raise_error
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'calls the method on the object' do
|
20
|
+
expect(@method.object).to receive(:count).with('o')
|
21
|
+
@method.perform
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "raises a NoMethodError if target method doesn't exist" do
|
26
|
+
expect do
|
27
|
+
Delayed::PerformableMethod.new(Object, :method_that_does_not_exist, [])
|
28
|
+
end.to raise_error(NoMethodError)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'does not raise NoMethodError if target method is private' do
|
32
|
+
clazz = Class.new do
|
33
|
+
def private_method
|
34
|
+
end
|
35
|
+
private :private_method
|
36
|
+
end
|
37
|
+
expect { Delayed::PerformableMethod.new(clazz.new, :private_method, []) }.not_to raise_error
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'display_name' do
|
41
|
+
it 'returns class_name#method_name for instance methods' do
|
42
|
+
expect(Delayed::PerformableMethod.new('foo', :count, ['o']).display_name).to eq('String#count')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns class_name.method_name for class methods' do
|
46
|
+
expect(Delayed::PerformableMethod.new(Class, :inspect, []).display_name).to eq('Class.inspect')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'hooks' do
|
51
|
+
%w[before after success].each do |hook|
|
52
|
+
it "delegates #{hook} hook to object" do
|
53
|
+
story = Story.create
|
54
|
+
job = story.delay.tell
|
55
|
+
|
56
|
+
expect(story).to receive(hook).with(job)
|
57
|
+
job.invoke_job
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'delegates enqueue hook to object' do
|
62
|
+
story = Story.create
|
63
|
+
expect(story).to receive(:enqueue).with(an_instance_of(Delayed::Job))
|
64
|
+
story.delay.tell
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'delegates error hook to object' do
|
68
|
+
story = Story.create
|
69
|
+
expect(story).to receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
|
70
|
+
expect(story).to receive(:tell).and_raise(RuntimeError)
|
71
|
+
expect { story.delay.tell.invoke_job }.to raise_error
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'delegates failure hook to object' do
|
75
|
+
method = Delayed::PerformableMethod.new('object', :size, [])
|
76
|
+
expect(method.object).to receive(:failure)
|
77
|
+
method.failure
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'with delay_job == false' do
|
81
|
+
before do
|
82
|
+
Delayed::Worker.delay_jobs = false
|
83
|
+
end
|
84
|
+
|
85
|
+
after do
|
86
|
+
Delayed::Worker.delay_jobs = true
|
87
|
+
end
|
88
|
+
|
89
|
+
%w[before after success].each do |hook|
|
90
|
+
it "delegates #{hook} hook to object" do
|
91
|
+
story = Story.create
|
92
|
+
expect(story).to receive(hook).with(an_instance_of(Delayed::Job))
|
93
|
+
story.delay.tell
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'delegates error hook to object' do
|
98
|
+
story = Story.create
|
99
|
+
expect(story).to receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
|
100
|
+
expect(story).to receive(:tell).and_raise(RuntimeError)
|
101
|
+
expect { story.delay.tell }.to raise_error
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'delegates failure hook to object' do
|
105
|
+
method = Delayed::PerformableMethod.new('object', :size, [])
|
106
|
+
expect(method.object).to receive(:failure)
|
107
|
+
method.failure
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe 'Psych::Visitors::ToRuby', :if => defined?(Psych::Visitors::ToRuby) do
|
4
|
+
context BigDecimal do
|
5
|
+
it 'deserializes correctly' do
|
6
|
+
deserialized = YAML.load("--- !ruby/object:BigDecimal 18:0.1337E2\n...\n")
|
7
|
+
|
8
|
+
expect(deserialized).to be_an_instance_of(BigDecimal)
|
9
|
+
expect(deserialized).to eq(BigDecimal('13.37'))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/spec/sample_jobs.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
class NamedJob < Struct.new(:perform)
|
2
|
+
def display_name
|
3
|
+
'named_job'
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class SimpleJob
|
8
|
+
cattr_accessor :runs
|
9
|
+
@runs = 0
|
10
|
+
def perform
|
11
|
+
self.class.runs += 1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class NamedQueueJob < SimpleJob
|
16
|
+
def queue_name
|
17
|
+
'job_tracking'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class ErrorJob
|
22
|
+
cattr_accessor :runs
|
23
|
+
@runs = 0
|
24
|
+
def perform
|
25
|
+
raise 'did not work'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class CustomRescheduleJob < Struct.new(:offset)
|
30
|
+
cattr_accessor :runs
|
31
|
+
@runs = 0
|
32
|
+
def perform
|
33
|
+
raise 'did not work'
|
34
|
+
end
|
35
|
+
|
36
|
+
def reschedule_at(time, _attempts)
|
37
|
+
time + offset
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class LongRunningJob
|
42
|
+
def perform
|
43
|
+
sleep 250
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class OnPermanentFailureJob < SimpleJob
|
48
|
+
attr_writer :raise_error
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
@raise_error = false
|
52
|
+
end
|
53
|
+
|
54
|
+
def failure
|
55
|
+
raise 'did not work' if @raise_error
|
56
|
+
end
|
57
|
+
|
58
|
+
def max_attempts
|
59
|
+
1
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
module M
|
64
|
+
class ModuleJob
|
65
|
+
cattr_accessor :runs
|
66
|
+
@runs = 0
|
67
|
+
def perform
|
68
|
+
self.class.runs += 1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class CallbackJob
|
74
|
+
cattr_accessor :messages
|
75
|
+
|
76
|
+
def enqueue(_job)
|
77
|
+
self.class.messages << 'enqueue'
|
78
|
+
end
|
79
|
+
|
80
|
+
def before(_job)
|
81
|
+
self.class.messages << 'before'
|
82
|
+
end
|
83
|
+
|
84
|
+
def perform
|
85
|
+
self.class.messages << 'perform'
|
86
|
+
end
|
87
|
+
|
88
|
+
def after(_job)
|
89
|
+
self.class.messages << 'after'
|
90
|
+
end
|
91
|
+
|
92
|
+
def success(_job)
|
93
|
+
self.class.messages << 'success'
|
94
|
+
end
|
95
|
+
|
96
|
+
def error(_job, error)
|
97
|
+
self.class.messages << "error: #{error.class}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def failure(_job)
|
101
|
+
self.class.messages << 'failure'
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class EnqueueJobMod < SimpleJob
|
106
|
+
def enqueue(job)
|
107
|
+
job.run_at = 20.minutes.from_now
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Delayed::Backend::Test::Job do
|
4
|
+
it_should_behave_like 'a delayed_job backend'
|
5
|
+
|
6
|
+
describe '#reload' do
|
7
|
+
it 'causes the payload object to be reloaded' do
|
8
|
+
job = 'foo'.delay.length
|
9
|
+
o = job.payload_object
|
10
|
+
expect(o.object_id).not_to eq(job.reload.payload_object.object_id)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/spec/worker_spec.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Delayed::Worker do
|
4
|
+
describe 'backend=' do
|
5
|
+
before do
|
6
|
+
@clazz = Class.new
|
7
|
+
Delayed::Worker.backend = @clazz
|
8
|
+
end
|
9
|
+
|
10
|
+
after do
|
11
|
+
Delayed::Worker.backend = :test
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'sets the Delayed::Job constant to the backend' do
|
15
|
+
expect(Delayed::Job).to eq(@clazz)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'sets backend with a symbol' do
|
19
|
+
Delayed::Worker.backend = :test
|
20
|
+
expect(Delayed::Worker.backend).to eq(Delayed::Backend::Test::Job)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'job_say' do
|
25
|
+
before do
|
26
|
+
@worker = Delayed::Worker.new
|
27
|
+
@job = double('job', :id => 123, :name => 'ExampleJob')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'logs with job name and id' do
|
31
|
+
expect(@worker).to receive(:say).
|
32
|
+
with('Job ExampleJob (id=123) message', Delayed::Worker.default_log_level)
|
33
|
+
@worker.job_say(@job, 'message')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'has a configurable default log level' do
|
37
|
+
Delayed::Worker.default_log_level = 'error'
|
38
|
+
|
39
|
+
expect(@worker).to receive(:say).
|
40
|
+
with('Job ExampleJob (id=123) message', 'error')
|
41
|
+
@worker.job_say(@job, 'message')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'worker read-ahead' do
|
46
|
+
before do
|
47
|
+
@read_ahead = Delayed::Worker.read_ahead
|
48
|
+
end
|
49
|
+
|
50
|
+
after do
|
51
|
+
Delayed::Worker.read_ahead = @read_ahead
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'reads five jobs' do
|
55
|
+
expect(Delayed::Job).to receive(:find_available).with(anything, 5, anything).and_return([])
|
56
|
+
Delayed::Job.reserve(Delayed::Worker.new)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'reads a configurable number of jobs' do
|
60
|
+
Delayed::Worker.read_ahead = 15
|
61
|
+
expect(Delayed::Job).to receive(:find_available).with(anything, Delayed::Worker.read_ahead, anything).and_return([])
|
62
|
+
Delayed::Job.reserve(Delayed::Worker.new)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'worker exit on complete' do
|
67
|
+
before do
|
68
|
+
Delayed::Worker.exit_on_complete = true
|
69
|
+
end
|
70
|
+
|
71
|
+
after do
|
72
|
+
Delayed::Worker.exit_on_complete = false
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'exits the loop when no jobs are available' do
|
76
|
+
worker = Delayed::Worker.new
|
77
|
+
Timeout.timeout(2) do
|
78
|
+
worker.start
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'worker job reservation' do
|
84
|
+
before do
|
85
|
+
Delayed::Worker.exit_on_complete = true
|
86
|
+
end
|
87
|
+
|
88
|
+
after do
|
89
|
+
Delayed::Worker.exit_on_complete = false
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'handles error during job reservation' do
|
93
|
+
expect(Delayed::Job).to receive(:reserve).and_raise(Exception)
|
94
|
+
Delayed::Worker.new.work_off
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'gives up after 10 backend failures' do
|
98
|
+
expect(Delayed::Job).to receive(:reserve).exactly(10).times.and_raise(Exception)
|
99
|
+
worker = Delayed::Worker.new
|
100
|
+
9.times { worker.work_off }
|
101
|
+
expect(lambda { worker.work_off }).to raise_exception Delayed::FatalBackendError
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'allows the backend to attempt recovery from reservation errors' do
|
105
|
+
expect(Delayed::Job).to receive(:reserve).and_raise(Exception)
|
106
|
+
expect(Delayed::Job).to receive(:recover_from).with(instance_of(Exception))
|
107
|
+
Delayed::Worker.new.work_off
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context '#say' do
|
112
|
+
before(:each) do
|
113
|
+
@worker = Delayed::Worker.new
|
114
|
+
@worker.name = 'ExampleJob'
|
115
|
+
@worker.logger = double('job')
|
116
|
+
time = Time.now
|
117
|
+
allow(Time).to receive(:now).and_return(time)
|
118
|
+
@text = 'Job executed'
|
119
|
+
@worker_name = '[Worker(ExampleJob)]'
|
120
|
+
@expected_time = time.strftime('%FT%T%z')
|
121
|
+
end
|
122
|
+
|
123
|
+
after(:each) do
|
124
|
+
@worker.logger = nil
|
125
|
+
end
|
126
|
+
|
127
|
+
shared_examples_for 'a worker which logs on the correct severity' do |severity|
|
128
|
+
it "logs a message on the #{severity[:level].upcase} level given a string" do
|
129
|
+
expect(@worker.logger).to receive(:send).
|
130
|
+
with(severity[:level], "#{@expected_time}: #{@worker_name} #{@text}")
|
131
|
+
@worker.say(@text, severity[:level])
|
132
|
+
end
|
133
|
+
|
134
|
+
it "logs a message on the #{severity[:level].upcase} level given a fixnum" do
|
135
|
+
expect(@worker.logger).to receive(:send).
|
136
|
+
with(severity[:level], "#{@expected_time}: #{@worker_name} #{@text}")
|
137
|
+
@worker.say(@text, severity[:index])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
severities = [{:index => 0, :level => 'debug'},
|
142
|
+
{:index => 1, :level => 'info'},
|
143
|
+
{:index => 2, :level => 'warn'},
|
144
|
+
{:index => 3, :level => 'error'},
|
145
|
+
{:index => 4, :level => 'fatal'},
|
146
|
+
{:index => 5, :level => 'unknown'}]
|
147
|
+
severities.each do |severity|
|
148
|
+
it_behaves_like 'a worker which logs on the correct severity', severity
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'logs a message on the default log\'s level' do
|
152
|
+
expect(@worker.logger).to receive(:send).
|
153
|
+
with('info', "#{@expected_time}: #{@worker_name} #{@text}")
|
154
|
+
@worker.say(@text, Delayed::Worker.default_log_level)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe 'YAML' do
|
4
|
+
it 'autoloads classes' do
|
5
|
+
expect do
|
6
|
+
yaml = "--- !ruby/class Autoloaded::Clazz\n"
|
7
|
+
expect(load_with_delayed_visitor(yaml)).to eq(Autoloaded::Clazz)
|
8
|
+
end.not_to raise_error
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'autoloads the class of a struct' do
|
12
|
+
expect do
|
13
|
+
yaml = "--- !ruby/class Autoloaded::Struct\n"
|
14
|
+
expect(load_with_delayed_visitor(yaml)).to eq(Autoloaded::Struct)
|
15
|
+
end.not_to raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'autoloads the class for the instance of a struct' do
|
19
|
+
expect do
|
20
|
+
yaml = '--- !ruby/struct:Autoloaded::InstanceStruct {}'
|
21
|
+
expect(load_with_delayed_visitor(yaml).class).to eq(Autoloaded::InstanceStruct)
|
22
|
+
end.not_to raise_error
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'autoloads the class of an anonymous struct' do
|
26
|
+
expect do
|
27
|
+
yaml = "--- !ruby/struct\nn: 1\n"
|
28
|
+
object = YAML.load(yaml)
|
29
|
+
expect(object).to be_kind_of(Struct)
|
30
|
+
expect(object.n).to eq(1)
|
31
|
+
end.not_to raise_error
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'autoloads the class for the instance' do
|
35
|
+
expect do
|
36
|
+
yaml = "--- !ruby/object:Autoloaded::InstanceClazz {}\n"
|
37
|
+
expect(load_with_delayed_visitor(yaml).class).to eq(Autoloaded::InstanceClazz)
|
38
|
+
end.not_to raise_error
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'does not throw an uninitialized constant Syck::Syck when using YAML.load with poorly formed yaml' do
|
42
|
+
expect { YAML.load(YAML.dump('foo: *bar')) }.not_to raise_error
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_with_delayed_visitor(yaml)
|
46
|
+
YAML.load_dj(yaml)
|
47
|
+
end
|
48
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delayed_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandon Keepers
|
@@ -50,6 +50,7 @@ files:
|
|
50
50
|
- CONTRIBUTING.md
|
51
51
|
- LICENSE.md
|
52
52
|
- README.md
|
53
|
+
- Rakefile
|
53
54
|
- contrib/delayed_job.monitrc
|
54
55
|
- contrib/delayed_job_multiple.monitrc
|
55
56
|
- contrib/delayed_job_rails_4.monitrc
|
@@ -79,6 +80,23 @@ files:
|
|
79
80
|
- lib/generators/delayed_job/delayed_job_generator.rb
|
80
81
|
- lib/generators/delayed_job/templates/script
|
81
82
|
- recipes/delayed_job.rb
|
83
|
+
- spec/autoloaded/clazz.rb
|
84
|
+
- spec/autoloaded/instance_clazz.rb
|
85
|
+
- spec/autoloaded/instance_struct.rb
|
86
|
+
- spec/autoloaded/struct.rb
|
87
|
+
- spec/delayed/backend/test.rb
|
88
|
+
- spec/delayed/command_spec.rb
|
89
|
+
- spec/delayed/serialization/test.rb
|
90
|
+
- spec/helper.rb
|
91
|
+
- spec/lifecycle_spec.rb
|
92
|
+
- spec/message_sending_spec.rb
|
93
|
+
- spec/performable_mailer_spec.rb
|
94
|
+
- spec/performable_method_spec.rb
|
95
|
+
- spec/psych_ext_spec.rb
|
96
|
+
- spec/sample_jobs.rb
|
97
|
+
- spec/test_backend_spec.rb
|
98
|
+
- spec/worker_spec.rb
|
99
|
+
- spec/yaml_ext_spec.rb
|
82
100
|
homepage: http://github.com/collectiveidea/delayed_job
|
83
101
|
licenses:
|
84
102
|
- MIT
|
@@ -103,4 +121,21 @@ rubygems_version: 2.4.4
|
|
103
121
|
signing_key:
|
104
122
|
specification_version: 4
|
105
123
|
summary: Database-backed asynchronous priority queue system -- Extracted from Shopify
|
106
|
-
test_files:
|
124
|
+
test_files:
|
125
|
+
- spec/autoloaded/clazz.rb
|
126
|
+
- spec/autoloaded/instance_clazz.rb
|
127
|
+
- spec/autoloaded/instance_struct.rb
|
128
|
+
- spec/autoloaded/struct.rb
|
129
|
+
- spec/delayed/backend/test.rb
|
130
|
+
- spec/delayed/command_spec.rb
|
131
|
+
- spec/delayed/serialization/test.rb
|
132
|
+
- spec/helper.rb
|
133
|
+
- spec/lifecycle_spec.rb
|
134
|
+
- spec/message_sending_spec.rb
|
135
|
+
- spec/performable_mailer_spec.rb
|
136
|
+
- spec/performable_method_spec.rb
|
137
|
+
- spec/psych_ext_spec.rb
|
138
|
+
- spec/sample_jobs.rb
|
139
|
+
- spec/test_backend_spec.rb
|
140
|
+
- spec/worker_spec.rb
|
141
|
+
- spec/yaml_ext_spec.rb
|