delayed_job 4.0.4 → 4.0.5

Sign up to get free protection for your applications and to get access to all the features.
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