delayed_job 4.0.5 → 4.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|