delayed_job 4.0.4 → 4.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 458beb66285efe46237b4f094fc7eadbbecf3e79
4
- data.tar.gz: 85e801616a1d8ad319b705386adcd602e3045234
3
+ metadata.gz: 7aeb53385b816641ccbee1ffeb9571f8a9fc277f
4
+ data.tar.gz: 54d26795c4f7f72f5c70e63e8ec0eca4c937def8
5
5
  SHA512:
6
- metadata.gz: e375922727337f1aa87d578715f5679b1edddc3c15f3505cfb8d084ef063eeeace708ee32c85291bff340996662751640c0627babe0e8c0beb3bb55856e83432
7
- data.tar.gz: 9da95592d2f8999d8ddb7365234791a117641bba32edb13361ec177b6a2f4d094a75707be4bdbee0eeacb0f7015fd7e4b072a222d2bb074b95497f0524eee3be
6
+ metadata.gz: 5bacca5b8cdb6ebd37341a7f574f00da50b1d201b6f453840b2123a84bd19c865cba84b090d9f2e85d11f75b233f3e0d4ce484cc674627a9146a1f551f89b933
7
+ data.tar.gz: 85db710c5c82ed12d7d230fbee0be2b3921275c4c174b921620d91491be6d320a7fda7b1335fb8bcfe6a5c878552425d00acfb48d0a1298d62dd7d30f53424f3
@@ -1,3 +1,11 @@
1
+ 4.0.5 - 2014-12-22
2
+ ==================
3
+ * Support for Rails 4.2
4
+ * Allow user to override where DJ writes log output
5
+ * First attempt at automatic code reloading
6
+ * Clearer error message when ActiveRecord object no longer exists
7
+ * Various improvements to the README
8
+
1
9
  4.0.4 - 2014-09-24
2
10
  ==================
3
11
  * Fix using options passed into delayed_job command
data/README.md CHANGED
@@ -58,6 +58,11 @@ running the following command:
58
58
  rails generate delayed_job:active_record
59
59
  rake db:migrate
60
60
 
61
+ Development
62
+ ===========
63
+ In development mode, if you are using Rails 3.1+, your application code will automatically reload every 100 jobs or when the queue finishes.
64
+ You no longer need to restart Delayed Job every time you update your code in development.
65
+
61
66
  Rails 4
62
67
  =======
63
68
  If you are using the protected_attributes gem, it must appear before delayed_job in your gemfile.
@@ -119,10 +124,12 @@ class LongTasks
119
124
  2.hours.from_now
120
125
  end
121
126
 
122
- def call_a_class_method
123
- # Some other code
127
+ class << self
128
+ def call_a_class_method
129
+ # Some other code
130
+ end
131
+ handle_asynchronously :call_a_class_method, :run_at => Proc.new { when_to_run }
124
132
  end
125
- handle_asynchronously :call_a_class_method, :run_at => Proc.new { when_to_run }
126
133
 
127
134
  attr_reader :how_important
128
135
 
@@ -289,6 +296,19 @@ NewsletterJob = Struct.new(:text, :emails) do
289
296
  end
290
297
  ```
291
298
 
299
+ On error, the job is scheduled again in 5 seconds + N ** 4, where N is the number of attempts. You can define your own `reschedule_at` method to override this default behavior.
300
+
301
+ ```ruby
302
+ NewsletterJob = Struct.new(:text, :emails) do
303
+ def perform
304
+ emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
305
+ end
306
+
307
+ def reschedule_at(current_time, attempts)
308
+ current_time + 5.seconds
309
+ end
310
+ end
311
+ ```
292
312
 
293
313
  Hooks
294
314
  =====
@@ -345,7 +365,7 @@ create_table :delayed_jobs, :force => true do |table|
345
365
  end
346
366
  ```
347
367
 
348
- On failure, the job is scheduled again in 5 seconds + N ** 4, where N is the number of retries.
368
+ On error, the job is scheduled again in 5 seconds + N ** 4, where N is the number of attempts or using the job's defined `reschedule_at` method.
349
369
 
350
370
  The default `Worker.max_attempts` is 25. After this, the job either deleted (default), or left in the database with "failed_at" set.
351
371
  With the default of 25 attempts, the last retry will be 20 days later, with the last interval being almost 100 hours.
@@ -378,6 +398,7 @@ Delayed::Worker.read_ahead = 10
378
398
  Delayed::Worker.default_queue_name = 'default'
379
399
  Delayed::Worker.delay_jobs = !Rails.env.test?
380
400
  Delayed::Worker.raise_signal_exceptions = :term
