delayed_job 2.1.4 → 3.0.0.pre

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.
@@ -2,16 +2,22 @@ require 'timeout'
2
2
  require 'active_support/core_ext/numeric/time'
3
3
  require 'active_support/core_ext/class/attribute_accessors'
4
4
  require 'active_support/core_ext/kernel'
5
+ require 'active_support/core_ext/enumerable'
5
6
  require 'logger'
6
7
 
7
8
  module Delayed
8
9
  class Worker
9
- cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time, :default_priority, :sleep_delay, :logger, :delay_jobs
10
+ cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time, :default_priority, :sleep_delay, :logger, :delay_jobs, :queues
10
11
  self.sleep_delay = 5
11
12
  self.max_attempts = 25
12
13
  self.max_run_time = 4.hours
13
14
  self.default_priority = 0
14
15
  self.delay_jobs = true
16
+ self.queues = []
17
+
18
+ # Add or remove plugins in this list before the worker is instantiated
19
+ cattr_accessor :plugins
20
+ self.plugins = [Delayed::Plugins::ClearLocks]
15
21
 
16
22
  # By default failed jobs are destroyed after too many attempts. If you want to keep them around
17
23
  # (perhaps to inspect the reason for the failure), set this to false.
@@ -31,7 +37,7 @@ module Delayed
31
37
 
32
38
  def self.backend=(backend)
33
39
  if backend.is_a? Symbol
34
- require "delayed/serialization/#{backend}"
40
+ require "delayed/serialization/#{backend}" if YAML.parser.class.name =~ /syck/i
35
41
  require "delayed/backend/#{backend}"
36
42
  backend = "Delayed::Backend::#{backend.to_s.classify}::Job".constantize
37
43
  end
@@ -40,7 +46,35 @@ module Delayed
40
46
  end
41
47
 
42
48
  def self.guess_backend
43
- self.backend ||= :active_record if defined?(ActiveRecord)
49
+ warn "[DEPRECATION] guess_backend is deprecated. Please remove it from your code."
50
+ end
51
+
52
+ def self.before_fork
53
+ unless @files_to_reopen
54
+ @files_to_reopen = []
55
+ ObjectSpace.each_object(File) do |file|
56
+ @files_to_reopen << file unless file.closed?
57
+ end
58
+ end
59
+
60
+ backend.before_fork
61
+ end
62
+
63
+ def self.after_fork
64
+ # Re-open file handles
65
+ @files_to_reopen.each do |file|
66
+ begin
67
+ file.reopen file.path, "a+"
68
+ file.sync = true
69
+ rescue ::Exception
70
+ end
71
+ end
72
+
73
+ backend.after_fork
74
+ end
75
+
76
+ def self.lifecycle
77
+ @lifecycle ||= Delayed::Lifecycle.new
44
78
  end
45
79
 
46
80
  def initialize(options={})
@@ -48,6 +82,9 @@ module Delayed
48
82
  self.class.min_priority = options[:min_priority] if options.has_key?(:min_priority)
49
83
  self.class.max_priority = options[:max_priority] if options.has_key?(:max_priority)
50
84
  self.class.sleep_delay = options[:sleep_delay] if options.has_key?(:sleep_delay)
85
+ self.class.queues = options[:queues] if options.has_key?(:queues)
86
+
87
+ self.plugins.each { |klass| klass.new }
51
88
  end
52
89
 
53
90
  # Every worker has a unique name which by default is the pid of the process. There are some
@@ -66,33 +103,38 @@ module Delayed
66
103
  end
67
104
 
68
105
  def start
106
+ trap('TERM') { say 'Exiting...'; stop }
107
+ trap('INT') { say 'Exiting...'; stop }
108
+
69
109
  say "Starting job worker"
70
110
 
71
- trap('TERM') { say 'Exiting...'; $exit = true }
72
- trap('INT') { say 'Exiting...'; $exit = true }
111
+ self.class.lifecycle.run_callbacks(:execute, self) do
112
+ loop do
113
+ self.class.lifecycle.run_callbacks(:loop, self) do
114
+ result = nil
73
115
 
74
- loop do
75
- result = nil
116
+ realtime = Benchmark.realtime do
117
+ result = work_off
118
+ end
76
119
 
77
- realtime = Benchmark.realtime do
78
- result = work_off
79
- end
120
+ count = result.sum
80
121
 
81
- count = result.sum
122
+ break if @exit
82
123
 
83
- break if $exit
124
+ if count.zero?
125
+ sleep(self.class.sleep_delay)
126
+ else
127
+ say "#{count} jobs processed at %.4f j/s, %d failed ..." % [count / realtime, result.last]
128
+ end
129
+ end
84
130
 
