Hello community,

here is the log from the commit of package rubygem-delayed_job for 
openSUSE:Factory checked in at 2015-09-27 08:40:16
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-delayed_job (Old)
 and      /work/SRC/openSUSE:Factory/.rubygem-delayed_job.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rubygem-delayed_job"

Changes:
--------
--- /work/SRC/openSUSE:Factory/rubygem-delayed_job/rubygem-delayed_job.changes  
2015-02-11 16:45:19.000000000 +0100
+++ 
/work/SRC/openSUSE:Factory/.rubygem-delayed_job.new/rubygem-delayed_job.changes 
    2015-09-27 08:39:42.000000000 +0200
@@ -1,0 +2,26 @@
+Fri Sep 25 04:28:48 UTC 2015 - co...@suse.com
+
+- updated to version 4.1.1
+ see installed CHANGELOG.md
+
+  4.1.1 - 2015-09-24
+  ==================
+  * Fix shared specs for back-ends that reload objects
+
+-------------------------------------------------------------------
+Wed Sep 23 04:29:11 UTC 2015 - co...@suse.com
+
+- updated to version 4.1.0
+ see installed CHANGELOG.md
+
+  4.1.0 - 2015-09-22
+  ==================
+  * Alter `Delayed::Command` to work with or without Rails
+  * Allow `Delayed::Worker.delay_jobs` configuration to be a proc
+  * Add ability to set destroy failed jobs on a per job basis
+  * Make `Delayed::Worker.new` idempotent
+  * Set quiet from the environment
+  * Rescue `Exception` instead of `StandardError` in worker
+  * Fix worker crash on serialization error
+
+-------------------------------------------------------------------

Old:
----
  delayed_job-4.0.6.gem

New:
----
  delayed_job-4.1.1.gem

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rubygem-delayed_job.spec ++++++
--- /var/tmp/diff_new_pack.uv6hMd/_old  2015-09-27 08:39:42.000000000 +0200
+++ /var/tmp/diff_new_pack.uv6hMd/_new  2015-09-27 08:39:42.000000000 +0200
@@ -24,7 +24,7 @@
 #
 
 Name:           rubygem-delayed_job
-Version:        4.0.6
+Version:        4.1.1
 Release:        0
 %define mod_name delayed_job
 %define mod_full_name %{mod_name}-%{version}

