delayed_job_tgmerritt 4.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +208 -0
  3. data/CONTRIBUTING.md +27 -0
  4. data/LICENSE.md +20 -0
  5. data/README.md +424 -0
  6. data/Rakefile +15 -0
  7. data/contrib/delayed_job.monitrc +14 -0
  8. data/contrib/delayed_job_multiple.monitrc +34 -0
  9. data/contrib/delayed_job_rails_4.monitrc +14 -0
  10. data/contrib/delayed_job_rails_4_multiple.monitrc +34 -0
  11. data/delayed_job.gemspec +15 -0
  12. data/lib/delayed/backend/base.rb +159 -0
  13. data/lib/delayed/backend/shared_spec.rb +657 -0
  14. data/lib/delayed/command.rb +170 -0
  15. data/lib/delayed/compatibility.rb +27 -0
  16. data/lib/delayed/deserialization_error.rb +4 -0
  17. data/lib/delayed/exceptions.rb +12 -0
  18. data/lib/delayed/lifecycle.rb +85 -0
  19. data/lib/delayed/message_sending.rb +52 -0
  20. data/lib/delayed/performable_mailer.rb +22 -0
  21. data/lib/delayed/performable_method.rb +41 -0
  22. data/lib/delayed/plugin.rb +15 -0
  23. data/lib/delayed/plugins/clear_locks.rb +15 -0
  24. data/lib/delayed/psych_ext.rb +89 -0
  25. data/lib/delayed/railtie.rb +22 -0
  26. data/lib/delayed/recipes.rb +54 -0
  27. data/lib/delayed/serialization/active_record.rb +17 -0
  28. data/lib/delayed/syck_ext.rb +42 -0
  29. data/lib/delayed/tasks.rb +39 -0
  30. data/lib/delayed/worker.rb +312 -0
  31. data/lib/delayed/yaml_ext.rb +10 -0
  32. data/lib/delayed_job.rb +22 -0
  33. data/lib/generators/delayed_job/delayed_job_generator.rb +11 -0
  34. data/lib/generators/delayed_job/templates/script +5 -0
  35. data/recipes/delayed_job.rb +1 -0
  36. data/spec/autoloaded/clazz.rb +7 -0
  37. data/spec/autoloaded/instance_clazz.rb +6 -0
  38. data/spec/autoloaded/instance_struct.rb +7 -0
  39. data/spec/autoloaded/struct.rb +8 -0
  40. data/spec/daemons.rb +2 -0
  41. data/spec/delayed/backend/test.rb +117 -0
  42. data/spec/delayed/command_spec.rb +180 -0
  43. data/spec/delayed/serialization/test.rb +0 -0
  44. data/spec/helper.rb +85 -0
  45. data/spec/lifecycle_spec.rb +75 -0
  46. data/spec/message_sending_spec.rb +122 -0
  47. data/spec/performable_mailer_spec.rb +43 -0
  48. data/spec/performable_method_spec.rb +111 -0
  49. data/spec/psych_ext_spec.rb +12 -0
  50. data/spec/sample_jobs.rb +111 -0
  51. data/spec/test_backend_spec.rb +13 -0
  52. data/spec/worker_spec.rb +175 -0
  53. data/spec/yaml_ext_spec.rb +48 -0
  54. metadata +144 -0
@@ -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
@@ -0,0 +1,111 @@
1
+ NamedJob = Struct.new(:perform)
2
+ class NamedJob
3
+ def display_name
4
+ 'named_job'
5
+ end
6
+ end
7
+
8
+ class SimpleJob
9
+ cattr_accessor :runs
10
+ @runs = 0
11
+ def perform
12
+ self.class.runs += 1
13
+ end
14
+ end
15
+
16
+ class NamedQueueJob < SimpleJob
17
+ def queue_name
18
+ 'job_tracking'
19
+ end
20
+ end
21
+
22
+ class ErrorJob
23
+ cattr_accessor :runs
24
+ @runs = 0
25
+ def perform
26
+ raise 'did not work'
27
+ end
28
+ end
29
+
30
+ CustomRescheduleJob = Struct.new(:offset)
31
+ class CustomRescheduleJob
32
+ cattr_accessor :runs
33
+ @runs = 0
34
+ def perform
35
+ raise 'did not work'
36
+ end
37
+
38
+ def reschedule_at(time, _attempts)
39
+ time + offset
40
+ end
41
+ end
42
+
43
+ class LongRunningJob
44
+ def perform
45
+ sleep 250
46
+ end
47
+ end
48
+
49
+ class OnPermanentFailureJob < SimpleJob
50
+ attr_writer :raise_error
51
+
52
+ def initialize
53
+ @raise_error = false
54
+ end
55
+
56
+ def failure
57
+ raise 'did not work' if @raise_error
58
+ end
59
+
60
+ def max_attempts
61
+ 1
62
+ end
63
+ end
64
+
65
+ module M
66
+ class ModuleJob
67
+ cattr_accessor :runs
68
+ @runs = 0
69
+ def perform
70
+ self.class.runs += 1
71
+ end
72
+ end
73
+ end
74
+
75
+ class CallbackJob
76
+ cattr_accessor :messages
77
+
78
+ def enqueue(_job)
79
+ self.class.messages << 'enqueue'
80
+ end
81
+
82
+ def before(_job)
83
+ self.class.messages << 'before'
84
+ end
85
+
86
+ def perform
87
+ self.class.messages << 'perform'
88
+ end
89
+
90
+ def after(_job)
91
+ self.class.messages << 'after'
92
+ end
93
+
94
+ def success(_job)
95
+ self.class.messages << 'success'
96
+ end
97
+
98
+ def error(_job, error)
99
+ self.class.messages << "error: #{error.class}"
100
+ end
101
+
102
+ def failure(_job)
103
+ self.class.messages << 'failure'
104
+ end
105
+ end
106
+
107
+ class EnqueueJobMod < SimpleJob
108
+ def enqueue(job)
109
+ job.run_at = 20.minutes.from_now
110
+ end
111
+ end