85
- if count.zero?
86
- sleep(self.class.sleep_delay)
87
- else
88
- say "#{count} jobs processed at %.4f j/s, %d failed ..." % [count / realtime, result.last]
131
+ break if @exit
89
132
  end
90
-
91
- break if $exit
92
133
  end
134
+ end
93
135
 
94
- ensure
95
- Delayed::Job.clear_locks!(name)
136
+ def stop
137
+ @exit = true
96
138
  end
97
139
 
98
140
  # Do num jobs and return stats on success/failure.
@@ -126,7 +168,7 @@ module Delayed
126
168
  job.last_error = "{#{error.message}\n#{error.backtrace.join('\n')}"
127
169
  failed(job)
128
170
  rescue Exception => error
129
- handle_failed_job(job, error)
171
+ self.class.lifecycle.run_callbacks(:error, self, job){ handle_failed_job(job, error) }
130
172
  return false # work failed
131
173
  end
132
174
 
@@ -145,11 +187,10 @@ module Delayed
145
187
  end
146
188
 
147
189
  def failed(job)
148
- job.hook(:failure)
149
- if job.respond_to?(:on_permanent_failure)
150
- warn "[DEPRECATION] The #on_permanent_failure hook has been renamed to #failure."
190
+ self.class.lifecycle.run_callbacks(:failure, self, job) do
191
+ job.hook(:failure)
192
+ self.class.destroy_failed_jobs ? job.destroy : job.fail!
151
193
  end
152
- self.class.destroy_failed_jobs ? job.destroy : job.update_attributes(:failed_at => Delayed::Job.db_time_now)
153
194
  end
154
195
 
155
196
  def say(text, level = Logger::INFO)
@@ -161,7 +202,7 @@ module Delayed
161
202
  def max_attempts(job)
162
203
  job.max_attempts || self.class.max_attempts
163
204
  end
164
-
205
+
165
206
  protected
166
207
 
167
208
  def handle_failed_job(job, error)
@@ -174,7 +215,7 @@ module Delayed
174
215
  # If no jobs are left we return nil
175
216
  def reserve_and_run_one_job
176
217
  job = Delayed::Job.reserve(self)
177
- run(job) if job
218
+ self.class.lifecycle.run_callbacks(:perform, self, job){ result = run(job) } if job
178
219
  end
179
220
  end
180
221
 
@@ -2,40 +2,9 @@
2
2
  # Classes, Modules and Structs
3
3
 
4
4
  require 'yaml'
5
- YAML::ENGINE.yamler = "syck" if defined?(YAML::ENGINE)
6
-
7
- class Module
8
- yaml_as "tag:ruby.yaml.org,2002:module"
9
-
10
- def self.yaml_new(klass, tag, val)
11
- val.constantize
12
- end
13
-
14
- def to_yaml( opts = {} )
15
- YAML::quick_emit( nil, opts ) { |out|
16
- out.scalar(taguri, self.name, :plain)
17
- }
18
- end
19
-
20
- def yaml_tag_read_class(name)
21
- # Constantize the object so that ActiveSupport can attempt
22
- # its auto loading magic. Will raise LoadError if not successful.
23
- name.constantize
24
- name
25
- end
26
-
27
- end
28
-
29
- class Class
30
- yaml_as "tag:ruby.yaml.org,2002:class"
31
- remove_method :to_yaml if respond_to?(:to_yaml) && method(:to_yaml).owner == Class # use Module's to_yaml
32
- end
33
-
34
- class Struct
35
- def self.yaml_tag_read_class(name)
36
- # Constantize the object so that ActiveSupport can attempt
37
- # its auto loading magic. Will raise LoadError if not successful.
38
- name.constantize
39
- "Struct::#{ name }"
40
- end
5
+ if YAML.parser.class.name =~ /syck/i
6
+ require File.expand_path('../syck_ext', __FILE__)
7
+ require File.expand_path('../serialization/active_record', __FILE__)
8
+ else
9
+ require File.expand_path('../psych_ext', __FILE__)
41
10
  end
data/lib/delayed_job.rb CHANGED
@@ -2,8 +2,17 @@ require 'active_support'
2
2
 
3
3
  require File.dirname(__FILE__) + '/delayed/message_sending'
4
4
  require File.dirname(__FILE__) + '/delayed/performable_method'