401
+ Delayed::Worker.logger = Logger.new(File.join(Rails.root, 'log', 'delayed_job.log'))
381
402
  ```
382
403
 
383
404
  Cleaning up
@@ -1,15 +1,14 @@
1
1
  Gem::Specification.new do |spec|
2
- spec.add_dependency 'activesupport', ['>= 3.0', '< 4.2']
2
+ spec.add_dependency 'activesupport', ['>= 3.0', '< 5.0']
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 Rakefile delayed_job.gemspec]
7
- spec.files += Dir.glob('{contrib,lib,recipes,spec}/**/*')
6
+ spec.files = %w[CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md delayed_job.gemspec]
7
+ spec.files += Dir['{contrib,lib,recipes}/**/*']
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.test_files = Dir.glob('spec/**/*')
14
- spec.version = '4.0.4'
13
+ spec.version = '4.0.5'
15
14
  end
@@ -34,8 +34,10 @@ module Delayed
34
34
  when /^!ruby\/object/
35
35
  result = super
36
36
  if defined?(ActiveRecord::Base) && result.is_a?(ActiveRecord::Base)
37
+ klass = result.class
38
+ id = result[klass.primary_key]
37
39
  begin
38
- result.class.find(result[result.class.primary_key])
40
+ klass.find(id)
39
41
  rescue ActiveRecord::RecordNotFound => error # rubocop:disable BlockNesting
40
42
  raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass}, primary key: #{id} (#{error.message})"
41
43
  end
@@ -8,7 +8,7 @@ module Delayed
8
8
  ActionMailer::Base.extend(Delayed::DelayMail)
9
9
  end
10
10
 
11
- Delayed::Worker.logger = if defined?(Rails)
11
+ Delayed::Worker.logger ||= if defined?(Rails)
12
12
  Rails.logger
13
13
  elsif defined?(RAILS_DEFAULT_LOGGER)
14
14
  RAILS_DEFAULT_LOGGER
@@ -35,7 +35,5 @@ namespace :jobs do
35
35
  if unprocessed_jobs > 0
36
36
  raise "#{unprocessed_jobs} jobs older than #{args[:max_age]} seconds have not been processed yet"
37
37
  end
38
-
39
38
  end
40
-
41
39
  end
@@ -100,6 +100,10 @@ module Delayed
100
100
  @lifecycle ||= Delayed::Lifecycle.new
101
101
  end
102
102
 
103
+ def self.reload_app?
104
+ defined?(ActionDispatch::Reloader) && Rails.application.config.cache_classes == false
105
+ end
106
+
103
107
  def initialize(options = {})
104
108
  @quiet = options.key?(:quiet) ? options[:quiet] : true
105
109
  @failed_reserve_count = 0
@@ -155,6 +159,7 @@ module Delayed
155
159
  break
156
160
  elsif !stop?
157
161
  sleep(self.class.sleep_delay)
162
+ reload!
158
163
  end
159
164
  else
160
165
  say format("#{count} jobs processed at %.4f j/s, %d failed", count / @realtime, @result.last)
@@ -286,5 +291,11 @@ module Delayed
286
291
  raise FatalBackendError if @failed_reserve_count >= 10
287
292
  nil
288
293
  end
294
+
295
+ def reload!
296
+ return unless self.class.reload_app?
297
+ ActionDispatch::Reloader.cleanup!
298
+ ActionDispatch::Reloader.prepare!
299
+ end
289
300
  end
290
301
  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
4
+ version: 4.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Keepers
@@ -15,7 +15,7 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2014-09-24 00:00:00.000000000 Z
18
+ date: 2014-12-22 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activesupport
@@ -26,7 +26,7 @@ dependencies:
26
26
  version: '3.0'
27
27
  - - "<"
28
28
  - !ruby/object:Gem::Version
29
- version: '4.2'
29
+ version: '5.0'
30
30
  type: :runtime
31
31
  prerelease: false
32
32
  version_requirements: !ruby/object:Gem::Requirement
@@ -36,7 +36,7 @@ dependencies:
36
36
  version: '3.0'
37
37
  - - "<"
38
38
  - !ruby/object:Gem::Version
39
- version: '4.2'
39
+ version: '5.0'
40
40
  description: Delayed_job (or DJ) encapsulates the common pattern of asynchronously
41
41
  executing longer tasks in the background. It is a direct extraction from Shopify
42
42
  where the job table is responsible for a multitude of core tasks.
@@ -50,7 +50,6 @@ files:
50
50
  - CONTRIBUTING.md
51
51
  - LICENSE.md
52
52
  - README.md
53
- - Rakefile
54
53
  - contrib/delayed_job.monitrc
55
54
  - contrib/delayed_job_multiple.monitrc
56
55
  - contrib/delayed_job_rails_4.monitrc
@@ -80,23 +79,6 @@ files:
80
79
  - lib/generators/delayed_job/delayed_job_generator.rb
81
80
  - lib/generators/delayed_job/templates/script
82
81
  - 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
100
82
  homepage: http://github.com/collectiveidea/delayed_job
101
83
  licenses:
102
84
  - MIT
@@ -117,25 +99,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
99
  version: '0'
118
100
  requirements: []
119
101
  rubyforge_project:
120
- rubygems_version: 2.2.2
102
+ rubygems_version: 2.4.4
121
103
  signing_key:
122
104
  specification_version: 4
123
105
  summary: Database-backed asynchronous priority queue system -- Extracted from Shopify
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
106
+ test_files: []
data/Rakefile DELETED
@@ -1,15 +0,0 @@
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]
@@ -1,7 +0,0 @@
1
- # Make sure this file does not get required manually
2
- module Autoloaded
3
- class Clazz
4
- def perform
5
- end
6
- end
7
- end
@@ -1,6 +0,0 @@
1
- module Autoloaded
2
- class InstanceClazz
3
- def perform
4
- end
5
- end
6
- end
@@ -1,6 +0,0 @@
1
- module Autoloaded
2
- class InstanceStruct < ::Struct.new(nil)
3
- def perform
4
- end
5
- end
6
- end
@@ -1,7 +0,0 @@
1
- # Make sure this file does not get required manually
2
- module Autoloaded
3
- class Struct < ::Struct.new(nil)
4
- def perform
5
- end
6
- end
7
- end
@@ -1,117 +0,0 @@
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
@@ -1,57 +0,0 @@
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
@@ -1,85 +0,0 @@
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
@@ -1,75 +0,0 @@
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
@@ -1,122 +0,0 @@
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
@@ -1,44 +0,0 @@
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
-
44
- end
@@ -1,144 +0,0 @@
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
- %w[before after success].each do |hook|
62
- it "delegates #{hook} hook to object" do
63
- story = Story.create
64
- job = story.delay.tell
65
-
66
- expect(story).to receive(hook).with(job)
67
- job.invoke_job
68
- end
69
- end
70
-
71
- it 'delegates enqueue hook to object' do
72
- story = Story.create
73
- expect(story).to receive(:enqueue).with(an_instance_of(Delayed::Job))
74
- story.delay.tell
75
- end
76
-
77
- it 'delegates error hook to object' do
78
- story = Story.create
79
- expect(story).to receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
80
- expect(story).to receive(:tell).and_raise(RuntimeError)
81
- expect { story.delay.tell.invoke_job }.to raise_error
82
- end
83
-
84
- it 'delegates error hook to object when delay_jobs = false' do
85
- story = Story.create
86
- expect(story).to receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
87
- expect(story).to receive(:tell).and_raise(RuntimeError)
88
- expect { story.delay.tell.invoke_job }.to raise_error
89
- end
90
-
91
- it 'delegates failure hook to object' do
92
- method = Delayed::PerformableMethod.new('object', :size, [])
93
- expect(method.object).to receive(:failure)
94
- method.failure
95
- end
96
-
97
- context 'with delay_job == false' do
98
- before do
99
- Delayed::Worker.delay_jobs = false
100
- end
101
-
102
- after do
103
- Delayed::Worker.delay_jobs = true
104
- end
105
-
106
- %w[before after success].each do |hook|
107
- it "delegates #{hook} hook to object" do
108
- story = Story.create
109
- expect(story).to receive(hook).with(an_instance_of(Delayed::Job))
110
- story.delay.tell
111
- end
112
- end
113
-
114
- %w[before after success].each do |hook|
115
- it "delegates #{hook} hook to object" do
116
- story = Story.create
117
- expect(story).to receive(hook).with(an_instance_of(Delayed::Job))
118
- story.delay.tell
119
- end
120
- end
121
-
122
- it 'delegates error hook to object' do
123
- story = Story.create
124
- expect(story).to receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
125
- expect(story).to receive(:tell).and_raise(RuntimeError)
126
- expect { story.delay.tell }.to raise_error
127
- end
128
-
129
- it 'delegates error hook to object when delay_jobs = false' do
130
- story = Story.create
131
- expect(story).to receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
132
- expect(story).to receive(:tell).and_raise(RuntimeError)
133
- expect { story.delay.tell }.to raise_error
134
- end
135
-
136
- it 'delegates failure hook to object when delay_jobs = false' do
137
- Delayed::Worker.delay_jobs = false
138
- method = Delayed::PerformableMethod.new('object', :size, [])
139
- expect(method.object).to receive(:failure)
140
- method.failure
141
- end
142
- end
143
- end
144
- end
@@ -1,12 +0,0 @@
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
@@ -1,109 +0,0 @@
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
@@ -1,13 +0,0 @@
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
@@ -1,157 +0,0 @@
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
@@ -1,48 +0,0 @@
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