++++++ delayed_job-4.0.6.gem -> delayed_job-4.1.1.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/CHANGELOG.md new/CHANGELOG.md
--- old/CHANGELOG.md    2014-12-22 14:22:31.000000000 +0100
+++ new/CHANGELOG.md    2015-09-24 23:40:11.000000000 +0200
@@ -1,3 +1,17 @@
+4.1.1 - 2015-09-24
+==================
+* Fix shared specs for back-ends that reload objects
+
+4.1.0 - 2015-09-22
+==================
+* Alter `Delayed::Command` to work with or without Rails
+* Allow `Delayed::Worker.delay_jobs` configuration to be a proc
+* Add ability to set destroy failed jobs on a per job basis
+* Make `Delayed::Worker.new` idempotent
+* Set quiet from the environment
+* Rescue `Exception` instead of `StandardError` in worker
+* Fix worker crash on serialization error
+
 4.0.6 - 2014-12-22
 ==================
 * Revert removing test files from the gem
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/README.md new/README.md
--- old/README.md       2014-12-22 14:22:31.000000000 +0100
+++ new/README.md       2015-09-24 23:40:11.000000000 +0200
@@ -1,3 +1,8 @@
+**If you're viewing this at https://github.com/collectiveidea/delayed_job,
+you're reading the documentation for the master branch.
+[View documentation for the latest release
+(4.0.6).](https://github.com/collectiveidea/delayed_job/tree/v4.0.6)**
+
 Delayed::Job
 ============
 [![Gem Version](https://badge.fury.io/rb/delayed_job.png)][gem]
@@ -58,14 +63,30 @@
     rails generate delayed_job:active_record
     rake db:migrate
 
+For Rails 4.2, see [below](#rails-42)
+
 Development
 ===========
 In development mode, if you are using Rails 3.1+, your application code will 
automatically reload every 100 jobs or when the queue finishes.
 You no longer need to restart Delayed Job every time you update your code in 
development.
 
-Rails 4
-=======
-If you are using the protected_attributes gem, it must appear before 
delayed_job in your gemfile.
+Rails 4.2
+=========
+Set the queue_adapter in config/application.rb
+
+```ruby
+config.active_job.queue_adapter = :delayed_job
+```
+
+See the [rails 
guide](http://guides.rubyonrails.org/active_job_basics.html#setting-the-backend)
 for more details.
+
+Rails 4.x
+=========
+If you are using the protected_attributes gem, it must appear before 
delayed_job in your gemfile. If your jobs are failing with:
+
+     ActiveRecord::StatementInvalid: PG::NotNullViolation: ERROR:  null value 
in column "handler" violates not-null constraint
+
+then this is the fix you're looking for.
 
 Upgrading from 2.x to 3.0.0 on Active Record
 ============================================
@@ -103,9 +124,17 @@
 device.deliver
 ```
 
-handle_asynchronously can take as options anything you can pass to delay. In
-addition, the values can be Proc objects allowing call time evaluation of the
-value. For some examples:
+## Parameters
+
+`#handle_asynchronously` and `#delay` take these parameters:
+
+- `:priority` (number): lower numbers run first; default is 0 but can be 
reconfigured (see below)
+- `:run_at` (Time): run the job after this time (probably in the future)
+- `:queue` (string): named queue to put this job in, an alternative to 
priorities (see below)
+
+These params can be Proc objects, allowing call-time evaluation of the value.
+
+For example:
 
 ```ruby
 class LongTasks
@@ -282,6 +311,20 @@
 end
 ```
 
+To set a per-job default for destroying failed jobs that overrides the 
Delayed::Worker.destroy_failed_jobs you can define a destroy_failed_jobs? 
method on the job
+
+```ruby
+NewsletterJob = Struct.new(:text, :emails) do
+  def perform
+    emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
+  end
+
+  def destroy_failed_jobs?
+    false
+  end
+end
+```
+
 To set a default queue name for a custom job that overrides 
Delayed::Worker.default_queue_name, you can define a queue_name method on the 
job
 
 ```ruby
@@ -384,6 +427,14 @@
 
 It is possible to disable delayed jobs for testing purposes. Set 
`Delayed::Worker.delay_jobs = false` to execute all jobs realtime.
 
+Or `Delayed::Worker.delay_jobs` can be a Proc that decides whether to execute 
jobs inline on a per-job basis:
+
+```ruby
+Delayed::Worker.delay_jobs = ->(job) {
+  job.queue != 'inline'
+}
+```
+
 You may need to raise exceptions on SIGTERM signals, 
`Delayed::Worker.raise_signal_exceptions = :term` will cause the worker to 
raise a `SignalException` causing the running job to abort and be unlocked, 
which makes the job available to other workers. The default for this option is 
false.
 
 Here is an example of changing job parameters in Rails:
Files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/delayed_job.gemspec new/delayed_job.gemspec
--- old/delayed_job.gemspec     2014-12-22 14:22:31.000000000 +0100
+++ new/delayed_job.gemspec     2015-09-24 23:40:11.000000000 +0200
@@ -4,12 +4,12 @@
   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.'
   spec.email          = ['br...@collectiveidea.com']
   spec.files          = %w[CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md 
Rakefile delayed_job.gemspec]
-  spec.files         += Dir.glob('{contrib,lib,recipes,spec}/**/*')
+  spec.files += Dir.glob('{contrib,lib,recipes,spec}/**/*')
   spec.homepage       = 'http://github.com/collectiveidea/delayed_job'
   spec.licenses       = ['MIT']
   spec.name           = 'delayed_job'
   spec.require_paths  = ['lib']
   spec.summary        = 'Database-backed asynchronous priority queue system -- 
Extracted from Shopify'
   spec.test_files     = Dir.glob('spec/**/*')
-  spec.version        = '4.0.6'
+  spec.version        = '4.1.1'
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/delayed/backend/base.rb 
new/lib/delayed/backend/base.rb
--- old/lib/delayed/backend/base.rb     2014-12-22 14:22:31.000000000 +0100
+++ new/lib/delayed/backend/base.rb     2015-09-24 23:40:11.000000000 +0200
@@ -10,7 +10,7 @@
         def enqueue(*args) # rubocop:disable CyclomaticComplexity
           options = args.extract_options!
           options[:payload_object] ||= args.shift
-          options[:priority]       ||= Delayed::Worker.default_priority
+          options[:priority] ||= Delayed::Worker.default_priority
 
           if options[:queue].nil?
             if options[:payload_object].respond_to?(:queue_name)
@@ -32,7 +32,7 @@
           new(options).tap do |job|
             Delayed::Worker.lifecycle.run_callbacks(:enqueue, job) do
               job.hook(:enqueue)
-              Delayed::Worker.delay_jobs ? job.save : job.invoke_job
+              Delayed::Worker.delay_job?(job) ? job.save : job.invoke_job
             end
           end
         end
@@ -63,6 +63,12 @@
         end
       end
 
+      attr_reader :error
+      def error=(error)
+        @error = error
+        self.last_error = "#{error.message}\n#{error.backtrace.join("\n")}" if 
self.respond_to?(:last_error=)
+      end
+
       def failed?
         !!failed_at
       end
@@ -93,7 +99,7 @@
             hook :before
             payload_object.perform
             hook :success
-          rescue => e
+          rescue Exception => e # rubocop:disable RescueException
             hook :error, e
             raise e
           ensure
@@ -139,6 +145,12 @@
         end
       end
 
+      def destroy_failed_jobs?
+        payload_object.respond_to?(:destroy_failed_jobs?) ? 
payload_object.destroy_failed_jobs? : Delayed::Worker.destroy_failed_jobs
+      rescue DeserializationError
+        Delayed::Worker.destroy_failed_jobs
+      end
+
       def fail!
         update_attributes(:failed_at => self.class.db_time_now)
       end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/delayed/backend/shared_spec.rb 
new/lib/delayed/backend/shared_spec.rb
--- old/lib/delayed/backend/shared_spec.rb      2014-12-22 14:22:31.000000000 
+0100
+++ new/lib/delayed/backend/shared_spec.rb      2015-09-24 23:40:11.000000000 
+0200
@@ -161,7 +161,7 @@
       job = described_class.enqueue(CallbackJob.new)
       expect(job.payload_object).to 
receive(:perform).and_raise(RuntimeError.new('fail'))
 
-      expect { job.invoke_job }.to raise_error
+      expect { job.invoke_job }.to raise_error(RuntimeError)
       expect(CallbackJob.messages).to eq(['enqueue', 'before', 'error: 
RuntimeError', 'after'])
     end
 
@@ -450,6 +450,34 @@
     end
   end
 
+  describe 'destroy_failed_jobs' do
+    context 'with a SimpleJob' do
+      before(:each) do
+        @job = described_class.enqueue SimpleJob.new
+      end
+
+      it 'is not defined' do
+        expect(@job.destroy_failed_jobs?).to be true
+      end
+
+      it 'uses the destroy failed jobs value on the payload when defined' do
+        expect(@job.payload_object).to 
receive(:destroy_failed_jobs?).and_return(false)
+        expect(@job.destroy_failed_jobs?).to be false
+      end
+    end
+
+    context 'with a job that raises DserializationError' do
+      before(:each) do
+        @job = described_class.new :handler => '--- 
!ruby/struct:GoingToRaiseArgError {}'
+      end
+
+      it 'falls back reasonably' do
+        expect(YAML).to receive(:load_dj).and_raise(ArgumentError)
+        expect(@job.destroy_failed_jobs?).to be true
+      end
+    end
+  end
+
   describe 'yaml serialization' do
     context 'when serializing jobs' do
       it 'raises error ArgumentError for new records' do
@@ -500,6 +528,7 @@
         Delayed::Worker.max_run_time = 1.second
         job = Delayed::Job.create :payload_object => LongRunningJob.new
         worker.run(job)
+        expect(job.error).to_not be_nil
         expect(job.reload.last_error).to match(/expired/)
         expect(job.reload.last_error).to match(/Delayed::Worker\.max_run_time 
is only 1 second/)
         expect(job.attempts).to eq(1)
@@ -513,6 +542,7 @@
         it 'marks the job as failed' do
           Delayed::Worker.destroy_failed_jobs = false
           job = described_class.create! :handler => '--- 
!ruby/object:JobThatDoesNotExist {}'
+          expect_any_instance_of(described_class).to 
receive(:destroy_failed_jobs?).and_return(false)
           worker.work_off
           job.reload
           expect(job).to be_failed
@@ -535,6 +565,7 @@
         Delayed::Worker.max_attempts = 1
         worker.run(@job)
         @job.reload
+        expect(@job.error).to_not be_nil
         expect(@job.last_error).to match(/did not work/)
         expect(@job.attempts).to eq(1)
         expect(@job).to be_failed
@@ -617,6 +648,10 @@
       end
 
       context 'and we want to destroy jobs' do
+        after do
+          Delayed::Worker.destroy_failed_jobs = true
+        end
+
         it_behaves_like 'any failure more than Worker.max_attempts times'
 
         it 'is destroyed if it failed more than Worker.max_attempts times' do
@@ -624,6 +659,13 @@
           Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
         end
 
+        it 'is destroyed if the job has destroy failed jobs set' do
+          Delayed::Worker.destroy_failed_jobs = false
+          expect(@job).to receive(:destroy_failed_jobs?).and_return(true)
+          expect(@job).to receive(:destroy)
+          Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
+        end
+
         it 'is not destroyed if failed fewer than Worker.max_attempts times' do
           expect(@job).not_to receive(:destroy)
           (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
@@ -641,15 +683,35 @@
 
         it_behaves_like 'any failure more than Worker.max_attempts times'
 
-        it 'is failed if it failed more than Worker.max_attempts times' do
-          expect(@job.reload).not_to be_failed
-          Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
-          expect(@job.reload).to be_failed
+        context 'and destroy failed jobs is false' do
+          it 'is failed if it failed more than Worker.max_attempts times' do
+            expect(@job.reload).not_to be_failed
+            Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
+            expect(@job.reload).to be_failed
+          end
+
+          it 'is not failed if it failed fewer than Worker.max_attempts times' 
do
+            (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) 
}
+            expect(@job.reload).not_to be_failed
+          end
         end
 
-        it 'is not failed if it failed fewer than Worker.max_attempts times' do
-          (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
-          expect(@job.reload).not_to be_failed
+        context 'and destroy failed jobs for job is false' do
+          before do
+            Delayed::Worker.destroy_failed_jobs = true
+          end
+
+          it 'is failed if it failed more than Worker.max_attempts times' do
+            expect(@job).to receive(:destroy_failed_jobs?).and_return(false)
+            expect(@job.reload).not_to be_failed
+            Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
+            expect(@job.reload).to be_failed
+          end
+
+          it 'is not failed if it failed fewer than Worker.max_attempts times' 
do
+            (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) 
}
+            expect(@job.reload).not_to be_failed
+          end
         end
       end
     end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/delayed/command.rb new/lib/delayed/command.rb
--- old/lib/delayed/command.rb  2014-12-22 14:22:31.000000000 +0100
+++ new/lib/delayed/command.rb  2015-09-24 23:40:11.000000000 +0200
@@ -6,15 +6,19 @@
   end
 end
 require 'optparse'
+require 'pathname'
 
 module Delayed
   class Command # rubocop:disable ClassLength
     attr_accessor :worker_count, :worker_pools
 
+    DIR_PWD = Pathname.new Dir.pwd
+
     def initialize(args) # rubocop:disable MethodLength
       @options = {
         :quiet => true,
-        :pid_dir => "#{Rails.root}/tmp/pids"
+        :pid_dir => "#{root}/tmp/pids",
+        :log_dir => "#{root}/log"
       }
 
       @worker_count = 1
@@ -37,11 +41,14 @@
           @options[:max_priority] = n
         end
         opt.on('-n', '--number_of_workers=workers', 'Number of unique workers 
to spawn') do |worker_count|
-          @worker_count = worker_count.to_i rescue 1 # rubocop:disable 
RescueModifier
+          @worker_count = worker_count.to_i rescue 1
         end
         opt.on('--pid-dir=DIR', 'Specifies an alternate directory in which to 
store the process ids.') do |dir|
           @options[:pid_dir] = dir
         end
+        opt.on('--log-dir=DIR', 'Specifies an alternate directory in which to 
store the delayed_job log.') do |dir|
+          @options[:log_dir] = dir
+        end
         opt.on('-i', '--identifier=n', 'A numeric identifier for the worker.') 
do |n|
           @options[:identifier] = n
         end
@@ -114,18 +121,19 @@
     end
 
     def run(worker_name = nil, options = {})
-      Dir.chdir(Rails.root)
+      Dir.chdir(root)
 
       Delayed::Worker.after_fork
-      Delayed::Worker.logger ||= Logger.new(File.join(Rails.root, 'log', 
'delayed_job.log'))
+      Delayed::Worker.logger ||= Logger.new(File.join(@options[:log_dir], 
'delayed_job.log'))
 
       worker = Delayed::Worker.new(options)
       worker.name_prefix = "#{worker_name} "
       worker.start
     rescue => e
-      Rails.logger.fatal e
       STDERR.puts e.message
-      exit 1
+      STDERR.puts e.backtrace
+      ::Rails.logger.fatal(e) if rails_logger_defined?
+      exit_with_error_status
     end
 
   private
@@ -142,5 +150,21 @@
       worker_count = (worker_count || 1).to_i rescue 1
       @worker_pools << [queues, worker_count]
     end
+
+    def root
+      @root ||= rails_root_defined? ? ::Rails.root : DIR_PWD
+    end
+
+    def rails_root_defined?
+      defined?(::Rails.root)
+    end
+
+    def rails_logger_defined?
+      defined?(::Rails.logger)
+    end
+
+    def exit_with_error_status
+      exit 1
+    end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/delayed/message_sending.rb 
new/lib/delayed/message_sending.rb
--- old/lib/delayed/message_sending.rb  2014-12-22 14:22:31.000000000 +0100
+++ new/lib/delayed/message_sending.rb  2015-09-24 23:40:11.000000000 +0200
@@ -31,8 +31,10 @@
 
     module ClassMethods
       def handle_asynchronously(method, opts = {})
-        aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1 # 
rubocop:disable PerlBackrefs
-        with_method, without_method = 
"#{aliased_method}_with_delay#{punctuation}", 
"#{aliased_method}_without_delay#{punctuation}"
+        aliased_method = method.to_s.sub(/([?!=])$/, '')
+        punctuation = $1 # rubocop:disable PerlBackrefs
+        with_method = "#{aliased_method}_with_delay#{punctuation}"
+        without_method = "#{aliased_method}_without_delay#{punctuation}"
         define_method(with_method) do |*args|
           curr_opts = opts.clone
           curr_opts.each_key do |key|
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/delayed/recipes.rb new/lib/delayed/recipes.rb
--- old/lib/delayed/recipes.rb  2014-12-22 14:22:31.000000000 +0100
+++ new/lib/delayed/recipes.rb  2015-09-24 23:40:11.000000000 +0200
@@ -38,17 +38,17 @@
 
     desc 'Stop the delayed_job process'
     task :stop, :roles => lambda { roles } do
-      run "cd #{current_path};#{rails_env} #{delayed_job_command} stop #{args}"
+      run "cd #{current_path} && #{rails_env} #{delayed_job_command} stop 
#{args}"
     end
 
     desc 'Start the delayed_job process'
     task :start, :roles => lambda { roles } do
-      run "cd #{current_path};#{rails_env} #{delayed_job_command} start 
#{args}"
+      run "cd #{current_path} && #{rails_env} #{delayed_job_command} start 
#{args}"
     end
 
     desc 'Restart the delayed_job process'
     task :restart, :roles => lambda { roles } do
-      run "cd #{current_path};#{rails_env} #{delayed_job_command} restart 
#{args}"
+      run "cd #{current_path} && #{rails_env} #{delayed_job_command} restart 
#{args}"
     end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/delayed/syck_ext.rb new/lib/delayed/syck_ext.rb
--- old/lib/delayed/syck_ext.rb 2014-12-22 14:22:31.000000000 +0100
+++ new/lib/delayed/syck_ext.rb 2015-09-24 23:40:11.000000000 +0200
@@ -29,7 +29,7 @@
     # Constantize the object so that ActiveSupport can attempt
     # its auto loading magic. Will raise LoadError if not successful.
     name.constantize
-    "Struct::#{ name }"
+    "Struct::#{name}"
   end
 end
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/delayed/tasks.rb new/lib/delayed/tasks.rb
--- old/lib/delayed/tasks.rb    2014-12-22 14:22:31.000000000 +0100
+++ new/lib/delayed/tasks.rb    2015-09-24 23:40:11.000000000 +0200
@@ -19,7 +19,7 @@
       :min_priority => ENV['MIN_PRIORITY'],
       :max_priority => ENV['MAX_PRIORITY'],
       :queues => (ENV['QUEUES'] || ENV['QUEUE'] || '').split(','),
-      :quiet => false
+      :quiet => ENV['QUIET']
     }
 
     @worker_options[:sleep_delay] = ENV['SLEEP_DELAY'].to_i if 
ENV['SLEEP_DELAY']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/delayed/worker.rb new/lib/delayed/worker.rb
--- old/lib/delayed/worker.rb   2014-12-22 14:22:31.000000000 +0100
+++ new/lib/delayed/worker.rb   2015-09-24 23:40:11.000000000 +0200
@@ -39,6 +39,7 @@
       self.delay_jobs        = DEFAULT_DELAY_JOBS
       self.queues            = DEFAULT_QUEUES
       self.read_ahead        = DEFAULT_READ_AHEAD
+      @lifecycle             = nil
     end
 
     reset
@@ -97,13 +98,29 @@
     end
 
     def self.lifecycle
-      @lifecycle ||= Delayed::Lifecycle.new
+      # In case a worker has not been set up, job enqueueing needs a lifecycle.
+      setup_lifecycle unless @lifecycle
+
+      @lifecycle
+    end
+
+    def self.setup_lifecycle
+      @lifecycle = Delayed::Lifecycle.new
+      plugins.each { |klass| klass.new }
     end
 
     def self.reload_app?
       defined?(ActionDispatch::Reloader) && 
Rails.application.config.cache_classes == false
     end
 
+    def self.delay_job?(job)
+      if delay_jobs.is_a?(Proc)
+        delay_jobs.arity == 1 ? delay_jobs.call(job) : delay_jobs.call
+      else
+        delay_jobs
+      end
+    end
+
     def initialize(options = {})
       @quiet = options.key?(:quiet) ? options[:quiet] : true
       @failed_reserve_count = 0
@@ -112,7 +129,9 @@
         self.class.send("#{option}=", options[option]) if options.key?(option)
       end
 
-      plugins.each { |klass| klass.new }
+      # Reset lifecycle on the offhand chance that something lazily
+      # triggered its creation before all plugins had been registered.
+      self.class.setup_lifecycle
     end
 
     # Every worker has a unique name which by default is the pid of the 
process. There are some
@@ -121,7 +140,7 @@
     # it crashed before.
     def name
       return @name unless @name.nil?
-      "#{@name_prefix}host:#{Socket.gethostname} pid:#{Process.pid}" rescue 
"#{@name_prefix}pid:#{Process.pid}" # rubocop:disable RescueModifier
+      "#{@name_prefix}host:#{Socket.gethostname} pid:#{Process.pid}" rescue 
"#{@name_prefix}pid:#{Process.pid}"
     end
 
     # Sets the name of the worker.
@@ -181,7 +200,8 @@
     # Do num jobs and return stats on success/failure.
     # Exit early if interrupted.
     def work_off(num = 100)
-      success, failure = 0, 0
+      success = 0
+      failure = 0
 
       num.times do
         case reserve_and_run_one_job
@@ -190,7 +210,7 @@
         when false
           failure += 1
         else
-          break  # leave if no work could be done
+          break # leave if no work could be done
         end
         break if stop? # leave if we're exiting
       end
@@ -200,18 +220,18 @@
 
     def run(job)
       job_say job, 'RUNNING'
-      runtime =  Benchmark.realtime do
+      runtime = Benchmark.realtime do
         Timeout.timeout(max_run_time(job).to_i, WorkerTimeout) { 
job.invoke_job }
         job.destroy
       end
       job_say job, format('COMPLETED after %.4f', runtime)
-      return true  # did work
+      return true # did work
     rescue DeserializationError => error
-      job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
+      job.error = error
       failed(job)
-    rescue => error
+    rescue Exception => error # rubocop:disable RescueException
       self.class.lifecycle.run_callbacks(:error, self, job) { 
handle_failed_job(job, error) }
-      return false  # work failed
+      return false # work failed
     end
 
     # Reschedule the job in the future (when a job fails).
@@ -236,7 +256,7 @@
           say "Error when running failure callback: #{error}", 'error'
           say error.backtrace.join("\n"), 'error'
         ensure
-          self.class.destroy_failed_jobs ? job.destroy : job.fail!
+          job.destroy_failed_jobs? ? job.destroy : job.fail!
         end
       end
     end
@@ -268,7 +288,7 @@
   protected
 
     def handle_failed_job(job, error)
-      job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
+      job.error = error
       job_say job, "FAILED (#{job.attempts} prior attempts) with 
#{error.class.name}: #{error.message}", 'error'
       reschedule(job)
     end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata        2014-12-22 14:22:31.000000000 +0100
+++ new/metadata        2015-09-24 23:40:11.000000000 +0200
@@ -1,7 +1,7 @@
 --- !ruby/object:Gem::Specification
 name: delayed_job
 version: !ruby/object:Gem::Version
-  version: 4.0.6
+  version: 4.1.1
 platform: ruby
 authors:
 - Brandon Keepers
@@ -15,7 +15,7 @@
 autorequire: 
 bindir: bin
 cert_chain: []
-date: 2014-12-22 00:00:00.000000000 Z
+date: 2015-09-24 00:00:00.000000000 Z
 dependencies:
 - !ruby/object:Gem::Dependency
   name: activesupport
@@ -84,6 +84,7 @@
 - spec/autoloaded/instance_clazz.rb
 - spec/autoloaded/instance_struct.rb
 - spec/autoloaded/struct.rb
+- spec/daemons.rb
 - spec/delayed/backend/test.rb
 - spec/delayed/command_spec.rb
 - spec/delayed/serialization/test.rb
@@ -117,7 +118,7 @@
       version: '0'
 requirements: []
 rubyforge_project: 
-rubygems_version: 2.4.4
+rubygems_version: 2.4.8
 signing_key: 
 specification_version: 4
 summary: Database-backed asynchronous priority queue system -- Extracted from 
Shopify
@@ -126,6 +127,7 @@
 - spec/autoloaded/instance_clazz.rb
 - spec/autoloaded/instance_struct.rb
 - spec/autoloaded/struct.rb
+- spec/daemons.rb
 - spec/delayed/backend/test.rb
 - spec/delayed/command_spec.rb
 - spec/delayed/serialization/test.rb
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/autoloaded/instance_struct.rb 
new/spec/autoloaded/instance_struct.rb
--- old/spec/autoloaded/instance_struct.rb      2014-12-22 14:22:31.000000000 
+0100
+++ new/spec/autoloaded/instance_struct.rb      2015-09-24 23:40:11.000000000 
+0200
@@ -1,5 +1,6 @@
 module Autoloaded
-  class InstanceStruct < ::Struct.new(nil)
+  InstanceStruct = ::Struct.new(nil)
+  class InstanceStruct
     def perform
     end
   end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/autoloaded/struct.rb 
new/spec/autoloaded/struct.rb
--- old/spec/autoloaded/struct.rb       2014-12-22 14:22:31.000000000 +0100
+++ new/spec/autoloaded/struct.rb       2015-09-24 23:40:11.000000000 +0200
@@ -1,6 +1,7 @@
 # Make sure this file does not get required manually
 module Autoloaded
-  class Struct < ::Struct.new(nil)
+  Struct = ::Struct.new(nil)
+  class Struct
     def perform
     end
   end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/daemons.rb new/spec/daemons.rb
--- old/spec/daemons.rb 1970-01-01 01:00:00.000000000 +0100
+++ new/spec/daemons.rb 2015-09-24 23:40:11.000000000 +0200
@@ -0,0 +1,2 @@
+# Fake "daemons" file on the spec load path to allow 
spec/delayed/command_spec.rb
+# to test the Delayed::Command class without actually adding daemons as a 
dependency.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/delayed/command_spec.rb 
new/spec/delayed/command_spec.rb
--- old/spec/delayed/command_spec.rb    2014-12-22 14:22:31.000000000 +0100
+++ new/spec/delayed/command_spec.rb    2015-09-24 23:40:11.000000000 +0200
@@ -2,6 +2,129 @@
 require 'delayed/command'
 
 describe Delayed::Command do
+  let(:options) { [] }
+  let(:logger) { double('Logger') }
+
+  subject { Delayed::Command.new options }
+
+  before do
+    allow(Delayed::Worker).to receive(:after_fork)
+    allow(Dir).to receive(:chdir)
+    allow(Logger).to receive(:new).and_return(logger)
+    allow_any_instance_of(Delayed::Worker).to receive(:start)
+    allow(Delayed::Worker).to receive(:logger=)
+    allow(Delayed::Worker).to receive(:logger).and_return(nil, logger)
+  end
+
+  shared_examples_for 'uses --log-dir option' do
+    context 'when --log-dir is specified' do
+      let(:options) { ['--log-dir=/custom/log/dir'] }
+
+      it 'creates the delayed_job.log in the specified directory' do
+        expect(Logger).to receive(:new).with('/custom/log/dir/delayed_job.log')
+        subject.run
+      end
+    end
+  end
+
+  describe 'run' do
+    it 'sets the Delayed::Worker logger' do
+      expect(Delayed::Worker).to receive(:logger=).with(logger)
+      subject.run
+    end
+
+    context 'when Rails root is defined' do
+      let(:rails_root) { Pathname.new '/rails/root' }
+      let(:rails) { double('Rails', :root => rails_root) }
+
+      before do
+        stub_const('Rails', rails)
+      end
+
+      it 'runs the Delayed::Worker process in Rails.root' do
+        expect(Dir).to receive(:chdir).with(rails_root)
+        subject.run
+      end
+
+      context 'when --log-dir is not specified' do
+        it 'creates the delayed_job.log in Rails.root/log' do
+          expect(Logger).to 
receive(:new).with('/rails/root/log/delayed_job.log')
+          subject.run
+        end
+      end
+
+      include_examples 'uses --log-dir option'
+    end
+
+    context 'when Rails root is not defined' do
+      let(:rails_without_root) { double('Rails') }
+
+      before do
+        stub_const('Rails', rails_without_root)
+      end
+
+      it 'runs the Delayed::Worker process in $PWD' do
+        expect(Dir).to receive(:chdir).with(Delayed::Command::DIR_PWD)
+        subject.run
+      end
+
+      context 'when --log-dir is not specified' do
+        it 'creates the delayed_job.log in $PWD/log' do
+          expect(Logger).to 
receive(:new).with("#{Delayed::Command::DIR_PWD}/log/delayed_job.log")
+          subject.run
+        end
+      end
+
+      include_examples 'uses --log-dir option'
+    end
+
+    context 'when an error is raised' do
+      let(:test_error) { Class.new(StandardError) }
+
+      before do
+        allow(Delayed::Worker).to receive(:new).and_raise(test_error.new('An 
error'))
+        allow(subject).to receive(:exit_with_error_status)
+        allow(STDERR).to receive(:puts)
+      end
+
+      it 'prints the error message to STDERR' do
+        expect(STDERR).to receive(:puts).with('An error')
+        subject.run
+      end
+
+      it 'exits with an error status' do
+        expect(subject).to receive(:exit_with_error_status)
+        subject.run
+      end
+
+      context 'when Rails logger is not defined' do
+        let(:rails) { double('Rails') }
+
+        before do
+          stub_const('Rails', rails)
+        end
+
+        it 'does not attempt to use the Rails logger' do
+          subject.run
+        end
+      end
+
+      context 'when Rails logger is defined' do
+        let(:rails_logger) { double('Rails logger') }
+        let(:rails) { double('Rails', :logger => rails_logger) }
+
+        before do
+          stub_const('Rails', rails)
+        end
+
+        it 'logs the error to the Rails logger' do
+          expect(rails_logger).to receive(:fatal).with(test_error)
+          subject.run
+        end
+      end
+    end
+  end
+
   describe 'parsing --pool argument' do
     it 'should parse --pool correctly' do
       command = Delayed::Command.new(['--pool=*:1', '--pool=test_queue:4', 
'--pool=mailers,misc:2'])
@@ -40,13 +163,13 @@
       expect(Dir).to receive(:mkdir).with('./tmp/pids').once
 
       [
-        ['delayed_job.0', {:quiet => true, :pid_dir => './tmp/pids', :queues 
=> []}],
-        ['delayed_job.1', {:quiet => true, :pid_dir => './tmp/pids', :queues 
=> ['test_queue']}],
-        ['delayed_job.2', {:quiet => true, :pid_dir => './tmp/pids', :queues 
=> ['test_queue']}],
-        ['delayed_job.3', {:quiet => true, :pid_dir => './tmp/pids', :queues 
=> ['test_queue']}],
-        ['delayed_job.4', {:quiet => true, :pid_dir => './tmp/pids', :queues 
=> ['test_queue']}],
-        ['delayed_job.5', {:quiet => true, :pid_dir => './tmp/pids', :queues 
=> %w[mailers misc]}],
-        ['delayed_job.6', {:quiet => true, :pid_dir => './tmp/pids', :queues 
=> %w[mailers misc]}]
+        ['delayed_job.0', {:quiet => true, :pid_dir => './tmp/pids', :log_dir 
=> './log', :queues => []}],
+        ['delayed_job.1', {:quiet => true, :pid_dir => './tmp/pids', :log_dir 
=> './log', :queues => ['test_queue']}],
+        ['delayed_job.2', {:quiet => true, :pid_dir => './tmp/pids', :log_dir 
=> './log', :queues => ['test_queue']}],
+        ['delayed_job.3', {:quiet => true, :pid_dir => './tmp/pids', :log_dir 
=> './log', :queues => ['test_queue']}],
+        ['delayed_job.4', {:quiet => true, :pid_dir => './tmp/pids', :log_dir 
=> './log', :queues => ['test_queue']}],
+        ['delayed_job.5', {:quiet => true, :pid_dir => './tmp/pids', :log_dir 
=> './log', :queues => %w[mailers misc]}],
+        ['delayed_job.6', {:quiet => true, :pid_dir => './tmp/pids', :log_dir 
=> './log', :queues => %w[mailers misc]}]
       ].each do |args|
         expect(command).to receive(:run_process).with(*args).once
       end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/message_sending_spec.rb 
new/spec/message_sending_spec.rb
--- old/spec/message_sending_spec.rb    2014-12-22 14:22:31.000000000 +0100
+++ new/spec/message_sending_spec.rb    2015-09-24 23:40:11.000000000 +0200
@@ -62,6 +62,7 @@
     class FairyTail
       attr_accessor :happy_ending
       def self.princesses; end
+
       def tell
         @happy_ending = true
       end
@@ -118,5 +119,25 @@
         end.not_to change(fairy_tail, :happy_ending)
       end.to change { Delayed::Job.count }.by(1)
     end
+
+    it 'does delay when delay_jobs is a proc returning true' do
+      Delayed::Worker.delay_jobs = ->(_job) { true }
+      fairy_tail = FairyTail.new
+      expect do
+        expect do
+          fairy_tail.delay.tell
+        end.not_to change(fairy_tail, :happy_ending)
+      end.to change { Delayed::Job.count }.by(1)
+    end
+
+    it 'does not delay the job when delay_jobs is a proc returning false' do
+      Delayed::Worker.delay_jobs = ->(_job) { false }
+      fairy_tail = FairyTail.new
+      expect do
+        expect do
+          fairy_tail.delay.tell
+        end.to change(fairy_tail, :happy_ending).from(nil).to(true)
+      end.not_to change { Delayed::Job.count }
+    end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/performable_method_spec.rb 
new/spec/performable_method_spec.rb
--- old/spec/performable_method_spec.rb 2014-12-22 14:22:31.000000000 +0100
+++ new/spec/performable_method_spec.rb 2015-09-24 23:40:11.000000000 +0200
@@ -68,7 +68,7 @@
       story = Story.create
       expect(story).to receive(:error).with(an_instance_of(Delayed::Job), 
an_instance_of(RuntimeError))
       expect(story).to receive(:tell).and_raise(RuntimeError)
-      expect { story.delay.tell.invoke_job }.to raise_error
+      expect { story.delay.tell.invoke_job }.to raise_error(RuntimeError)
     end
 
     it 'delegates failure hook to object' do
@@ -98,7 +98,7 @@
         story = Story.create
         expect(story).to receive(:error).with(an_instance_of(Delayed::Job), 
an_instance_of(RuntimeError))
         expect(story).to receive(:tell).and_raise(RuntimeError)
-        expect { story.delay.tell }.to raise_error
+        expect { story.delay.tell }.to raise_error(RuntimeError)
       end
 
       it 'delegates failure hook to object' do
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/sample_jobs.rb new/spec/sample_jobs.rb
--- old/spec/sample_jobs.rb     2014-12-22 14:22:31.000000000 +0100
+++ new/spec/sample_jobs.rb     2015-09-24 23:40:11.000000000 +0200
@@ -1,4 +1,5 @@
-class NamedJob < Struct.new(:perform)
+NamedJob = Struct.new(:perform)
+class NamedJob
   def display_name
     'named_job'
   end
@@ -22,11 +23,12 @@
   cattr_accessor :runs
   @runs = 0
   def perform
-    raise 'did not work'
+    raise Exception, 'did not work'
   end
 end
 
-class CustomRescheduleJob < Struct.new(:offset)
+CustomRescheduleJob = Struct.new(:offset)
+class CustomRescheduleJob
   cattr_accessor :runs
   @runs = 0
   def perform
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/worker_spec.rb new/spec/worker_spec.rb
--- old/spec/worker_spec.rb     2014-12-22 14:22:31.000000000 +0100
+++ new/spec/worker_spec.rb     2015-09-24 23:40:11.000000000 +0200
@@ -154,4 +154,22 @@
       @worker.say(@text, Delayed::Worker.default_log_level)
     end
   end
+
+  describe 'plugin registration' do
+    it 'does not double-register plugins on worker instantiation' do
+      performances = 0
+      plugin = Class.new(Delayed::Plugin) do
+        callbacks do |lifecycle|
+          lifecycle.before(:enqueue) { performances += 1 }
+        end
+      end
+      Delayed::Worker.plugins << plugin
+
+      Delayed::Worker.new
+      Delayed::Worker.new
+      Delayed::Worker.lifecycle.run_callbacks(:enqueue, nil) {}
+
+      expect(performances).to eq(1)
+    end
+  end
 end


Reply via email to