5
- require File.dirname(__FILE__) + '/delayed/performable_mailer' if defined?(ActionMailer)
5
+
6
+ # PerformableMailer is compatible with ActionMailer 3 (and possibly 3.1)
7
+ if defined?(ActionMailer)
8
+ require 'action_mailer/version'
9
+ require File.dirname(__FILE__) + '/delayed/performable_mailer' if 3 == ActionMailer::VERSION::MAJOR
10
+ end
11
+
6
12
  require File.dirname(__FILE__) + '/delayed/yaml_ext'
13
+ require File.dirname(__FILE__) + '/delayed/lifecycle'
14
+ require File.dirname(__FILE__) + '/delayed/plugin'
15
+ require File.dirname(__FILE__) + '/delayed/plugins/clear_locks'
7
16
  require File.dirname(__FILE__) + '/delayed/backend/base'
8
17
  require File.dirname(__FILE__) + '/delayed/worker'
9
18
  require File.dirname(__FILE__) + '/delayed/deserialization_error'
@@ -1,34 +1,11 @@
1
1
  require 'rails/generators'
2
- require 'rails/generators/migration'
3
2
 
4
3
  class DelayedJobGenerator < Rails::Generators::Base
5
-
6
- include Rails::Generators::Migration
7
4
 
8
- def self.source_root
9
- @source_root ||= File.join(File.dirname(__FILE__), 'templates')
10
- end
11
-
12
- # Implement the required interface for Rails::Generators::Migration.
13
- #
14
- def self.next_migration_number(dirname) #:nodoc:
15
- next_migration_number = current_migration_number(dirname) + 1
16
- if ActiveRecord::Base.timestamped_migrations
17
- [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
18
- else
19
- "%.3d" % next_migration_number
20
- end
21
- end
5
+ self.source_paths << File.join(File.dirname(__FILE__), 'templates')
22
6
 
23
7
  def create_script_file
24
8
  template 'script', 'script/delayed_job'
25
9
  chmod 'script/delayed_job', 0755
26
10
  end
27
-
28
- def create_migration_file
29
- if defined?(ActiveRecord)
30
- migration_template 'migration.rb', 'db/migrate/create_delayed_jobs.rb'
31
- end
32
- end
33
-
34
11
  end
@@ -0,0 +1,6 @@
1
+ module Autoloaded
2
+ class InstanceClazz
3
+ def perform
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Autoloaded
2
+ class InstanceStruct < ::Struct.new(nil)
3
+ def perform
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,112 @@
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
+ @jobs = []
32
+ def self.all
33
+ @jobs
34
+ end
35
+
36
+ def self.count
37
+ all.size
38
+ end
39
+
40
+ def self.delete_all
41
+ all.clear
42
+ end
43
+
44
+ def self.create(attrs = {})
45
+ new(attrs).tap do |o|
46
+ o.save
47
+ end
48
+ end
49
+
50
+ def self.create!(*args); create(*args); end
51
+
52
+ def self.clear_locks!(worker_name)
53
+ all.select{|j| j.locked_by == worker_name}.each {|j| j.locked_by = nil; j.locked_at = nil}
54
+ end
55
+
56
+ # Find a few candidate jobs to run (in case some immediately get locked by others).
57
+ def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)
58
+ jobs = all.select do |j|
59
+ j.run_at <= db_time_now &&
60
+ (j.locked_at.nil? || j.locked_at < db_time_now - max_run_time || j.locked_by == worker_name) &&
61
+ j.failed_at.nil?
62
+ end
63
+
64
+ jobs = jobs.select{|j| Worker.queues.include?(j.queue)} if Worker.queues.any?
65
+ jobs = jobs.select{|j| j.priority >= Worker.min_priority} if Worker.min_priority
66
+ jobs = jobs.select{|j| j.priority <= Worker.max_priority} if Worker.max_priority
67
+ jobs.sort_by{|j| [j.priority, j.run_at]}[0..limit-1]
68
+ end
69
+
70
+ # Lock this job for this worker.
71
+ # Returns true if we have the lock, false otherwise.
72
+ def lock_exclusively!(max_run_time, worker)
73
+ now = self.class.db_time_now
74
+ if locked_by != worker
75
+ # We don't own this job so we will update the locked_by name and the locked_at
76
+ self.locked_at = now
77
+ self.locked_by = worker
78
+ end
79
+
80
+ return true
81
+ end
82
+
83
+ def self.db_time_now
84
+ Time.current
85
+ end
86
+
87
+ def update_attributes(attrs = {})
88
+ attrs.each{|k,v| send(:"#{k}=", v)}
89
+ save
90
+ end
91
+
92
+ def destroy
93
+ self.class.all.delete(self)
94
+ end
95
+
96
+ def save
97
+ self.run_at ||= Time.current
98
+
99
+ self.class.all << self unless self.class.all.include?(self)
100
+ true
101
+ end
102
+
103
+ def save!; save; end
104
+
105
+ def reload
106
+ reset
107
+ self
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
File without changes
@@ -0,0 +1,107 @@
1
+ require 'spec_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) { mock(Object, :before! => nil, :after! => nil, :inside! => nil) }
8
+ let(:wrapped_block) { Proc.new { behavior.inside! } }
9
+
10
+ describe "before callbacks" do
11
+ before(:each) do
12
+ lifecycle.before(:execute, &callback)
13
+ end
14
+
15
+ it 'should execute before wrapped block' do
16
+ callback.should_receive(:call).with(*arguments).ordered
17
+ behavior.should_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 'should execute after wrapped block' do
28
+ behavior.should_receive(:inside!).ordered
29
+ callback.should_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 'should before and after wrapped block' do
44
+ behavior.should_receive(:before!).ordered
45
+ behavior.should_receive(:inside!).ordered
46
+ behavior.should_receive(:after!).ordered
47
+ lifecycle.run_callbacks :execute, *arguments, &wrapped_block
48
+ end
49
+
50
+ it "should execute multiple callbacks in order" do
51
+ behavior.should_receive(:one).ordered
52
+ behavior.should_receive(:two).ordered
53
+ behavior.should_receive(:three).ordered
54
+
55
+ lifecycle.around(:execute) { |*args, &block| behavior.one; block.call(*args) }
56
+ lifecycle.around(:execute) { |*args, &block| behavior.two; block.call(*args) }
57
+ lifecycle.around(:execute) { |*args, &block| behavior.three; block.call(*args) }
58
+
59
+ lifecycle.run_callbacks(:execute, *arguments, &wrapped_block)
60
+ end
61
+ end
62
+
63
+ it "should raise if callback is executed with wrong number of parameters" do
64
+ lifecycle.before(:execute, &callback)
65
+ expect { lifecycle.run_callbacks(:execute, 1,2,3) {} }.to raise_error(ArgumentError, /1 parameter/)
66
+ end
67
+
68
+ # # This is a spectacularly crappy way to test callbacks. What's a better way?
69
+ # describe 'arguments callbacks' do
70
+ # subject do
71
+ # class Testarguments < Delayed::arguments
72
+ # def before_execute; end
73
+ # def before_loop; end
74
+ # def before_perform; end
75
+ #
76
+ # set_callback :execute, :before, :before_execute
77
+ # set_callback :loop, :before, :before_loop
78
+ # set_callback :perform, :before, :before_perform
79
+ # end
80
+ #
81
+ # Testarguments.new.tap { |w| w.stop }
82
+ # end
83
+ #
84
+ # it "should trigger for execute event" do
85
+ # subject.should_receive(:before_execute).with()
86
+ # subject.start
87
+ # end
88
+ #
89
+ # it "should trigger for loop event" do
90
+ # subject.should_receive(:before_loop).with()
91
+ # subject.start
92
+ # end
93
+ #
94
+ # it "should trigger for perform event" do
95
+ # "foo".delay.length
96
+ # subject.should_receive(:before_perform).with()
97
+ # subject.start
98
+ # end
99
+ # end
100
+ #
101
+ # describe 'job callbacks' do
102
+ # it "should trigger for enqueue event" do
103
+ # pending 'figure out how to test this'
104
+ # end
105
+ # end
106
+
107
+ end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Delayed::MessageSending do
4
4
  describe "handle_asynchronously" do
5
- class Story < ActiveRecord::Base
5
+ class Story
6
6
  def tell!(arg)
7
7
  end
8
8
  handle_asynchronously :tell!
@@ -14,7 +14,7 @@ describe Delayed::MessageSending do
14
14
  end
15
15
 
16
16
  it "should create a PerformableMethod" do
17
- story = Story.create!
17
+ story = Story.new
18
18
  lambda {
19
19
  job = story.tell!(1)
20
20
  job.payload_object.class.should == Delayed::PerformableMethod
@@ -47,18 +47,43 @@ describe Delayed::PerformableMethod do
47
47
  story.delay.tell.invoke_job
48
48
  end
49
49
  end
50
-
50
+
51
+ %w(before after success).each do |hook|
52
+ it "should delegate #{hook} hook to object when delay_jobs = false" do
53
+ Delayed::Worker.delay_jobs = false
54
+ story = Story.new
55
+ story.should_receive(hook).with(an_instance_of(Delayed::Job))
56
+ story.delay.tell
57
+ end
58
+ end
59
+
51
60
  it "should delegate error hook to object" do
52
61
  story = Story.new
53
62
  story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
54
63
  story.should_receive(:tell).and_raise(RuntimeError)
55
64
  lambda { story.delay.tell.invoke_job }.should raise_error
56
65
  end
66
+
67
+ it "should delegate error hook to object when delay_jobs = false" do
68
+ Delayed::Worker.delay_jobs = false
69
+ story = Story.new
70
+ story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
71
+ story.should_receive(:tell).and_raise(RuntimeError)
72
+ lambda { story.delay.tell }.should raise_error
73
+ end
57
74
 
58
75
  it "should delegate failure hook to object" do
59
76
  method = Delayed::PerformableMethod.new("object", :size, [])
60
77
  method.object.should_receive(:failure)
61
78
  method.failure
62
79
  end
80
+
81
+ it "should delegate failure hook to object when delay_jobs = false" do
82
+ Delayed::Worker.delay_jobs = false
83
+ method = Delayed::PerformableMethod.new("object", :size, [])
84
+ method.object.should_receive(:failure)
85
+ method.failure
86
+ end
87
+
63
88
  end
64
89
  end
data/spec/sample_jobs.rb CHANGED
@@ -67,3 +67,9 @@ class CallbackJob
67
67
  self.class.messages << 'failure'
68
68
  end
69
69
  end
70
+
71
+ class EnqueueJobMod < SimpleJob
72
+ def enqueue(job)
73
+ job.run_at = 20.minutes.from_now
74
+ end
75
+ end
data/spec/spec_helper.rb CHANGED
@@ -6,8 +6,9 @@ require 'rspec'
6
6
  require 'logger'
7
7
 
8
8
  require 'rails'
9
- require 'active_record'
10
9
  require 'action_mailer'
10
+ require 'active_support/dependencies'
11
+ require 'active_record'
11
12
 
12
13
  require 'delayed_job'
13
14
  require 'delayed/backend/shared_spec'
@@ -15,44 +16,32 @@ require 'delayed/backend/shared_spec'
15
16
  Delayed::Worker.logger = Logger.new('/tmp/dj.log')
16
17
  ENV['RAILS_ENV'] = 'test'
17
18
 
18
- config = YAML.load(File.read('spec/database.yml'))
19
- ActiveRecord::Base.configurations = {'test' => config['mysql']}
20
- ActiveRecord::Base.establish_connection
19
+ Delayed::Worker.backend = :test
20
+
21
+ # Add this directory so the ActiveSupport autoloading works
22
+ ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
23
+
24
+ # Add this to simulate Railtie initializer being executed
25
+ ActionMailer::Base.send(:extend, Delayed::DelayMail)
26
+
27
+
28
+ # Used to test interactions between DJ and an ORM
29
+ ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
21
30
  ActiveRecord::Base.logger = Delayed::Worker.logger
22
31
  ActiveRecord::Migration.verbose = false
23
32
 
24
33
  ActiveRecord::Schema.define do
25
- create_table :delayed_jobs, :force => true do |table|
26
- table.integer :priority, :default => 0
27
- table.integer :attempts, :default => 0
28
- table.text :handler
29
- table.text :last_error
30
- table.datetime :run_at
31
- table.datetime :locked_at
32
- table.datetime :failed_at
33
- table.string :locked_by
34
- table.timestamps
35
- end
36
-
37
- add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
38
-
39
- create_table :stories, :force => true do |table|
34
+ create_table :stories, :primary_key => :story_id, :force => true do |table|
40
35
  table.string :text
36
+ table.boolean :scoped, :default => true
41
37
  end
42
38
  end
43
39
 
44
- # Purely useful for test cases...
45
40
  class Story < ActiveRecord::Base
41
+ set_primary_key :story_id
46
42
  def tell; text; end
47
43
  def whatever(n, _); tell*n; end
44
+ default_scope where(:scoped => true)
48
45
 
49
46
  handle_asynchronously :whatever
50
47
  end
51
-
52
- Delayed::Worker.backend = :active_record
53
-
54
- # Add this directory so the ActiveSupport autoloading works
55
- ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
56
-
57
- # Add this to simulate Railtie initializer being executed
58
- ActionMailer::Base.send(:extend, Delayed::DelayMail)
@@ -0,0 +1,13 @@
1
+ require 'spec_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 'should cause the payload object to be reloaded' do
8
+ job = "foo".delay.length
9
+ o = job.payload_object
10
+ o.object_id.should_not == job.reload.payload_object.object_id
11
+ end
12
+ end
13
+ end