Commit e227e0e3d4402574e317db2fd372483a07e8c2ea

Authored by Rodrigo Souto
1 parent d568d8b7

rails3: upgrading delayed_job

Showing 111 changed files with 3703 additions and 2006 deletions   Show diff stats
config/initializers/delayed_job_config.rb
  1 +require 'delayed_job'
1 Delayed::Worker.backend = :active_record 2 Delayed::Worker.backend = :active_record
2 Delayed::Worker.max_attempts = 2 3 Delayed::Worker.max_attempts = 2
3 Delayed::Worker.max_run_time = 10.minutes 4 Delayed::Worker.max_run_time = 10.minutes
db/migrate/20130924152827_add_queue_to_delayed_jobs.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class AddQueueToDelayedJobs < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :delayed_jobs, :queue, :string
  4 + end
  5 +
  6 + def self.down
  7 + remove_column :delayed_jobs, :queue
  8 + end
  9 +end
1 -# This file is auto-generated from the current state of the database. Instead of editing this file,  
2 -# please use the migrations feature of Active Record to incrementally modify your database, and  
3 -# then regenerate this schema definition. 1 +# encoding: UTF-8
  2 +# This file is auto-generated from the current state of the database. Instead
  3 +# of editing this file, please use the migrations feature of Active Record to
  4 +# incrementally modify your database, and then regenerate this schema definition.
4 # 5 #
5 -# Note that this schema.rb definition is the authoritative source for your database schema. If you need  
6 -# to create the application database on another system, you should be using db:schema:load, not running  
7 -# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations 6 +# Note that this schema.rb definition is the authoritative source for your
  7 +# database schema. If you need to create the application database on another
  8 +# system, you should be using db:schema:load, not running all the migrations
  9 +# from scratch. The latter is a flawed and unsustainable approach (the more migrations
8 # you'll amass, the slower it'll run and the greater likelihood for issues). 10 # you'll amass, the slower it'll run and the greater likelihood for issues).
9 # 11 #
10 # It's strongly recommended to check this file into your version control system. 12 # It's strongly recommended to check this file into your version control system.
11 13
12 -ActiveRecord::Schema.define(:version => 20130606110602) do 14 +ActiveRecord::Schema.define(:version => 20130924152827) do
13 15
14 create_table "abuse_reports", :force => true do |t| 16 create_table "abuse_reports", :force => true do |t|
15 t.integer "reporter_id" 17 t.integer "reporter_id"
@@ -245,6 +247,7 @@ ActiveRecord::Schema.define(:version =&gt; 20130606110602) do @@ -245,6 +247,7 @@ ActiveRecord::Schema.define(:version =&gt; 20130606110602) do
245 t.string "locked_by" 247 t.string "locked_by"
246 t.datetime "created_at" 248 t.datetime "created_at"
247 t.datetime "updated_at" 249 t.datetime "updated_at"
  250 + t.string "queue"
248 end 251 end
249 252
250 add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority" 253 add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority"
script/delayed_job
@@ -7,7 +7,5 @@ @@ -7,7 +7,5 @@
7 # etc. The actual feed update logic is DelayedJob plugin. 7 # etc. The actual feed update logic is DelayedJob plugin.
8 8
9 require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) 9 require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
10 -require 'daemons'  
11 require 'delayed/command' 10 require 'delayed/command'
12 -  
13 Delayed::Command.new(ARGV).daemonize 11 Delayed::Command.new(ARGV).daemonize
vendor/plugins/delayed_job/.rspec 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +--color
  2 +--fail-fast
  3 +--order random
vendor/plugins/delayed_job/.travis.yml 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 +language: ruby
  2 +only:
  3 + - master
  4 +rvm:
  5 + - jruby-19mode
  6 + - rbx-19mode
  7 + - 1.9.2
  8 + - 1.9.3
  9 + - 2.0.0
  10 +env:
  11 + matrix:
  12 + - "RAILS_VERSION=\"~> 3.0.0\""
  13 + - "RAILS_VERSION=\"~> 3.1.0\""
  14 + - "RAILS_VERSION=\"~> 3.2.0\""
  15 + - "RAILS_VERSION=\"~> 4.0.0\""
  16 +matrix:
  17 + exclude:
  18 + - rvm: 1.9.2
  19 + env: "RAILS_VERSION=\"~> 4.0.0\""
  20 + allow_failures:
  21 + - rvm: jruby-19mode
  22 + - rvm: rbx-19mode
0 \ No newline at end of file 23 \ No newline at end of file
vendor/plugins/delayed_job/CHANGELOG.md 0 → 100644
@@ -0,0 +1,170 @@ @@ -0,0 +1,170 @@
  1 +4.0.0 - 2013-07-30
  2 +==================
  3 +* Rails 4 compatibility
  4 +* Reverted threaded startup due to daemons incompatibilities
  5 +* Attempt to recover from job reservation errors
  6 +
  7 +4.0.0.beta2 - 2013-05-28
  8 +========================
  9 +* Rails 4 compatibility
  10 +* Threaded startup script for faster multi-worker startup
  11 +* YAML compatibility changes
  12 +* Added jobs:check rake task
  13 +
  14 +4.0.0.beta1 - 2013-03-02
  15 +========================
  16 +* Rails 4 compatibility
  17 +
  18 +3.0.5 - 2013-01-28
  19 +==================
  20 +* Better job timeout error logging
  21 +* psych support for delayed_job_data_mapper deserialization
  22 +* User can configure the worker to raise a SignalException on TERM and/or INT
  23 +* Add the ability to run all available jobs and exit when complete
  24 +
  25 +3.0.4 - 2012-11-09
  26 +==================
  27 +* Allow the app to specify a default queue name
  28 +* Capistrano script now allows user to specify the DJ command, allowing the user to add "bundle exec" if necessary
  29 +* Persisted record check is now more general
  30 +
  31 +3.0.3 - 2012-05-25
  32 +==================
  33 +* Fix a bug where the worker would not respect the exit condition
  34 +* Properly handle sleep delay command line argument
  35 +
  36 +3.0.2 - 2012-04-02
  37 +==================
  38 +* Fix deprecation warnings
  39 +* Raise ArgumentError if attempting to enqueue a performable method on an object that hasn't been persisted yet
  40 +* Allow the number of jobs read at a time to be configured from the command line using --read-ahead
  41 +* Allow custom logger to be configured through Delayed::Worker.logger
  42 +* Various documentation improvements
  43 +
  44 +3.0.1 - 2012-01-24
  45 +==================
  46 +* Added RecordNotFound message to deserialization error
  47 +* Direct JRuby's yecht parser to syck extensions
  48 +* Updated psych extensions for better compatibility with ruby 1.9.2
  49 +* Updated syck extension for increased compatibility with class methods
  50 +* Test grooming
  51 +
  52 +3.0.0 - 2011-12-30
  53 +==================
  54 +* New: Named queues
  55 +* New: Job/Worker lifecycle callbacks
  56 +* Change: daemons is no longer a runtime dependency
  57 +* Change: Active Record backend support is provided by a separate gem
  58 +* Change: Enqueue hook is called before jobs are saved so that they may be modified
  59 +* Fix problem deserializing models that use a custom primary key column
  60 +* Fix deserializing AR models when the object isn't in the default scope
  61 +* Fix hooks not getting called when delay_jobs is false
  62 +
  63 +2.1.4 - 2011-02-11
  64 +==================
  65 +* Working around issues when psych is loaded, fixes issues with bundler 1.0.10 and Rails 3.0.4
  66 +* Added -p/--prefix option to help differentiate multiple delayed job workers on the same host.
  67 +
  68 +2.1.3 - 2011-01-20
  69 +==================
  70 +* Revert worker contention fix due to regressions
  71 +* Added Delayed::Worker.delay_jobs flag to support running jobs immediately
  72 +
  73 +2.1.2 - 2010-12-01
  74 +==================
  75 +* Remove contention between multiple workers by performing an update to lock a job before fetching it
  76 +* Job payloads may implement #max_attempts to control how many times it should be retried
  77 +* Fix for loading ActionMailer extension
  78 +* Added 'delayed_job_server_role' Capistrano variable to allow delayed_job to run on its own worker server
  79 + set :delayed_job_server_role, :worker
  80 +* Fix `rake jobs:work` so it outputs to the console
  81 +
  82 +2.1.1 - 2010-11-14
  83 +==================
  84 +* Fix issue with worker name not getting properly set when locking a job
  85 +* Fixes for YAML serialization
  86 +
  87 +2.1.0 - 2010-11-14
  88 +==================
  89 +* Added enqueue, before, after, success, error, and failure. See the README
  90 +* Remove Merb support
  91 +* Remove all non Active Record backends into separate gems. See https://github.com/collectiveidea/delayed_job/wiki/Backends
  92 +* remove rails 2 support. delayed_job 2.1 will only support Rails 3
  93 +* New pure-YAML serialization
  94 +* Added Rails 3 railtie and generator
  95 +* Changed @@sleep_delay to self.class.sleep_delay to be consistent with other class variable usage
  96 +* Added --sleep-delay command line option
  97 +
  98 +2.0.8 - Unreleased
  99 +==================
  100 +* Backport fix for deserialization errors that bring down the daemon
  101 +
  102 +2.0.7 - 2011-02-10
  103 +==================
  104 +* Fixed missing generators and recipes for Rails 2.x
  105 +
  106 +2.0.6 - 2011-01-20
  107 +==================
  108 +* Revert worker contention fix due to regressions
  109 +
  110 +2.0.5 - 2010-12-01
  111 +==================
  112 +* Added #reschedule_at hook on payload to determine when the job should be rescheduled [backported from 2.1]
  113 +* Added --sleep-delay command line option [backported from 2.1]
  114 +* Added 'delayed_job_server_role' Capistrano variable to allow delayed_job to run on its own worker server
  115 + set :delayed_job_server_role, :worker
  116 +* Changed AR backend to reserve jobs using an UPDATE query to reduce worker contention [backported from 2.1]
  117 +
  118 +2.0.4 - 2010-11-14
  119 +==================
  120 +* Fix issue where dirty tracking prevented job from being properly unlocked
  121 +* Add delayed_job_args variable for Capistrano recipe to allow configuration of started workers (e.g. "-n 2 --max-priority 10")
  122 +* Added options to handle_asynchronously
  123 +* Added Delayed::Worker.default_priority
  124 +* Allow private methods to be delayed
  125 +* Fixes for Ruby 1.9
  126 +* Added -m command line option to start a monitor process
  127 +* normalize logging in worker
  128 +* Deprecate #send_later and #send_at in favor of new #delay method
  129 +* Added @#delay@ to Object that allows you to delay any method and pass options:
  130 + options = {:priority => 19, :run_at => 5.minutes.from_now}
  131 + UserMailer.delay(options).deliver_confirmation(@user)
  132 +
  133 +2.0.3 - 2010-04-16
  134 +==================
  135 +* Fix initialization for Rails 2.x
  136 +
  137 +2.0.2 - 2010-04-08
  138 +==================
  139 +* Fixes to Mongo Mapper backend [ "14be7a24":http://github.com/collectiveidea/delayed_job/commit/14be7a24, "dafd5f46":http://github.com/collectiveidea/delayed_job/commit/dafd5f46, "54d40913":http://github.com/collectiveidea/delayed_job/commit/54d40913 ]
  140 +* DataMapper backend performance improvements [ "93833cce":http://github.com/collectiveidea/delayed_job/commit/93833cce, "e9b1573e":http://github.com/collectiveidea/delayed_job/commit/e9b1573e, "37a16d11":http://github.com/collectiveidea/delayed_job/commit/37a16d11, "803f2bfa":http://github.com/collectiveidea/delayed_job/commit/803f2bfa ]
  141 +* Fixed Delayed::Command to create tmp/pids directory [ "8ec8ca41":http://github.com/collectiveidea/delayed_job/commit/8ec8ca41 ]
  142 +* Railtie to perform Rails 3 initialization [ "3e0fc41f":http://github.com/collectiveidea/delayed_job/commit/3e0fc41f ]
  143 +* Added on_permanent_failure hook [ "d2f14cd6":http://github.com/collectiveidea/delayed_job/commit/d2f14cd6 ]
  144 +
  145 +2.0.1 - 2010-04-03
  146 +==================
  147 +* Bug fix for using ActiveRecord backend with daemon [martinbtt]
  148 +
  149 +2.0.0 - 2010-04-03
  150 +==================
  151 +* Multiple backend support (See README for more details)
  152 +* Added MongoMapper backend [zbelzer, moneypools]
  153 +* Added DataMapper backend [lpetre]
  154 +* Reverse priority so the jobs table can be indexed. Lower numbers have higher priority. The default priority is 0, so increase it for jobs that are not important.
  155 +* Move most of the heavy lifting from Job to Worker (#work_off, #reschedule, #run, #min_priority, #max_priority, #max_run_time, #max_attempts, #worker_name) [albus522]
  156 +* Remove EvaledJob. Implement your own if you need this functionality.
  157 +* Only use Time.zone if it is set. Closes #20
  158 +* Fix for last_error recording when destroy_failed_jobs = false, max_attempts = 1
  159 +* Implemented worker name_prefix to maintain dynamic nature of pid detection
  160 +* Some Rails 3 compatibility fixes [fredwu]
  161 +
  162 +1.8.5 - 2010-03-15
  163 +==================
  164 +* Set auto_flushing=true on Rails logger to fix logging in production
  165 +* Fix error message when trying to send_later on a method that doesn't exist
  166 +* Don't use rails_env in capistrano if it's not set. closes #22
  167 +* Delayed job should append to delayed_job.log not overwrite
  168 +* Version bump to 1.8.5
  169 +* fixing Time.now to be Time.zone.now if set to honor the app set local TimeZone
  170 +* Replaced @Worker::SLEEP@, @Job::MAX_ATTEMPTS@, and @Job::MAX_RUN_TIME@ with class methods that can be overridden.
vendor/plugins/delayed_job/CONTRIBUTING.md 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +How to contribute
  2 +=================
  3 +
  4 +If you find what looks like a bug:
  5 +
  6 +* Search the "mailing list":http://groups.google.com/group/delayed_job to see
  7 + if anyone else had the same issue.
  8 +* Check the "GitHub issue tracker":http://github.com/collectiveidea/delayed_job/issues/
  9 + to see if anyone else has reported issue.
  10 +* Make sure you are using the latest version of delayed_job
  11 + ![Gem Version](https://badge.fury.io/rb/delayed_job.png)
  12 +* Make sure you are using the latest backend gem for delayed_job
  13 + * Active Record ![Gem Version](https://badge.fury.io/rb/delayed_job_active_record.png)
  14 + * Mongoid ![Gem Version](https://badge.fury.io/rb/delayed_job_mongoid.png)
  15 +* If you are still having an issue, create an issue including:
  16 + * Ruby version
  17 + * Gemfile.lock contents or at least major gem versions, such as Rails version
  18 + * Steps to reproduce the issue
  19 + * Full backtrace for any errors encountered
  20 +
  21 +If you want to contribute an enhancement or a fix:
  22 +
  23 +* Fork the project on GitHub.
  24 +* Make your changes with tests.
  25 +* Commit the changes without making changes to the Rakefile or any other files
  26 + that aren't related to your enhancement or fix.
  27 +* Send a pull request.
vendor/plugins/delayed_job/Gemfile 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +platforms :ruby do
  6 + gem 'sqlite3'
  7 +end
  8 +
  9 +platforms :jruby do
  10 + gem 'jruby-openssl'
  11 + gem 'activerecord-jdbcsqlite3-adapter'
  12 +end
  13 +
  14 +group :test do
  15 + gem 'activerecord', (ENV['RAILS_VERSION'] || ['>= 3.0', '< 4.1'])
  16 + gem 'actionmailer', (ENV['RAILS_VERSION'] || ['>= 3.0', '< 4.1'])
  17 + gem 'coveralls', :require => false
  18 + gem 'rspec', '>= 2.11'
  19 + gem 'simplecov', :require => false
  20 +end
  21 +
  22 +gemspec
vendor/plugins/delayed_job/LICENSE.md 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +Copyright (c) 2005 Tobias Lütke
  2 +
  3 +Permission is hereby granted, free of charge, to any person obtaining
  4 +a copy of this software and associated documentation files (the
  5 +"Software"), to deal in the Software without restriction, including
  6 +without limitation the rights to use, copy, modify, merge, publish,
  7 +distribute, sublicense, and/or sell copies of the Software, and to
  8 +permit persons to whom the Software is furnished to do so, subject to
  9 +the following conditions:
  10 +
  11 +The above copyright notice and this permission notice shall be
  12 +included in all copies or substantial portions of the Software.
  13 +
  14 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
  17 +NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
vendor/plugins/delayed_job/MIT-LICENSE
@@ -1,20 +0,0 @@ @@ -1,20 +0,0 @@
1 -Copyright (c) 2005 Tobias Luetke  
2 -  
3 -Permission is hereby granted, free of charge, to any person obtaining  
4 -a copy of this software and associated documentation files (the  
5 -"Software"), to deal in the Software without restriction, including  
6 -without limitation the rights to use, copy, modify, merge, publish,  
7 -distribute, sublicense, and/or sell copies of the Software, and to  
8 -permit persons to whom the Software is furnished to do so, subject to  
9 -the following conditions:  
10 -  
11 -The above copyright notice and this permission notice shall be  
12 -included in all copies or substantial portions of the Software.  
13 -  
14 -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,  
15 -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF  
16 -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND  
17 -NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE  
18 -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION  
19 -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION  
20 -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  
21 \ No newline at end of file 0 \ No newline at end of file
vendor/plugins/delayed_job/README.md 0 → 100644
@@ -0,0 +1,346 @@ @@ -0,0 +1,346 @@
  1 +Delayed::Job
  2 +============
  3 +[![Gem Version](https://badge.fury.io/rb/delayed_job.png)][gem]
  4 +[![Build Status](https://secure.travis-ci.org/collectiveidea/delayed_job.png?branch=master)][travis]
  5 +[![Dependency Status](https://gemnasium.com/collectiveidea/delayed_job.png?travis)][gemnasium]
  6 +[![Code Climate](https://codeclimate.com/github/collectiveidea/delayed_job.png)][codeclimate]
  7 +[![Coverage Status](https://coveralls.io/repos/collectiveidea/delayed_job/badge.png?branch=master)][coveralls]
  8 +
  9 +[gem]: https://rubygems.org/gems/delayed_job
  10 +[travis]: http://travis-ci.org/collectiveidea/delayed_job
  11 +[gemnasium]: https://gemnasium.com/collectiveidea/delayed_job
  12 +[codeclimate]: https://codeclimate.com/github/collectiveidea/delayed_job
  13 +[coveralls]: https://coveralls.io/r/collectiveidea/delayed_job
  14 +
  15 +Delayed::Job (or DJ) encapsulates the common pattern of asynchronously executing
  16 +longer tasks in the background.
  17 +
  18 +It is a direct extraction from Shopify where the job table is responsible for a
  19 +multitude of core tasks. Amongst those tasks are:
  20 +
  21 +* sending massive newsletters
  22 +* image resizing
  23 +* http downloads
  24 +* updating smart collections
  25 +* updating solr, our search server, after product changes
  26 +* batch imports
  27 +* spam checks
  28 +
  29 +[Follow us on Twitter][twitter] to get updates and notices about new releases.
  30 +
  31 +[twitter]: https://twitter.com/delayedjob
  32 +
  33 +Installation
  34 +============
  35 +delayed_job 3.0.0 only supports Rails 3.0+. See the [2.0
  36 +branch](https://github.com/collectiveidea/delayed_job/tree/v2.0) for Rails 2.
  37 +
  38 +delayed_job supports multiple backends for storing the job queue. [See the wiki
  39 +for other backends](http://wiki.github.com/collectiveidea/delayed_job/backends).
  40 +
  41 +If you plan to use delayed_job with Active Record, add `delayed_job_active_record` to your `Gemfile`.
  42 +
  43 +```ruby
  44 +gem 'delayed_job_active_record'
  45 +```
  46 +
  47 +If you plan to use delayed_job with Mongoid, add `delayed_job_mongoid` to your `Gemfile`.
  48 +
  49 +```ruby
  50 +gem 'delayed_job_mongoid'
  51 +```
  52 +
  53 +Run `bundle install` to install the backend and delayed_job gems.
  54 +
  55 +The Active Record backend requires a jobs table. You can create that table by
  56 +running the following command:
  57 +
  58 + rails generate delayed_job:active_record
  59 + rake db:migrate
  60 +
  61 +Rails 4
  62 +=======
  63 +If you are using the protected_attributes gem, it must appear before delayed_job in your gemfile.
  64 +
  65 +Upgrading from 2.x to 3.0.0 on Active Record
  66 +============================================
  67 +Delayed Job 3.0.0 introduces a new column to the delayed_jobs table.
  68 +
  69 +If you're upgrading from Delayed Job 2.x, run the upgrade generator to create a migration to add the column.
  70 +
  71 + rails generate delayed_job:upgrade
  72 + rake db:migrate
  73 +
  74 +Queuing Jobs
  75 +============
  76 +Call `.delay.method(params)` on any object and it will be processed in the background.
  77 +
  78 +```ruby
  79 +# without delayed_job
  80 +@user.activate!(@device)
  81 +
  82 +# with delayed_job
  83 +@user.delay.activate!(@device)
  84 +```
  85 +
  86 +If a method should always be run in the background, you can call
  87 +`#handle_asynchronously` after the method declaration:
  88 +
  89 +```ruby
  90 +class Device
  91 + def deliver
  92 + # long running method
  93 + end
  94 + handle_asynchronously :deliver
  95 +end
  96 +
  97 +device = Device.new
  98 +device.deliver
  99 +```
  100 +
  101 +handle_asynchronously can take as options anything you can pass to delay. In
  102 +addition, the values can be Proc objects allowing call time evaluation of the
  103 +value. For some examples:
  104 +
  105 +```ruby
  106 +class LongTasks
  107 + def send_mailer
  108 + # Some other code
  109 + end
  110 + handle_asynchronously :send_mailer, :priority => 20
  111 +
  112 + def in_the_future
  113 + # Some other code
  114 + end
  115 + # 5.minutes.from_now will be evaluated when in_the_future is called
  116 + handle_asynchronously :in_the_future, :run_at => Proc.new { 5.minutes.from_now }
  117 +
  118 + def self.when_to_run
  119 + 2.hours.from_now
  120 + end
  121 +
  122 + def call_a_class_method
  123 + # Some other code
  124 + end
  125 + handle_asynchronously :call_a_class_method, :run_at => Proc.new { when_to_run }
  126 +
  127 + attr_reader :how_important
  128 +
  129 + def call_an_instance_method
  130 + # Some other code
  131 + end
  132 + handle_asynchronously :call_an_instance_method, :priority => Proc.new {|i| i.how_important }
  133 +end
  134 +```
  135 +
  136 +If you ever want to call a `handle_asynchronously`'d method without Delayed Job, for instance while debugging something at the console, just add `_without_delay` to the method name. For instance, if your original method was `foo`, then call `foo_without_delay`.
  137 +
  138 +Rails 3 Mailers
  139 +===============
  140 +Due to how mailers are implemented in Rails 3, we had to do a little work around to get delayed_job to work.
  141 +
  142 +```ruby
  143 +# without delayed_job
  144 +Notifier.signup(@user).deliver
  145 +
  146 +# with delayed_job
  147 +Notifier.delay.signup(@user)
  148 +```
  149 +
  150 +Remove the `.deliver` method to make it work. It's not ideal, but it's the best
  151 +we could do for now.
  152 +
  153 +Named Queues
  154 +============
  155 +DJ 3 introduces Resque-style named queues while still retaining DJ-style
  156 +priority. The goal is to provide a system for grouping tasks to be worked by
  157 +separate pools of workers, which may be scaled and controlled individually.
  158 +
  159 +Jobs can be assigned to a queue by setting the `queue` option:
  160 +
  161 +```ruby
  162 +object.delay(:queue => 'tracking').method
  163 +
  164 +Delayed::Job.enqueue job, :queue => 'tracking'
  165 +
  166 +handle_asynchronously :tweet_later, :queue => 'tweets'
  167 +```
  168 +
  169 +Running Jobs
  170 +============
  171 +`script/delayed_job` can be used to manage a background process which will
  172 +start working off jobs.
  173 +
  174 +To do so, add `gem "daemons"` to your `Gemfile` and make sure you've run `rails
  175 +generate delayed_job`.
  176 +
  177 +You can then do the following:
  178 +
  179 + RAILS_ENV=production script/delayed_job start
  180 + RAILS_ENV=production script/delayed_job stop
  181 +
  182 + # Runs two workers in separate processes.
  183 + RAILS_ENV=production script/delayed_job -n 2 start
  184 + RAILS_ENV=production script/delayed_job stop
  185 +
  186 + # Set the --queue or --queues option to work from a particular queue.
  187 + RAILS_ENV=production script/delayed_job --queue=tracking start
  188 + RAILS_ENV=production script/delayed_job --queues=mailers,tasks start
  189 +
  190 + # Runs all available jobs and then exits
  191 + RAILS_ENV=production script/delayed_job start --exit-on-complete
  192 + # or to run in the foreground
  193 + RAILS_ENV=production script/delayed_job run --exit-on-complete
  194 +
  195 +**Rails 4:** *replace script/delayed_job with bin/delayed_job*
  196 +
  197 +Workers can be running on any computer, as long as they have access to the
  198 +database and their clock is in sync. Keep in mind that each worker will check
  199 +the database at least every 5 seconds.
  200 +
  201 +You can also invoke `rake jobs:work` which will start working off jobs. You can
  202 +cancel the rake task with `CTRL-C`.
  203 +
  204 +If you want to just run all available jobs and exit you can use `rake jobs:workoff`
  205 +
  206 +Work off queues by setting the `QUEUE` or `QUEUES` environment variable.
  207 +
  208 + QUEUE=tracking rake jobs:work
  209 + QUEUES=mailers,tasks rake jobs:work
  210 +
  211 +Restarting delayed_job
  212 +======================
  213 +
  214 +The following syntax will restart delayed jobs:
  215 +
  216 + RAILS_ENV=production script/delayed_job restart
  217 +
  218 +To restart multiple delayed_job workers:
  219 +
  220 + RAILS_ENV=production script/delayed_job -n2 restart
  221 +
  222 +**Rails 4:** *replace script/delayed_job with bin/delayed_job*
  223 +
  224 +
  225 +
  226 +Custom Jobs
  227 +===========
  228 +Jobs are simple ruby objects with a method called perform. Any object which responds to perform can be stuffed into the jobs table. Job objects are serialized to yaml so that they can later be resurrected by the job runner.
  229 +
  230 +```ruby
  231 +class NewsletterJob < Struct.new(:text, :emails)
  232 + def perform
  233 + emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
  234 + end
  235 +end
  236 +
  237 +Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.find(:all).collect(&:email))
  238 +```
  239 +To set a per-job max attempts that overrides the Delayed::Worker.max_attempts you can define a max_attempts method on the job
  240 +```ruby
  241 +class NewsletterJob < Struct.new(:text, :emails)
  242 + def perform
  243 + emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
  244 + end
  245 +
  246 + def max_attempts
  247 + return 3
  248 + end
  249 +end
  250 +````
  251 +
  252 +
  253 +Hooks
  254 +=====
  255 +You can define hooks on your job that will be called at different stages in the process:
  256 +
  257 +```ruby
  258 +class ParanoidNewsletterJob < NewsletterJob
  259 + def enqueue(job)
  260 + record_stat 'newsletter_job/enqueue'
  261 + end
  262 +
  263 + def perform
  264 + emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
  265 + end
  266 +
  267 + def before(job)
  268 + record_stat 'newsletter_job/start'
  269 + end
  270 +
  271 + def after(job)
  272 + record_stat 'newsletter_job/after'
  273 + end
  274 +
  275 + def success(job)
  276 + record_stat 'newsletter_job/success'
  277 + end
  278 +
  279 + def error(job, exception)
  280 + Airbrake.notify(exception)
  281 + end
  282 +
  283 + def failure(job)
  284 + page_sysadmin_in_the_middle_of_the_night
  285 + end
  286 +end
  287 +```
  288 +
  289 +Gory Details
  290 +============
  291 +The library revolves around a delayed_jobs table which looks as follows:
  292 +
  293 +```ruby
  294 +create_table :delayed_jobs, :force => true do |table|
  295 + table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue
  296 + table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually.
  297 + table.text :handler # YAML-encoded string of the object that will do work
  298 + table.text :last_error # reason for last failure (See Note below)
  299 + table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
  300 + table.datetime :locked_at # Set when a client is working on this object
  301 + table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
  302 + table.string :locked_by # Who is working on this object (if locked)
  303 + table.string :queue # The name of the queue this job is in
  304 + table.timestamps
  305 +end
  306 +```
  307 +
  308 +On failure, the job is scheduled again in 5 seconds + N ** 4, where N is the number of retries.
  309 +
  310 +The default Worker.max_attempts is 25. After this, the job either deleted (default), or left in the database with "failed_at" set.
  311 +With the default of 25 attempts, the last retry will be 20 days later, with the last interval being almost 100 hours.
  312 +
  313 +The default Worker.max_run_time is 4.hours. If your job takes longer than that, another computer could pick it up. It's up to you to
  314 +make sure your job doesn't exceed this time. You should set this to the longest time you think the job could take.
  315 +
  316 +By default, it will delete failed jobs (and it always deletes successful jobs). If you want to keep failed jobs, set
  317 +Delayed::Worker.destroy_failed_jobs = false. The failed jobs will be marked with non-null failed_at.
  318 +
  319 +By default all jobs are scheduled with priority = 0, which is top priority. You can change this by setting Delayed::Worker.default_priority to something else. Lower numbers have higher priority.
  320 +
  321 +The default behavior is to read 5 jobs from the queue when finding an available job. You can configure this by setting Delayed::Worker.read_ahead.
  322 +
  323 +By default all jobs will be queued without a named queue. A default named queue can be specified by using Delayed::Worker.default_queue_name.
  324 +
  325 +It is possible to disable delayed jobs for testing purposes. Set Delayed::Worker.delay_jobs = false to execute all jobs realtime.
  326 +
  327 +Here is an example of changing job parameters in Rails:
  328 +
  329 +```ruby
  330 +# config/initializers/delayed_job_config.rb
  331 +Delayed::Worker.destroy_failed_jobs = false
  332 +Delayed::Worker.sleep_delay = 60
  333 +Delayed::Worker.max_attempts = 3
  334 +Delayed::Worker.max_run_time = 5.minutes
  335 +Delayed::Worker.read_ahead = 10
  336 +Delayed::Worker.default_queue_name = 'default'
  337 +Delayed::Worker.delay_jobs = !Rails.env.test?
  338 +```
  339 +
  340 +Cleaning up
  341 +===========
  342 +You can invoke `rake jobs:clear` to delete all jobs in the queue.
  343 +
  344 +Mailing List
  345 +============
  346 +Join us on the [mailing list](http://groups.google.com/group/delayed_job)
vendor/plugins/delayed_job/README.textile
@@ -1,209 +0,0 @@ @@ -1,209 +0,0 @@
1 -h1. Delayed::Job  
2 -  
3 -Delated_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background.  
4 -  
5 -It is a direct extraction from Shopify where the job table is responsible for a multitude of core tasks. Amongst those tasks are:  
6 -  
7 -* sending massive newsletters  
8 -* image resizing  
9 -* http downloads  
10 -* updating smart collections  
11 -* batch imports  
12 -* spam checks  
13 -  
14 -h2. Installation  
15 -  
16 -To install as a gem, add the following to @config/environment.rb@:  
17 -  
18 -<pre>  
19 -config.gem 'delayed_job'  
20 -</pre>  
21 -  
22 -Rake tasks are not automatically loaded from gems, so you'll need to add the following to your Rakefile:  
23 -  
24 -<pre>  
25 -begin  
26 - require 'delayed/tasks'  
27 -rescue LoadError  
28 - STDERR.puts "Run `rake gems:install` to install delayed_job"  
29 -end  
30 -</pre>  
31 -  
32 -To install as a plugin:  
33 -  
34 -<pre>  
35 -script/plugin install git://github.com/collectiveidea/delayed_job.git  
36 -</pre>  
37 -  
38 -After delayed_job is installed, you will need to setup the backend.  
39 -  
40 -h2. Backends  
41 -  
42 -delayed_job supports multiple backends for storing the job queue. There are currently implementations for Active Record, MongoMapper, and DataMapper.  
43 -  
44 -h3. Active Record  
45 -  
46 -The default is Active Record, which requires a jobs table.  
47 -  
48 -<pre>  
49 -$ script/generate delayed_job  
50 -$ rake db:migrate  
51 -</pre>  
52 -  
53 -h3. MongoMapper  
54 -  
55 -You must use @MongoMapper.setup@ in the initializer:  
56 -  
57 -<pre>  
58 -config = YAML::load(File.read(Rails.root.join('config/mongo.yml')))  
59 -MongoMapper.setup(config, Rails.env)  
60 -  
61 -Delayed::Worker.backend = :mongo_mapper  
62 -</pre>  
63 -  
64 -h3. DataMapper  
65 -  
66 -<pre>  
67 -# config/initializers/delayed_job.rb  
68 -Delayed::Worker.backend = :data_mapper  
69 -Delayed::Worker.backend.auto_upgrade!  
70 -</pre>  
71 -  
72 -h2. Queuing Jobs  
73 -  
74 -Call @.delay.method(params)@ on any object and it will be processed in the background.  
75 -  
76 -<pre>  
77 -# without delayed_job  
78 -Notifier.deliver_signup(@user)  
79 -  
80 -# with delayed_job  
81 -Notifier.delay.deliver_signup @user  
82 -</pre>  
83 -  
84 -If a method should always be run in the background, you can call @#handle_asynchronously@ after the method declaration:  
85 -  
86 -<pre>  
87 -class Device  
88 - def deliver  
89 - # long running method  
90 - end  
91 - handle_asynchronously :deliver  
92 -end  
93 -  
94 -device = Device.new  
95 -device.deliver  
96 -</pre>  
97 -  
98 -h2. Running Jobs  
99 -  
100 -@script/delayed_job@ can be used to manage a background process which will start working off jobs. Make sure you've run `script/generate delayed_job`.  
101 -  
102 -<pre>  
103 -$ RAILS_ENV=production script/delayed_job start  
104 -$ RAILS_ENV=production script/delayed_job stop  
105 -  
106 -# Runs two workers in separate processes.  
107 -$ RAILS_ENV=production script/delayed_job -n 2 start  
108 -$ RAILS_ENV=production script/delayed_job stop  
109 -</pre>  
110 -  
111 -Workers can be running on any computer, as long as they have access to the database and their clock is in sync. Keep in mind that each worker will check the database at least every 5 seconds.  
112 -  
113 -You can also invoke @rake jobs:work@ which will start working off jobs. You can cancel the rake task with @CTRL-C@.  
114 -  
115 -h2. Custom Jobs  
116 -  
117 -Jobs are simple ruby objects with a method called perform. Any object which responds to perform can be stuffed into the jobs table. Job objects are serialized to yaml so that they can later be resurrected by the job runner.  
118 -  
119 -<pre>  
120 -class NewsletterJob < Struct.new(:text, :emails)  
121 - def perform  
122 - emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }  
123 - end  
124 -end  
125 -  
126 -Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.find(:all).collect(&:email))  
127 -</pre>  
128 -  
129 -You can also add an optional on_permanent_failure method which will run if the job has failed too many times to be retried:  
130 -  
131 -<pre>  
132 -class ParanoidNewsletterJob < NewsletterJob  
133 - def perform  
134 - emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }  
135 - end  
136 -  
137 - def on_permanent_failure  
138 - page_sysadmin_in_the_middle_of_the_night  
139 - end  
140 -end  
141 -</pre>  
142 -  
143 -h2. Gory Details  
144 -  
145 -The library evolves around a delayed_jobs table which looks as follows:  
146 -  
147 -<pre>  
148 -create_table :delayed_jobs, :force => true do |table|  
149 - table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue  
150 - table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually.  
151 - table.text :handler # YAML-encoded string of the object that will do work  
152 - table.text :last_error # reason for last failure (See Note below)  
153 - table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.  
154 - table.datetime :locked_at # Set when a client is working on this object  
155 - table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)  
156 - table.string :locked_by # Who is working on this object (if locked)  
157 - table.timestamps  
158 -end  
159 -</pre>  
160 -  
161 -On failure, the job is scheduled again in 5 seconds + N ** 4, where N is the number of retries.  
162 -  
163 -The default Worker.max_attempts is 25. After this, the job either deleted (default), or left in the database with "failed_at" set.  
164 -With the default of 25 attempts, the last retry will be 20 days later, with the last interval being almost 100 hours.  
165 -  
166 -The default Worker.max_run_time is 4.hours. If your job takes longer than that, another computer could pick it up. It's up to you to  
167 -make sure your job doesn't exceed this time. You should set this to the longest time you think the job could take.  
168 -  
169 -By default, it will delete failed jobs (and it always deletes successful jobs). If you want to keep failed jobs, set  
170 -Delayed::Worker.destroy_failed_jobs = false. The failed jobs will be marked with non-null failed_at.  
171 -  
172 -Here is an example of changing job parameters in Rails:  
173 -  
174 -<pre>  
175 -# config/initializers/delayed_job_config.rb  
176 -Delayed::Worker.destroy_failed_jobs = false  
177 -Delayed::Worker.sleep_delay = 60  
178 -Delayed::Worker.max_attempts = 3  
179 -Delayed::Worker.max_run_time = 5.minutes  
180 -</pre>  
181 -  
182 -h3. Cleaning up  
183 -  
184 -You can invoke @rake jobs:clear@ to delete all jobs in the queue.  
185 -  
186 -h2. Mailing List  
187 -  
188 -Join us on the mailing list at http://groups.google.com/group/delayed_job  
189 -  
190 -h2. How to contribute  
191 -  
192 -If you find what looks like a bug:  
193 -  
194 -# Check the GitHub issue tracker to see if anyone else has had the same issue.  
195 - http://github.com/collectiveidea/delayed_job/issues/  
196 -# If you don't see anything, create an issue with information on how to reproduce it.  
197 -  
198 -If you want to contribute an enhancement or a fix:  
199 -  
200 -# Fork the project on github.  
201 - http://github.com/collectiveidea/delayed_job/  
202 -# Make your changes with tests.  
203 -# Commit the changes without making changes to the Rakefile, VERSION, or any other files that aren't related to your enhancement or fix  
204 -# Send a pull request.  
205 -  
206 -h3. Changelog  
207 -  
208 -See http://wiki.github.com/collectiveidea/delayed_job/changelog for a list of changes.  
209 -  
vendor/plugins/delayed_job/Rakefile
1 # -*- encoding: utf-8 -*- 1 # -*- encoding: utf-8 -*-
2 -begin  
3 - require 'jeweler'  
4 -rescue LoadError  
5 - puts "Jeweler not available. Install it with: sudo gem install jeweler"  
6 - exit 1  
7 -end  
8 -  
9 -Jeweler::Tasks.new do |s|  
10 - s.name = "delayed_job"  
11 - s.summary = "Database-backed asynchronous priority queue system -- Extracted from Shopify"  
12 - s.email = "tobi@leetsoft.com"  
13 - s.homepage = "http://github.com/collectiveidea/delayed_job"  
14 - s.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.\n\nThis gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job)."  
15 - s.authors = ["Brandon Keepers", "Tobias Lütke"]  
16 -  
17 - s.has_rdoc = true  
18 - s.rdoc_options = ["--main", "README.textile", "--inline-source", "--line-numbers"]  
19 - s.extra_rdoc_files = ["README.textile"]  
20 -  
21 - s.test_files = Dir['spec/*_spec.rb']  
22 -  
23 - s.add_dependency "daemons"  
24 - s.add_development_dependency "rspec"  
25 - s.add_development_dependency "sqlite3-ruby"  
26 - s.add_development_dependency "activerecord"  
27 - s.add_development_dependency "mongo_mapper"  
28 - s.add_development_dependency "dm-core"  
29 - s.add_development_dependency "dm-observer"  
30 - s.add_development_dependency "dm-aggregates"  
31 - s.add_development_dependency "dm-validations"  
32 - s.add_development_dependency "do_sqlite3"  
33 - s.add_development_dependency "couchrest"  
34 -end  
35 -  
36 -require 'spec/rake/spectask'  
37 -  
38 -  
39 -task :default do  
40 - %w(2.3.5 3.0.0.beta3).each do |version|  
41 - puts "Running specs with Rails #{version}"  
42 - system("RAILS_VERSION=#{version} rake -s spec;")  
43 - end  
44 -end 2 +require 'bundler/setup'
  3 +Bundler::GemHelper.install_tasks
45 4
  5 +require 'rspec/core/rake_task'
46 desc 'Run the specs' 6 desc 'Run the specs'
47 -Spec::Rake::SpecTask.new(:spec) do |t|  
48 - t.libs << 'lib'  
49 - t.pattern = 'spec/*_spec.rb'  
50 - t.verbose = true 7 +RSpec::Core::RakeTask.new do |r|
  8 + r.verbose = false
51 end 9 end
52 -task :spec => :check_dependencies  
53 10
  11 +task :default => :spec
vendor/plugins/delayed_job/VERSION
@@ -1 +0,0 @@ @@ -1 +0,0 @@
1 -2.1.0.pre  
vendor/plugins/delayed_job/benchmarks.rb
1 -$:.unshift(File.dirname(__FILE__) + '/lib')  
2 -require 'rubygems' 1 +require 'spec/helper'
3 require 'logger' 2 require 'logger'
4 -require 'delayed_job'  
5 require 'benchmark' 3 require 'benchmark'
6 4
7 -RAILS_ENV = 'test'  
8 -  
9 -Delayed::Worker.logger = Logger.new('/dev/null')  
10 -  
11 -BACKENDS = []  
12 -Dir.glob("#{File.dirname(__FILE__)}/spec/setup/*.rb") do |backend|  
13 - begin  
14 - backend = File.basename(backend, '.rb')  
15 - require "spec/setup/#{backend}"  
16 - BACKENDS << backend.to_sym  
17 - rescue LoadError  
18 - puts "Unable to load #{backend} backend! #{$!}"  
19 - end  
20 -end  
21 - 5 +# Delayed::Worker.logger = Logger.new('/dev/null')
22 6
23 Benchmark.bm(10) do |x| 7 Benchmark.bm(10) do |x|
24 - BACKENDS.each do |backend|  
25 - require "spec/setup/#{backend}"  
26 - Delayed::Worker.backend = backend  
27 -  
28 - n = 10000  
29 - n.times { "foo".delay.length } 8 + Delayed::Job.delete_all
  9 + n = 10000
  10 + n.times { "foo".delay.length }
30 11
31 - x.report(backend.to_s) { Delayed::Worker.new(:quiet => true).work_off(n) }  
32 - end 12 + x.report { Delayed::Worker.new(:quiet => true).work_off(n) }
33 end 13 end
vendor/plugins/delayed_job/contrib/delayed_job.monitrc
@@ -11,4 +11,4 @@ @@ -11,4 +11,4 @@
11 check process delayed_job 11 check process delayed_job
12 with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.pid 12 with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.pid
13 start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start" 13 start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start"
14 - stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop"  
15 \ No newline at end of file 14 \ No newline at end of file
  15 + stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop"
vendor/plugins/delayed_job/contrib/delayed_job_multiple.monitrc
@@ -3,21 +3,32 @@ @@ -3,21 +3,32 @@
3 # To use: 3 # To use:
4 # 1. copy to /var/www/apps/{app_name}/shared/delayed_job.monitrc 4 # 1. copy to /var/www/apps/{app_name}/shared/delayed_job.monitrc
5 # 2. replace {app_name} as appropriate 5 # 2. replace {app_name} as appropriate
  6 +# you might also need to change the program strings to
  7 +# "/bin/su - {username} -c '/usr/bin/env ...'"
  8 +# to load your shell environment.
  9 +#
6 # 3. add this to your /etc/monit/monitrc 10 # 3. add this to your /etc/monit/monitrc
7 # 11 #
8 # include /var/www/apps/{app_name}/shared/delayed_job.monitrc 12 # include /var/www/apps/{app_name}/shared/delayed_job.monitrc
  13 +#
  14 +# The processes are grouped so that monit can act on them as a whole, e.g.
  15 +#
  16 +# monit -g delayed_job restart
9 17
10 check process delayed_job_0 18 check process delayed_job_0
11 with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.0.pid 19 with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.0.pid
12 start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 0" 20 start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 0"
13 stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 0" 21 stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 0"
14 - 22 + group delayed_job
  23 +
15 check process delayed_job_1 24 check process delayed_job_1
16 with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.1.pid 25 with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.1.pid
17 start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 1" 26 start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 1"
18 stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 1" 27 stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 1"
19 - 28 + group delayed_job
  29 +
20 check process delayed_job_2 30 check process delayed_job_2
21 with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.2.pid 31 with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.2.pid
22 start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 2" 32 start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 2"
23 - stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 2"  
24 \ No newline at end of file 33 \ No newline at end of file
  34 + stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 2"
  35 + group delayed_job
vendor/plugins/delayed_job/contrib/delayed_job_rails_4.monitrc 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +# an example Monit configuration file for delayed_job
  2 +# See: http://stackoverflow.com/questions/1226302/how-to-monitor-delayedjob-with-monit/1285611
  3 +#
  4 +# To use:
  5 +# 1. copy to /var/www/apps/{app_name}/shared/delayed_job.monitrc
  6 +# 2. replace {app_name} as appropriate
  7 +# 3. add this to your /etc/monit/monitrc
  8 +#
  9 +# include /var/www/apps/{app_name}/shared/delayed_job.monitrc
  10 +
  11 +check process delayed_job
  12 + with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.pid
  13 + start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job start"
  14 + stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job stop"
vendor/plugins/delayed_job/contrib/delayed_job_rails_4_multiple.monitrc 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +# an example Monit configuration file for delayed_job running multiple processes
  2 +#
  3 +# To use:
  4 +# 1. copy to /var/www/apps/{app_name}/shared/delayed_job.monitrc
  5 +# 2. replace {app_name} as appropriate
  6 +# you might also need to change the program strings to
  7 +# "/bin/su - {username} -c '/usr/bin/env ...'"
  8 +# to load your shell environment.
  9 +#
  10 +# 3. add this to your /etc/monit/monitrc
  11 +#
  12 +# include /var/www/apps/{app_name}/shared/delayed_job.monitrc
  13 +#
  14 +# The processes are grouped so that monit can act on them as a whole, e.g.
  15 +#
  16 +# monit -g delayed_job restart
  17 +
  18 +check process delayed_job_0
  19 + with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.0.pid
  20 + start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job start -i 0"
  21 + stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job stop -i 0"
  22 + group delayed_job
  23 +
  24 +check process delayed_job_1
  25 + with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.1.pid
  26 + start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job start -i 1"
  27 + stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job stop -i 1"
  28 + group delayed_job
  29 +
  30 +check process delayed_job_2
  31 + with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.2.pid
  32 + start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job start -i 2"
  33 + stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job stop -i 2"
  34 + group delayed_job
vendor/plugins/delayed_job/delayed_job.gemspec
1 -# Generated by jeweler  
2 -# DO NOT EDIT THIS FILE DIRECTLY  
3 -# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command  
4 # -*- encoding: utf-8 -*- 1 # -*- encoding: utf-8 -*-
5 2
6 -Gem::Specification.new do |s|  
7 - s.name = %q{delayed_job}  
8 - s.version = "2.1.0.pre"  
9 -  
10 - s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=  
11 - s.authors = ["Brandon Keepers", "Tobias L\303\274tke"]  
12 - s.date = %q{2010-05-21}  
13 - s.description = %q{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.  
14 -  
15 -This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job).}  
16 - s.email = %q{tobi@leetsoft.com}  
17 - s.extra_rdoc_files = [  
18 - "README.textile"  
19 - ]  
20 - s.files = [  
21 - ".gitignore",  
22 - "MIT-LICENSE",  
23 - "README.textile",  
24 - "Rakefile",  
25 - "VERSION",  
26 - "benchmarks.rb",  
27 - "contrib/delayed_job.monitrc",  
28 - "contrib/delayed_job_multiple.monitrc",  
29 - "delayed_job.gemspec",  
30 - "generators/delayed_job/delayed_job_generator.rb",  
31 - "generators/delayed_job/templates/migration.rb",  
32 - "generators/delayed_job/templates/script",  
33 - "init.rb",  
34 - "lib/delayed/backend/active_record.rb",  
35 - "lib/delayed/backend/base.rb",  
36 - "lib/delayed/backend/couch_rest.rb",  
37 - "lib/delayed/backend/data_mapper.rb",  
38 - "lib/delayed/backend/mongo_mapper.rb",  
39 - "lib/delayed/command.rb",  
40 - "lib/delayed/message_sending.rb",  
41 - "lib/delayed/performable_method.rb",  
42 - "lib/delayed/railtie.rb",  
43 - "lib/delayed/recipes.rb",  
44 - "lib/delayed/tasks.rb",  
45 - "lib/delayed/worker.rb",  
46 - "lib/delayed/yaml_ext.rb",  
47 - "lib/delayed_job.rb",  
48 - "lib/generators/delayed_job/delayed_job_generator.rb",  
49 - "lib/generators/delayed_job/templates/migration.rb",  
50 - "lib/generators/delayed_job/templates/script",  
51 - "rails/init.rb",  
52 - "recipes/delayed_job.rb",  
53 - "spec/autoloaded/clazz.rb",  
54 - "spec/autoloaded/struct.rb",  
55 - "spec/backend/active_record_job_spec.rb",  
56 - "spec/backend/couch_rest_job_spec.rb",  
57 - "spec/backend/data_mapper_job_spec.rb",  
58 - "spec/backend/mongo_mapper_job_spec.rb",  
59 - "spec/backend/shared_backend_spec.rb",  
60 - "spec/message_sending_spec.rb",  
61 - "spec/performable_method_spec.rb",  
62 - "spec/sample_jobs.rb",  
63 - "spec/setup/active_record.rb",  
64 - "spec/setup/couch_rest.rb",  
65 - "spec/setup/data_mapper.rb",  
66 - "spec/setup/mongo_mapper.rb",  
67 - "spec/spec_helper.rb",  
68 - "spec/worker_spec.rb",  
69 - "tasks/jobs.rake"  
70 - ]  
71 - s.homepage = %q{http://github.com/collectiveidea/delayed_job}  
72 - s.rdoc_options = ["--main", "README.textile", "--inline-source", "--line-numbers"]  
73 - s.require_paths = ["lib"]  
74 - s.rubygems_version = %q{1.3.6}  
75 - s.summary = %q{Database-backed asynchronous priority queue system -- Extracted from Shopify}  
76 - s.test_files = [  
77 - "spec/message_sending_spec.rb",  
78 - "spec/performable_method_spec.rb",  
79 - "spec/worker_spec.rb"  
80 - ]  
81 -  
82 - if s.respond_to? :specification_version then  
83 - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION  
84 - s.specification_version = 3  
85 -  
86 - if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then  
87 - s.add_runtime_dependency(%q<daemons>, [">= 0"])  
88 - s.add_development_dependency(%q<rspec>, [">= 0"])  
89 - s.add_development_dependency(%q<sqlite3-ruby>, [">= 0"])  
90 - s.add_development_dependency(%q<activerecord>, [">= 0"])  
91 - s.add_development_dependency(%q<mongo_mapper>, [">= 0"])  
92 - s.add_development_dependency(%q<dm-core>, [">= 0"])  
93 - s.add_development_dependency(%q<dm-observer>, [">= 0"])  
94 - s.add_development_dependency(%q<dm-aggregates>, [">= 0"])  
95 - s.add_development_dependency(%q<dm-validations>, [">= 0"])  
96 - s.add_development_dependency(%q<do_sqlite3>, [">= 0"])  
97 - s.add_development_dependency(%q<couchrest>, [">= 0"])  
98 - else  
99 - s.add_dependency(%q<daemons>, [">= 0"])  
100 - s.add_dependency(%q<rspec>, [">= 0"])  
101 - s.add_dependency(%q<sqlite3-ruby>, [">= 0"])  
102 - s.add_dependency(%q<activerecord>, [">= 0"])  
103 - s.add_dependency(%q<mongo_mapper>, [">= 0"])  
104 - s.add_dependency(%q<dm-core>, [">= 0"])  
105 - s.add_dependency(%q<dm-observer>, [">= 0"])  
106 - s.add_dependency(%q<dm-aggregates>, [">= 0"])  
107 - s.add_dependency(%q<dm-validations>, [">= 0"])  
108 - s.add_dependency(%q<do_sqlite3>, [">= 0"])  
109 - s.add_dependency(%q<couchrest>, [">= 0"])  
110 - end  
111 - else  
112 - s.add_dependency(%q<daemons>, [">= 0"])  
113 - s.add_dependency(%q<rspec>, [">= 0"])  
114 - s.add_dependency(%q<sqlite3-ruby>, [">= 0"])  
115 - s.add_dependency(%q<activerecord>, [">= 0"])  
116 - s.add_dependency(%q<mongo_mapper>, [">= 0"])  
117 - s.add_dependency(%q<dm-core>, [">= 0"])  
118 - s.add_dependency(%q<dm-observer>, [">= 0"])  
119 - s.add_dependency(%q<dm-aggregates>, [">= 0"])  
120 - s.add_dependency(%q<dm-validations>, [">= 0"])  
121 - s.add_dependency(%q<do_sqlite3>, [">= 0"])  
122 - s.add_dependency(%q<couchrest>, [">= 0"])  
123 - end 3 +Gem::Specification.new do |spec|
  4 + spec.add_dependency 'activesupport', ['>= 3.0', '< 4.1']
  5 + spec.authors = ["Brandon Keepers", "Brian Ryckbost", "Chris Gaffney", "David Genord II", "Erik Michaels-Ober", "Matt Griffin", "Steve Richert", "Tobias Lütke"]
  6 + 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."
  7 + spec.email = ['brian@collectiveidea.com']
  8 + spec.files = %w(CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md Rakefile delayed_job.gemspec)
  9 + spec.files += Dir.glob('{contrib,lib,recipes,spec}/**/*')
  10 + spec.homepage = 'http://github.com/collectiveidea/delayed_job'
  11 + spec.licenses = ['MIT']
  12 + spec.name = 'delayed_job'
  13 + spec.require_paths = ['lib']
  14 + spec.summary = 'Database-backed asynchronous priority queue system -- Extracted from Shopify'
  15 + spec.test_files = Dir.glob('spec/**/*')
  16 + spec.version = '4.0.0'
124 end 17 end
125 -  
vendor/plugins/delayed_job/generators/delayed_job/delayed_job_generator.rb
@@ -1,22 +0,0 @@ @@ -1,22 +0,0 @@
1 -class DelayedJobGenerator < Rails::Generator::Base  
2 - default_options :skip_migration => false  
3 -  
4 - def manifest  
5 - record do |m|  
6 - m.template 'script', 'script/delayed_job', :chmod => 0755  
7 - if !options[:skip_migration] && defined?(ActiveRecord)  
8 - m.migration_template "migration.rb", 'db/migrate',  
9 - :migration_file_name => "create_delayed_jobs"  
10 - end  
11 - end  
12 - end  
13 -  
14 -protected  
15 -  
16 - def add_options!(opt)  
17 - opt.separator ''  
18 - opt.separator 'Options:'  
19 - opt.on("--skip-migration", "Don't generate a migration") { |v| options[:skip_migration] = v }  
20 - end  
21 -  
22 -end  
vendor/plugins/delayed_job/generators/delayed_job/templates/migration.rb
@@ -1,21 +0,0 @@ @@ -1,21 +0,0 @@
1 -class CreateDelayedJobs < ActiveRecord::Migration  
2 - def self.up  
3 - create_table :delayed_jobs, :force => true do |table|  
4 - table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue  
5 - table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually.  
6 - table.text :handler # YAML-encoded string of the object that will do work  
7 - table.text :last_error # reason for last failure (See Note below)  
8 - table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.  
9 - table.datetime :locked_at # Set when a client is working on this object  
10 - table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)  
11 - table.string :locked_by # Who is working on this object (if locked)  
12 - table.timestamps  
13 - end  
14 -  
15 - add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'  
16 - end  
17 -  
18 - def self.down  
19 - drop_table :delayed_jobs  
20 - end  
21 -end  
22 \ No newline at end of file 0 \ No newline at end of file
vendor/plugins/delayed_job/init.rb
@@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
1 -require File.join(File.dirname(__FILE__), 'rails', 'init')  
2 -  
3 -config.after_initialize do  
4 - Delayed::Worker.guess_backend  
5 -end  
vendor/plugins/delayed_job/lib/delayed/backend/active_record.rb
@@ -1,101 +0,0 @@ @@ -1,101 +0,0 @@
1 -require 'active_record'  
2 -require 'active_record/version'  
3 -  
4 -class ActiveRecord::Base  
5 - yaml_as "tag:ruby.yaml.org,2002:ActiveRecord"  
6 -  
7 - def self.yaml_new(klass, tag, val)  
8 - klass.find(val['attributes']['id'])  
9 - rescue ActiveRecord::RecordNotFound  
10 - nil  
11 - end  
12 -  
13 - def to_yaml_properties  
14 - ['@attributes']  
15 - end  
16 -end  
17 -  
18 -module Delayed  
19 - module Backend  
20 - module ActiveRecord  
21 - # A job object that is persisted to the database.  
22 - # Contains the work object as a YAML field.  
23 - class Job < ::ActiveRecord::Base  
24 - include Delayed::Backend::Base  
25 -  
26 - attr_accessible :payload_object, :priority, :run_at  
27 -  
28 - set_table_name :delayed_jobs  
29 -  
30 - before_save :set_default_run_at  
31 -  
32 - if ::ActiveRecord::VERSION::MAJOR >= 3  
33 - scope :ready_to_run, lambda {|worker_name, max_run_time|  
34 - where(['(run_at <= ? AND (locked_at IS NULL OR locked_at < ?) OR locked_by = ?) AND failed_at IS NULL', db_time_now, db_time_now - max_run_time, worker_name])  
35 - }  
36 - scope :by_priority, order('priority ASC, run_at ASC')  
37 - else  
38 - scope :ready_to_run, lambda {|worker_name, max_run_time|  
39 - {:conditions => ['(run_at <= ? AND (locked_at IS NULL OR locked_at < ?) OR locked_by = ?) AND failed_at IS NULL', db_time_now, db_time_now - max_run_time, worker_name]}  
40 - }  
41 - scope :by_priority, :order => 'priority ASC, run_at ASC'  
42 - end  
43 -  
44 - def self.after_fork  
45 - ::ActiveRecord::Base.connection.reconnect!  
46 - end  
47 -  
48 - # When a worker is exiting, make sure we don't have any locked jobs.  
49 - def self.clear_locks!(worker_name)  
50 - update_all("locked_by = null, locked_at = null", ["locked_by = ?", worker_name])  
51 - end  
52 -  
53 - # Find a few candidate jobs to run (in case some immediately get locked by others).  
54 - def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)  
55 - scope = self.ready_to_run(worker_name, max_run_time)  
56 - scope = scope.scoped(:conditions => ['priority >= ?', Worker.min_priority]) if Worker.min_priority  
57 - scope = scope.scoped(:conditions => ['priority <= ?', Worker.max_priority]) if Worker.max_priority  
58 -  
59 - ::ActiveRecord::Base.silence do  
60 - scope.by_priority.all(:limit => limit)  
61 - end  
62 - end  
63 -  
64 - # Lock this job for this worker.  
65 - # Returns true if we have the lock, false otherwise.  
66 - def lock_exclusively!(max_run_time, worker)  
67 - now = self.class.db_time_now  
68 - affected_rows = if locked_by != worker  
69 - # We don't own this job so we will update the locked_by name and the locked_at  
70 - self.class.update_all(["locked_at = ?, locked_by = ?", now, worker], ["id = ? and (locked_at is null or locked_at < ?) and (run_at <= ?)", id, (now - max_run_time.to_i), now])  
71 - else  
72 - # We already own this job, this may happen if the job queue crashes.  
73 - # Simply resume and update the locked_at  
74 - self.class.update_all(["locked_at = ?", now], ["id = ? and locked_by = ?", id, worker])  
75 - end  
76 - if affected_rows == 1  
77 - self.locked_at = now  
78 - self.locked_by = worker  
79 - return true  
80 - else  
81 - return false  
82 - end  
83 - end  
84 -  
85 - # Get the current time (GMT or local depending on DB)  
86 - # Note: This does not ping the DB to get the time, so all your clients  
87 - # must have syncronized clocks.  
88 - def self.db_time_now  
89 - if Time.zone  
90 - Time.zone.now  
91 - elsif ::ActiveRecord::Base.default_timezone == :utc  
92 - Time.now.utc  
93 - else  
94 - Time.now  
95 - end  
96 - end  
97 -  
98 - end  
99 - end  
100 - end  
101 -end  
vendor/plugins/delayed_job/lib/delayed/backend/base.rb
1 module Delayed 1 module Delayed
2 module Backend 2 module Backend
3 - class DeserializationError < StandardError  
4 - end  
5 -  
6 module Base 3 module Base
7 def self.included(base) 4 def self.included(base)
8 base.extend ClassMethods 5 base.extend ClassMethods
9 end 6 end
10 - 7 +
11 module ClassMethods 8 module ClassMethods
12 # Add a job to the queue 9 # Add a job to the queue
13 def enqueue(*args) 10 def enqueue(*args)
14 - object = args.shift  
15 - unless object.respond_to?(:perform) 11 + options = {
  12 + :priority => Delayed::Worker.default_priority,
  13 + :queue => Delayed::Worker.default_queue_name
  14 + }.merge!(args.extract_options!)
  15 +
  16 + options[:payload_object] ||= args.shift
  17 +
  18 + if args.size > 0
  19 + warn "[DEPRECATION] Passing multiple arguments to `#enqueue` is deprecated. Pass a hash with :priority and :run_at."
  20 + options[:priority] = args.first || options[:priority]
  21 + options[:run_at] = args[1]
  22 + end
  23 +
  24 + unless options[:payload_object].respond_to?(:perform)
16 raise ArgumentError, 'Cannot enqueue items which do not respond to perform' 25 raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
17 end 26 end
18 -  
19 - priority = args.first || Delayed::Worker.default_priority  
20 - run_at = args[1]  
21 - self.create(:payload_object => object, :priority => priority.to_i, :run_at => run_at) 27 +
  28 + if Delayed::Worker.delay_jobs
  29 + self.new(options).tap do |job|
  30 + Delayed::Worker.lifecycle.run_callbacks(:enqueue, job) do
  31 + job.hook(:enqueue)
  32 + job.save
  33 + end
  34 + end
  35 + else
  36 + Delayed::Job.new(:payload_object => options[:payload_object]).tap do |job|
  37 + job.invoke_job
  38 + end
  39 + end
22 end 40 end
23 - 41 +
  42 + def reserve(worker, max_run_time = Worker.max_run_time)
  43 + # We get up to 5 jobs from the db. In case we cannot get exclusive access to a job we try the next.
  44 + # this leads to a more even distribution of jobs across the worker processes
  45 + find_available(worker.name, worker.read_ahead, max_run_time).detect do |job|
  46 + job.lock_exclusively!(max_run_time, worker.name)
  47 + end
  48 + end
  49 +
  50 + # Allow the backend to attempt recovery from reserve errors
  51 + def recover_from(error)
  52 + end
  53 +
24 # Hook method that is called before a new worker is forked 54 # Hook method that is called before a new worker is forked
25 def before_fork 55 def before_fork
26 end 56 end
27 - 57 +
28 # Hook method that is called after a new worker is forked 58 # Hook method that is called after a new worker is forked
29 def after_fork 59 def after_fork
30 end 60 end
31 - 61 +
32 def work_off(num = 100) 62 def work_off(num = 100)
33 warn "[DEPRECATION] `Delayed::Job.work_off` is deprecated. Use `Delayed::Worker.new.work_off instead." 63 warn "[DEPRECATION] `Delayed::Job.work_off` is deprecated. Use `Delayed::Worker.new.work_off instead."
34 Delayed::Worker.new.work_off(num) 64 Delayed::Worker.new.work_off(num)
35 end 65 end
36 end 66 end
37 -  
38 - ParseObjectFromYaml = /\!ruby\/\w+\:([^\s]+)/  
39 67
40 def failed? 68 def failed?
41 - failed_at 69 + !!failed_at
42 end 70 end
43 alias_method :failed, :failed? 71 alias_method :failed, :failed?
44 72
  73 + ParseObjectFromYaml = /\!ruby\/\w+\:([^\s]+)/
  74 +
45 def name 75 def name
46 - @name ||= begin  
47 - payload = payload_object  
48 - payload.respond_to?(:display_name) ? payload.display_name : payload.class.name  
49 - end 76 + @name ||= payload_object.respond_to?(:display_name) ?
  77 + payload_object.display_name :
  78 + payload_object.class.name
  79 + rescue DeserializationError
  80 + ParseObjectFromYaml.match(handler)[1]
50 end 81 end
51 82
52 def payload_object=(object) 83 def payload_object=(object)
  84 + @payload_object = object
53 self.handler = object.to_yaml 85 self.handler = object.to_yaml
54 end 86 end
55 - 87 +
56 def payload_object 88 def payload_object
57 - @payload_object ||= YAML.load(self.handler)  
58 - rescue TypeError, LoadError, NameError => e  
59 - raise DeserializationError,  
60 - "Job failed to load: #{e.message}. Try to manually require the required file. Handler: #{handler.inspect}" 89 + if YAML.respond_to?(:unsafe_load)
  90 + #See https://github.com/dtao/safe_yaml
  91 + #When the method is there, we need to load our YAML like this...
  92 + @payload_object ||= YAML.load(self.handler, :safe => false)
  93 + else
  94 + @payload_object ||= YAML.load(self.handler)
  95 + end
  96 + rescue TypeError, LoadError, NameError, ArgumentError => e
  97 + raise DeserializationError,
  98 + "Job failed to load: #{e.message}. Handler: #{handler.inspect}"
61 end 99 end
62 100
63 - # Moved into its own method so that new_relic can trace it.  
64 def invoke_job 101 def invoke_job
65 - payload_object.perform 102 + Delayed::Worker.lifecycle.run_callbacks(:invoke_job, self) do
  103 + begin
  104 + hook :before
  105 + payload_object.perform
  106 + hook :success
  107 + rescue Exception => e
  108 + hook :error, e
  109 + raise e
  110 + ensure
  111 + hook :after
  112 + end
  113 + end
66 end 114 end
67 - 115 +
68 # Unlock this job (note: not saved to DB) 116 # Unlock this job (note: not saved to DB)
69 def unlock 117 def unlock
70 self.locked_at = nil 118 self.locked_at = nil
71 self.locked_by = nil 119 self.locked_by = nil
72 end 120 end
73 - 121 +
  122 + def hook(name, *args)
  123 + if payload_object.respond_to?(name)
  124 + method = payload_object.method(name)
  125 + method.arity == 0 ? method.call : method.call(self, *args)
  126 + end
  127 + rescue DeserializationError
  128 + # do nothing
  129 + end
  130 +
  131 + def reschedule_at
  132 + payload_object.respond_to?(:reschedule_at) ?
  133 + payload_object.reschedule_at(self.class.db_time_now, attempts) :
  134 + self.class.db_time_now + (attempts ** 4) + 5
  135 + end
  136 +
  137 + def max_attempts
  138 + payload_object.max_attempts if payload_object.respond_to?(:max_attempts)
  139 + end
  140 +
  141 + def fail!
  142 + update_attributes(:failed_at => self.class.db_time_now)
  143 + end
  144 +
74 protected 145 protected
75 146
76 def set_default_run_at 147 def set_default_run_at
77 self.run_at ||= self.class.db_time_now 148 self.run_at ||= self.class.db_time_now
78 end 149 end
79 - 150 +
  151 + # Call during reload operation to clear out internal state
  152 + def reset
  153 + @payload_object = nil
  154 + end
80 end 155 end
81 end 156 end
82 end 157 end
vendor/plugins/delayed_job/lib/delayed/backend/couch_rest.rb
@@ -1,109 +0,0 @@ @@ -1,109 +0,0 @@
1 -require 'couchrest'  
2 -  
3 -#extent couchrest to handle delayed_job serialization.  
4 -class CouchRest::ExtendedDocument  
5 - yaml_as "tag:ruby.yaml.org,2002:CouchRest"  
6 -  
7 - def reload  
8 - job = self.class.get self['_id']  
9 - job.each {|k,v| self[k] = v}  
10 - end  
11 - def self.find(id)  
12 - get id  
13 - end  
14 - def self.yaml_new(klass, tag, val)  
15 - klass.get(val['_id'])  
16 - end  
17 - def ==(other)  
18 - if other.is_a? ::CouchRest::ExtendedDocument  
19 - self['_id'] == other['_id']  
20 - else  
21 - super  
22 - end  
23 - end  
24 -end  
25 -  
26 -#couchrest adapter  
27 -module Delayed  
28 - module Backend  
29 - module CouchRest  
30 - class Job < ::CouchRest::ExtendedDocument  
31 - include Delayed::Backend::Base  
32 - use_database ::CouchRest::Server.new.database('delayed_job')  
33 -  
34 - property :handler  
35 - property :last_error  
36 - property :locked_by  
37 - property :priority, :default => 0  
38 - property :attempts, :default => 0  
39 - property :run_at, :cast_as => 'Time'  
40 - property :locked_at, :cast_as => 'Time'  
41 - property :failed_at, :cast_as => 'Time'  
42 - timestamps!  
43 -  
44 - set_callback :save, :before, :set_default_run_at  
45 -  
46 - view_by(:failed_at, :locked_by, :run_at,  
47 - :map => "function(doc){" +  
48 - " if(doc['couchrest-type'] == 'Delayed::Backend::CouchRest::Job') {" +  
49 - " emit([doc.failed_at || null, doc.locked_by || null, doc.run_at || null], null);}" +  
50 - " }")  
51 - view_by(:failed_at, :locked_at, :run_at,  
52 - :map => "function(doc){" +  
53 - " if(doc['couchrest-type'] == 'Delayed::Backend::CouchRest::Job') {" +  
54 - " emit([doc.failed_at || null, doc.locked_at || null, doc.run_at || null], null);}" +  
55 - " }")  
56 -  
57 - def self.db_time_now; Time.now; end  
58 - def self.find_available(worker_name, limit = 5, max_run_time = ::Delayed::Worker.max_run_time)  
59 - ready = ready_jobs  
60 - mine = my_jobs worker_name  
61 - expire = expired_jobs max_run_time  
62 - jobs = (ready + mine + expire)[0..limit-1].sort_by { |j| j.priority }  
63 - jobs = jobs.find_all { |j| j.priority >= Worker.min_priority } if Worker.min_priority  
64 - jobs = jobs.find_all { |j| j.priority <= Worker.max_priority } if Worker.max_priority  
65 - jobs  
66 - end  
67 - def self.clear_locks!(worker_name)  
68 - jobs = my_jobs worker_name  
69 - jobs.each { |j| j.locked_by, j.locked_at = nil, nil; }  
70 - database.bulk_save jobs  
71 - end  
72 - def self.delete_all  
73 - database.bulk_save all.each { |doc| doc['_deleted'] = true }  
74 - end  
75 -  
76 - def lock_exclusively!(max_run_time, worker = worker_name)  
77 - return false if locked_by_other?(worker) and not expired?(max_run_time)  
78 - case  
79 - when locked_by_me?(worker)  
80 - self.locked_at = self.class.db_time_now  
81 - when (unlocked? or (locked_by_other?(worker) and expired?(max_run_time)))  
82 - self.locked_at, self.locked_by = self.class.db_time_now, worker  
83 - end  
84 - save  
85 - rescue RestClient::Conflict  
86 - false  
87 - end  
88 -  
89 - private  
90 - def self.ready_jobs  
91 - options = {:startkey => [nil, nil], :endkey => [nil, nil, db_time_now]}  
92 - by_failed_at_and_locked_by_and_run_at options  
93 - end  
94 - def self.my_jobs(worker_name)  
95 - options = {:startkey => [nil, worker_name], :endkey => [nil, worker_name, {}]}  
96 - by_failed_at_and_locked_by_and_run_at options  
97 - end  
98 - def self.expired_jobs(max_run_time)  
99 - options = {:startkey => [nil,'0'], :endkey => [nil, db_time_now - max_run_time, db_time_now]}  
100 - by_failed_at_and_locked_at_and_run_at options  
101 - end  
102 - def unlocked?; locked_by.nil?; end  
103 - def expired?(time); locked_at < self.class.db_time_now - time; end  
104 - def locked_by_me?(worker); not locked_by.nil? and locked_by == worker; end  
105 - def locked_by_other?(worker); not locked_by.nil? and locked_by != worker; end  
106 - end  
107 - end  
108 - end  
109 -end  
vendor/plugins/delayed_job/lib/delayed/backend/data_mapper.rb
@@ -1,121 +0,0 @@ @@ -1,121 +0,0 @@
1 -require 'dm-core'  
2 -require 'dm-observer'  
3 -require 'dm-aggregates'  
4 -  
5 -DataMapper::Resource.class_eval do  
6 - yaml_as "tag:ruby.yaml.org,2002:DataMapper"  
7 -  
8 - def self.yaml_new(klass, tag, val)  
9 - klass.find(val['id'])  
10 - end  
11 -  
12 - def to_yaml_properties  
13 - ['@id']  
14 - end  
15 -end  
16 -  
17 -module Delayed  
18 - module Backend  
19 - module DataMapper  
20 - class Job  
21 - include ::DataMapper::Resource  
22 - include Delayed::Backend::Base  
23 -  
24 - storage_names[:default] = 'delayed_jobs'  
25 -  
26 - property :id, Serial  
27 - property :priority, Integer, :default => 0, :index => :run_at_priority  
28 - property :attempts, Integer, :default => 0  
29 - property :handler, Text, :lazy => false  
30 - property :run_at, Time, :index => :run_at_priority  
31 - property :locked_at, Time, :index => true  
32 - property :locked_by, String  
33 - property :failed_at, Time  
34 - property :last_error, Text  
35 -  
36 - def self.db_time_now  
37 - Time.now  
38 - end  
39 -  
40 - def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)  
41 -  
42 - simple_conditions = { :run_at.lte => db_time_now, :limit => limit, :failed_at => nil, :order => [:priority.asc, :run_at.asc] }  
43 -  
44 - # respect priorities  
45 - simple_conditions[:priority.gte] = Worker.min_priority if Worker.min_priority  
46 - simple_conditions[:priority.lte] = Worker.max_priority if Worker.max_priority  
47 -  
48 - # lockable  
49 - lockable = (  
50 - # not locked or past the max time  
51 - ( all(:locked_at => nil ) | all(:locked_at.lt => db_time_now - max_run_time)) |  
52 -  
53 - # OR locked by our worker  
54 - all(:locked_by => worker_name))  
55 -  
56 - # plus some other boring junk  
57 - (lockable).all( simple_conditions )  
58 - end  
59 -  
60 - # When a worker is exiting, make sure we don't have any locked jobs.  
61 - def self.clear_locks!(worker_name)  
62 - all(:locked_by => worker_name).update(:locked_at => nil, :locked_by => nil)  
63 - end  
64 -  
65 - # Lock this job for this worker.  
66 - # Returns true if we have the lock, false otherwise.  
67 - def lock_exclusively!(max_run_time, worker = worker_name)  
68 -  
69 - now = self.class.db_time_now  
70 - overtime = now - max_run_time  
71 -  
72 - # FIXME - this is a bit gross  
73 - # DM doesn't give us the number of rows affected by a collection update  
74 - # so we have to circumvent some niceness in DM::Collection here  
75 - collection = locked_by != worker ?  
76 - (self.class.all(:id => id, :run_at.lte => now) & ( self.class.all(:locked_at => nil) | self.class.all(:locked_at.lt => overtime) ) ) :  
77 - self.class.all(:id => id, :locked_by => worker)  
78 -  
79 - attributes = collection.model.new(:locked_at => now, :locked_by => worker).dirty_attributes  
80 - affected_rows = self.repository.update(attributes, collection)  
81 -  
82 - if affected_rows == 1  
83 - self.locked_at = now  
84 - self.locked_by = worker  
85 - return true  
86 - else  
87 - return false  
88 - end  
89 - end  
90 -  
91 - # these are common to the other backends, so we provide an implementation  
92 - def self.delete_all  
93 - Delayed::Job.auto_migrate!  
94 - end  
95 -  
96 - def self.find id  
97 - get id  
98 - end  
99 -  
100 - def update_attributes(attributes)  
101 - attributes.each do |k,v|  
102 - self[k] = v  
103 - end  
104 - self.save  
105 - end  
106 -  
107 -  
108 - end  
109 -  
110 - class JobObserver  
111 - include ::DataMapper::Observer  
112 -  
113 - observe Job  
114 -  
115 - before :save do  
116 - self.run_at ||= self.class.db_time_now  
117 - end  
118 - end  
119 - end  
120 - end  
121 -end  
vendor/plugins/delayed_job/lib/delayed/backend/mongo_mapper.rb
@@ -1,106 +0,0 @@ @@ -1,106 +0,0 @@
1 -require 'mongo_mapper'  
2 -  
3 -MongoMapper::Document.class_eval do  
4 - yaml_as "tag:ruby.yaml.org,2002:MongoMapper"  
5 -  
6 - def self.yaml_new(klass, tag, val)  
7 - klass.find(val['_id'])  
8 - end  
9 -  
10 - def to_yaml_properties  
11 - ['@_id']  
12 - end  
13 -end  
14 -  
15 -module Delayed  
16 - module Backend  
17 - module MongoMapper  
18 - class Job  
19 - include ::MongoMapper::Document  
20 - include Delayed::Backend::Base  
21 - set_collection_name 'delayed_jobs'  
22 -  
23 - key :priority, Integer, :default => 0  
24 - key :attempts, Integer, :default => 0  
25 - key :handler, String  
26 - key :run_at, Time  
27 - key :locked_at, Time  
28 - key :locked_by, String, :index => true  
29 - key :failed_at, Time  
30 - key :last_error, String  
31 - timestamps!  
32 -  
33 - before_save :set_default_run_at  
34 -  
35 - ensure_index [[:priority, 1], [:run_at, 1]]  
36 -  
37 - def self.before_fork  
38 - ::MongoMapper.connection.close  
39 - end  
40 -  
41 - def self.after_fork  
42 - ::MongoMapper.connect(RAILS_ENV)  
43 - end  
44 -  
45 - def self.db_time_now  
46 - Time.now.utc  
47 - end  
48 -  
49 - def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)  
50 - right_now = db_time_now  
51 -  
52 - conditions = {  
53 - :run_at => {"$lte" => right_now},  
54 - :limit => -limit, # In mongo, positive limits are 'soft' and negative are 'hard'  
55 - :failed_at => nil,  
56 - :sort => [['priority', 1], ['run_at', 1]]  
57 - }  
58 -  
59 - where = "this.locked_at == null || this.locked_at < #{make_date(right_now - max_run_time)}"  
60 -  
61 - (conditions[:priority] ||= {})['$gte'] = Worker.min_priority.to_i if Worker.min_priority  
62 - (conditions[:priority] ||= {})['$lte'] = Worker.max_priority.to_i if Worker.max_priority  
63 -  
64 - results = all(conditions.merge(:locked_by => worker_name))  
65 - results += all(conditions.merge('$where' => where)) if results.size < limit  
66 - results  
67 - end  
68 -  
69 - # When a worker is exiting, make sure we don't have any locked jobs.  
70 - def self.clear_locks!(worker_name)  
71 - collection.update({:locked_by => worker_name}, {"$set" => {:locked_at => nil, :locked_by => nil}}, :multi => true)  
72 - end  
73 -  
74 - # Lock this job for this worker.  
75 - # Returns true if we have the lock, false otherwise.  
76 - def lock_exclusively!(max_run_time, worker = worker_name)  
77 - right_now = self.class.db_time_now  
78 - overtime = right_now - max_run_time.to_i  
79 -  
80 - query = "this.locked_at == null || this.locked_at < #{make_date(overtime)} || this.locked_by == #{worker.to_json}"  
81 - conditions = {:_id => id, :run_at => {"$lte" => right_now}, "$where" => query}  
82 -  
83 - collection.update(conditions, {"$set" => {:locked_at => right_now, :locked_by => worker}})  
84 - affected_rows = collection.find({:_id => id, :locked_by => worker}).count  
85 - if affected_rows == 1  
86 - self.locked_at = right_now  
87 - self.locked_by = worker  
88 - return true  
89 - else  
90 - return false  
91 - end  
92 - end  
93 -  
94 - private  
95 -  
96 - def self.make_date(date_or_seconds)  
97 - "new Date(#{date_or_seconds.to_f * 1000})"  
98 - end  
99 -  
100 - def make_date(date)  
101 - self.class.make_date(date)  
102 - end  
103 - end  
104 - end  
105 - end  
106 -end  
vendor/plugins/delayed_job/lib/delayed/backend/shared_spec.rb 0 → 100644
@@ -0,0 +1,594 @@ @@ -0,0 +1,594 @@
  1 +require File.expand_path('../../../../spec/sample_jobs', __FILE__)
  2 +
  3 +require 'active_support/core_ext'
  4 +
  5 +shared_examples_for "a delayed_job backend" do
  6 + let(:worker) { Delayed::Worker.new }
  7 +
  8 + def create_job(opts = {})
  9 + described_class.create(opts.merge(:payload_object => SimpleJob.new))
  10 + end
  11 +
  12 + before do
  13 + Delayed::Worker.max_priority = nil
  14 + Delayed::Worker.min_priority = nil
  15 + Delayed::Worker.default_priority = 99
  16 + Delayed::Worker.delay_jobs = true
  17 + SimpleJob.runs = 0
  18 + described_class.delete_all
  19 + end
  20 +
  21 + after do
  22 + Delayed::Worker.reset
  23 + end
  24 +
  25 + it "sets run_at automatically if not set" do
  26 + expect(described_class.create(:payload_object => ErrorJob.new ).run_at).not_to be_nil
  27 + end
  28 +
  29 + it "does not set run_at automatically if already set" do
  30 + later = described_class.db_time_now + 5.minutes
  31 + job = described_class.create(:payload_object => ErrorJob.new, :run_at => later)
  32 + expect(job.run_at).to be_within(1).of(later)
  33 + end
  34 +
  35 + describe "#reload" do
  36 + it "reloads the payload" do
  37 + job = described_class.enqueue :payload_object => SimpleJob.new
  38 + expect(job.payload_object.object_id).not_to eq(job.reload.payload_object.object_id)
  39 + end
  40 + end
  41 +
  42 + describe "enqueue" do
  43 + context "with a hash" do
  44 + it "raises ArgumentError when handler doesn't respond_to :perform" do
  45 + expect{described_class.enqueue(:payload_object => Object.new)}.to raise_error(ArgumentError)
  46 + end
  47 +
  48 + it "is able to set priority" do
  49 + job = described_class.enqueue :payload_object => SimpleJob.new, :priority => 5
  50 + expect(job.priority).to eq(5)
  51 + end
  52 +
  53 + it "uses default priority" do
  54 + job = described_class.enqueue :payload_object => SimpleJob.new
  55 + expect(job.priority).to eq(99)
  56 + end
  57 +
  58 + it "is able to set run_at" do
  59 + later = described_class.db_time_now + 5.minutes
  60 + job = described_class.enqueue :payload_object => SimpleJob.new, :run_at => later
  61 + expect(job.run_at).to be_within(1).of(later)
  62 + end
  63 +
  64 + it "is able to set queue" do
  65 + job = described_class.enqueue :payload_object => SimpleJob.new, :queue => 'tracking'
  66 + expect(job.queue).to eq('tracking')
  67 + end
  68 + end
  69 +
  70 + context "with multiple arguments" do
  71 + it "raises ArgumentError when handler doesn't respond_to :perform" do
  72 + expect{described_class.enqueue(Object.new)}.to raise_error(ArgumentError)
  73 + end
  74 +
  75 + it "increases count after enqueuing items" do
  76 + described_class.enqueue SimpleJob.new
  77 + expect(described_class.count).to eq(1)
  78 + end
  79 +
  80 + it "is able to set priority [DEPRECATED]" do
  81 + silence_warnings do
  82 + job = described_class.enqueue SimpleJob.new, 5
  83 + expect(job.priority).to eq(5)
  84 + end
  85 + end
  86 +
  87 + it "uses default priority when it is not set" do
  88 + @job = described_class.enqueue SimpleJob.new
  89 + expect(@job.priority).to eq(99)
  90 + end
  91 +
  92 + it "is able to set run_at [DEPRECATED]" do
  93 + silence_warnings do
  94 + later = described_class.db_time_now + 5.minutes
  95 + @job = described_class.enqueue SimpleJob.new, 5, later
  96 + expect(@job.run_at).to be_within(1).of(later)
  97 + end
  98 + end
  99 +
  100 + it "works with jobs in modules" do
  101 + M::ModuleJob.runs = 0
  102 + job = described_class.enqueue M::ModuleJob.new
  103 + expect{job.invoke_job}.to change { M::ModuleJob.runs }.from(0).to(1)
  104 + end
  105 + end
  106 +
  107 + context "with delay_jobs = false" do
  108 + before(:each) do
  109 + Delayed::Worker.delay_jobs = false
  110 + end
  111 +
  112 + it "does not increase count after enqueuing items" do
  113 + described_class.enqueue SimpleJob.new
  114 + expect(described_class.count).to eq(0)
  115 + end
  116 +
  117 + it "invokes the enqueued job" do
  118 + job = SimpleJob.new
  119 + job.should_receive(:perform)
  120 + described_class.enqueue job
  121 + end
  122 +
  123 + it "returns a job, not the result of invocation" do
  124 + expect(described_class.enqueue(SimpleJob.new)).to be_instance_of(described_class)
  125 + end
  126 + end
  127 + end
  128 +
  129 + describe "callbacks" do
  130 + before(:each) do
  131 + CallbackJob.messages = []
  132 + end
  133 +
  134 + %w(before success after).each do |callback|
  135 + it "calls #{callback} with job" do
  136 + job = described_class.enqueue(CallbackJob.new)
  137 + job.payload_object.should_receive(callback).with(job)
  138 + job.invoke_job
  139 + end
  140 + end
  141 +
  142 + it "calls before and after callbacks" do
  143 + job = described_class.enqueue(CallbackJob.new)
  144 + expect(CallbackJob.messages).to eq(["enqueue"])
  145 + job.invoke_job
  146 + expect(CallbackJob.messages).to eq(["enqueue", "before", "perform", "success", "after"])
  147 + end
  148 +
  149 + it "calls the after callback with an error" do
  150 + job = described_class.enqueue(CallbackJob.new)
  151 + job.payload_object.should_receive(:perform).and_raise(RuntimeError.new("fail"))
  152 +
  153 + expect{job.invoke_job}.to raise_error
  154 + expect(CallbackJob.messages).to eq(["enqueue", "before", "error: RuntimeError", "after"])
  155 + end
  156 +
  157 + it "calls error when before raises an error" do
  158 + job = described_class.enqueue(CallbackJob.new)
  159 + job.payload_object.should_receive(:before).and_raise(RuntimeError.new("fail"))
  160 + expect{job.invoke_job}.to raise_error(RuntimeError)
  161 + expect(CallbackJob.messages).to eq(["enqueue", "error: RuntimeError", "after"])
  162 + end
  163 + end
  164 +
  165 + describe "payload_object" do
  166 + it "raises a DeserializationError when the job class is totally unknown" do
  167 + job = described_class.new :handler => "--- !ruby/object:JobThatDoesNotExist {}"
  168 + expect{job.payload_object}.to raise_error(Delayed::DeserializationError)
  169 + end
  170 +
  171 + it "raises a DeserializationError when the job struct is totally unknown" do
  172 + job = described_class.new :handler => "--- !ruby/struct:StructThatDoesNotExist {}"
  173 + expect{job.payload_object}.to raise_error(Delayed::DeserializationError)
  174 + end
  175 +
  176 + it "raises a DeserializationError when the YAML.load raises argument error" do
  177 + job = described_class.new :handler => "--- !ruby/struct:GoingToRaiseArgError {}"
  178 + YAML.should_receive(:load).and_raise(ArgumentError)
  179 + expect{job.payload_object}.to raise_error(Delayed::DeserializationError)
  180 + end
  181 + end
  182 +
  183 + describe "reserve" do
  184 + before do
  185 + Delayed::Worker.max_run_time = 2.minutes
  186 + end
  187 +
  188 + after do
  189 + Time.zone = nil
  190 + end
  191 +
  192 + it "does not reserve failed jobs" do
  193 + create_job :attempts => 50, :failed_at => described_class.db_time_now
  194 + expect(described_class.reserve(worker)).to be_nil
  195 + end
  196 +
  197 + it "does not reserve jobs scheduled for the future" do
  198 + create_job :run_at => described_class.db_time_now + 1.minute
  199 + expect(described_class.reserve(worker)).to be_nil
  200 + end
  201 +
  202 + it "reserves jobs scheduled for the past" do
  203 + job = create_job :run_at => described_class.db_time_now - 1.minute
  204 + expect(described_class.reserve(worker)).to eq(job)
  205 + end
  206 +
  207 + it "reserves jobs scheduled for the past when time zones are involved" do
  208 + Time.zone = 'US/Eastern'
  209 + job = create_job :run_at => described_class.db_time_now - 1.minute
  210 + expect(described_class.reserve(worker)).to eq(job)
  211 + end
  212 +
  213 + it "does not reserve jobs locked by other workers" do
  214 + job = create_job
  215 + other_worker = Delayed::Worker.new
  216 + other_worker.name = 'other_worker'
  217 + expect(described_class.reserve(other_worker)).to eq(job)
  218 + expect(described_class.reserve(worker)).to be_nil
  219 + end
  220 +
  221 + it "reserves open jobs" do
  222 + job = create_job
  223 + expect(described_class.reserve(worker)).to eq(job)
  224 + end
  225 +
  226 + it "reserves expired jobs" do
  227 + job = create_job(:locked_by => 'some other worker', :locked_at => described_class.db_time_now - Delayed::Worker.max_run_time - 1.minute)
  228 + expect(described_class.reserve(worker)).to eq(job)
  229 + end
  230 +
  231 + it "reserves own jobs" do
  232 + job = create_job(:locked_by => worker.name, :locked_at => (described_class.db_time_now - 1.minutes))
  233 + expect(described_class.reserve(worker)).to eq(job)
  234 + end
  235 + end
  236 +
  237 + context "#name" do
  238 + it "is the class name of the job that was enqueued" do
  239 + expect(described_class.create(:payload_object => ErrorJob.new ).name).to eq('ErrorJob')
  240 + end
  241 +
  242 + it "is the method that will be called if its a performable method object" do
  243 + job = described_class.new(:payload_object => NamedJob.new)
  244 + expect(job.name).to eq('named_job')
  245 + end
  246 +
  247 + it "is the instance method that will be called if its a performable method object" do
  248 + job = Story.create(:text => "...").delay.save
  249 + expect(job.name).to eq('Story#save')
  250 + end
  251 +
  252 + it "parses from handler on deserialization error" do
  253 + job = Story.create(:text => "...").delay.text
  254 + job.payload_object.object.destroy
  255 + expect(job.reload.name).to eq('Delayed::PerformableMethod')
  256 + end
  257 + end
  258 +
  259 + context "worker prioritization" do
  260 + after do
  261 + Delayed::Worker.max_priority = nil
  262 + Delayed::Worker.min_priority = nil
  263 + end
  264 +
  265 + it "fetches jobs ordered by priority" do
  266 + 10.times { described_class.enqueue SimpleJob.new, :priority => rand(10) }
  267 + jobs = []
  268 + 10.times { jobs << described_class.reserve(worker) }
  269 + expect(jobs.size).to eq(10)
  270 + jobs.each_cons(2) do |a, b|
  271 + expect(a.priority).to be <= b.priority
  272 + end
  273 + end
  274 +
  275 + it "only finds jobs greater than or equal to min priority" do
  276 + min = 5
  277 + Delayed::Worker.min_priority = min
  278 + [4,5,6].sort_by {|i| rand }.each {|i| create_job :priority => i }
  279 + 2.times do
  280 + job = described_class.reserve(worker)
  281 + expect(job.priority).to be >= min
  282 + job.destroy
  283 + end
  284 + expect(described_class.reserve(worker)).to be_nil
  285 + end
  286 +
  287 + it "only finds jobs less than or equal to max priority" do
  288 + max = 5
  289 + Delayed::Worker.max_priority = max
  290 + [4,5,6].sort_by {|i| rand }.each {|i| create_job :priority => i }
  291 + 2.times do
  292 + job = described_class.reserve(worker)
  293 + expect(job.priority).to be <= max
  294 + job.destroy
  295 + end
  296 + expect(described_class.reserve(worker)).to be_nil
  297 + end
  298 + end
  299 +
  300 + context "clear_locks!" do
  301 + before do
  302 + @job = create_job(:locked_by => 'worker1', :locked_at => described_class.db_time_now)
  303 + end
  304 +
  305 + it "clears locks for the given worker" do
  306 + described_class.clear_locks!('worker1')
  307 + expect(described_class.reserve(worker)).to eq(@job)
  308 + end
  309 +
  310 + it "does not clear locks for other workers" do
  311 + described_class.clear_locks!('different_worker')
  312 + expect(described_class.reserve(worker)).not_to eq(@job)
  313 + end
  314 + end
  315 +
  316 + context "unlock" do
  317 + before do
  318 + @job = create_job(:locked_by => 'worker', :locked_at => described_class.db_time_now)
  319 + end
  320 +
  321 + it "clears locks" do
  322 + @job.unlock
  323 + expect(@job.locked_by).to be_nil
  324 + expect(@job.locked_at).to be_nil
  325 + end
  326 + end
  327 +
  328 + context "large handler" do
  329 + before do
  330 + text = "Lorem ipsum dolor sit amet. " * 1000
  331 + @job = described_class.enqueue Delayed::PerformableMethod.new(text, :length, {})
  332 + end
  333 +
  334 + it "has an id" do
  335 + expect(@job.id).not_to be_nil
  336 + end
  337 + end
  338 +
  339 + context "named queues" do
  340 + context "when worker has one queue set" do
  341 + before(:each) do
  342 + worker.queues = ['large']
  343 + end
  344 +
  345 + it "only works off jobs which are from its queue" do
  346 + expect(SimpleJob.runs).to eq(0)
  347 +
  348 + create_job(:queue => "large")
  349 + create_job(:queue => "small")
  350 + worker.work_off
  351 +
  352 + expect(SimpleJob.runs).to eq(1)
  353 + end
  354 + end
  355 +
  356 + context "when worker has two queue set" do
  357 + before(:each) do
  358 + worker.queues = ['large', 'small']
  359 + end
  360 +
  361 + it "only works off jobs which are from its queue" do
  362 + expect(SimpleJob.runs).to eq(0)
  363 +
  364 + create_job(:queue => "large")
  365 + create_job(:queue => "small")
  366 + create_job(:queue => "medium")
  367 + create_job
  368 + worker.work_off
  369 +
  370 + expect(SimpleJob.runs).to eq(2)
  371 + end
  372 + end
  373 +
  374 + context "when worker does not have queue set" do
  375 + before(:each) do
  376 + worker.queues = []
  377 + end
  378 +
  379 + it "works off all jobs" do
  380 + expect(SimpleJob.runs).to eq(0)
  381 +
  382 + create_job(:queue => "one")
  383 + create_job(:queue => "two")
  384 + create_job
  385 + worker.work_off
  386 +
  387 + expect(SimpleJob.runs).to eq(3)
  388 + end
  389 + end
  390 + end
  391 +
  392 + context "max_attempts" do
  393 + before(:each) do
  394 + @job = described_class.enqueue SimpleJob.new
  395 + end
  396 +
  397 + it "is not defined" do
  398 + expect(@job.max_attempts).to be_nil
  399 + end
  400 +
  401 + it "uses the max_retries value on the payload when defined" do
  402 + @job.payload_object.stub(:max_attempts).and_return(99)
  403 + expect(@job.max_attempts).to eq(99)
  404 + end
  405 + end
  406 +
  407 + describe "yaml serialization" do
  408 + it "reloads changed attributes" do
  409 + story = Story.create(:text => 'hello')
  410 + job = story.delay.tell
  411 + story.update_attributes :text => 'goodbye'
  412 + expect(job.reload.payload_object.object.text).to eq('goodbye')
  413 + end
  414 +
  415 + it "raises error ArgumentError the record is not persisted" do
  416 + story = Story.new(:text => 'hello')
  417 + if story.respond_to?(:new_record?)
  418 + expect {
  419 + story.delay.tell
  420 + }.to raise_error(ArgumentError, "Jobs cannot be created for records before they've been persisted")
  421 + end
  422 + end
  423 +
  424 + it "raises deserialization error for destroyed records" do
  425 + story = Story.create(:text => 'hello')
  426 + job = story.delay.tell
  427 + story.destroy
  428 + expect {
  429 + job.reload.payload_object
  430 + }.to raise_error(Delayed::DeserializationError)
  431 + end
  432 + end
  433 +
  434 + describe "worker integration" do
  435 + before do
  436 + Delayed::Job.delete_all
  437 + SimpleJob.runs = 0
  438 + end
  439 +
  440 + describe "running a job" do
  441 + it "fails after Worker.max_run_time" do
  442 + Delayed::Worker.max_run_time = 1.second
  443 + job = Delayed::Job.create :payload_object => LongRunningJob.new
  444 + worker.run(job)
  445 + expect(job.reload.last_error).to match(/expired/)
  446 + expect(job.reload.last_error).to match(/Delayed::Worker.max_run_time is only 1 second/)
  447 + expect(job.attempts).to eq(1)
  448 + end
  449 +
  450 + context "when the job raises a deserialization error" do
  451 + after do
  452 + Delayed::Worker.destroy_failed_jobs = true
  453 + end
  454 +
  455 + it "marks the job as failed" do
  456 + Delayed::Worker.destroy_failed_jobs = false
  457 + job = described_class.create! :handler => "--- !ruby/object:JobThatDoesNotExist {}"
  458 + worker.work_off
  459 + job.reload
  460 + expect(job).to be_failed
  461 + end
  462 + end
  463 + end
  464 +
  465 + describe "failed jobs" do
  466 + before do
  467 + @job = Delayed::Job.enqueue(ErrorJob.new, :run_at => described_class.db_time_now - 1)
  468 + end
  469 +
  470 + after do
  471 + # reset default
  472 + Delayed::Worker.destroy_failed_jobs = true
  473 + end
  474 +
  475 + it "records last_error when destroy_failed_jobs = false, max_attempts = 1" do
  476 + Delayed::Worker.destroy_failed_jobs = false
  477 + Delayed::Worker.max_attempts = 1
  478 + worker.run(@job)
  479 + @job.reload
  480 + expect(@job.last_error).to match(/did not work/)
  481 + expect(@job.attempts).to eq(1)
  482 + expect(@job).to be_failed
  483 + end
  484 +
  485 + it "re-schedules jobs after failing" do
  486 + worker.work_off
  487 + @job.reload
  488 + expect(@job.last_error).to match(/did not work/)
  489 + expect(@job.last_error).to match(/sample_jobs.rb:\d+:in `perform'/)
  490 + expect(@job.attempts).to eq(1)
  491 + expect(@job.run_at).to be > Delayed::Job.db_time_now - 10.minutes
  492 + expect(@job.run_at).to be < Delayed::Job.db_time_now + 10.minutes
  493 + expect(@job.locked_by).to be_nil
  494 + expect(@job.locked_at).to be_nil
  495 + end
  496 +
  497 + it "re-schedules jobs with handler provided time if present" do
  498 + job = Delayed::Job.enqueue(CustomRescheduleJob.new(99.minutes))
  499 + worker.run(job)
  500 + job.reload
  501 +
  502 + expect((Delayed::Job.db_time_now + 99.minutes - job.run_at).abs).to be < 1
  503 + end
  504 +
  505 + it "does not fail when the triggered error doesn't have a message" do
  506 + error_with_nil_message = StandardError.new
  507 + error_with_nil_message.stub(:message).and_return nil
  508 + @job.stub(:invoke_job).and_raise error_with_nil_message
  509 + expect{worker.run(@job)}.not_to raise_error
  510 + end
  511 + end
  512 +
  513 + context "reschedule" do
  514 + before do
  515 + @job = Delayed::Job.create :payload_object => SimpleJob.new
  516 + end
  517 +
  518 + share_examples_for "any failure more than Worker.max_attempts times" do
  519 + context "when the job's payload has a #failure hook" do
  520 + before do
  521 + @job = Delayed::Job.create :payload_object => OnPermanentFailureJob.new
  522 + expect(@job.payload_object).to respond_to :failure
  523 + end
  524 +
  525 + it "runs that hook" do
  526 + @job.payload_object.should_receive :failure
  527 + worker.reschedule(@job)
  528 + end
  529 + end
  530 +
  531 + context "when the job's payload has no #failure hook" do
  532 + # It's a little tricky to test this in a straightforward way,
  533 + # because putting a should_not_receive expectation on
  534 + # @job.payload_object.failure makes that object
  535 + # incorrectly return true to
  536 + # payload_object.respond_to? :failure, which is what
  537 + # reschedule uses to decide whether to call failure.
  538 + # So instead, we just make sure that the payload_object as it
  539 + # already stands doesn't respond_to? failure, then
  540 + # shove it through the iterated reschedule loop and make sure we
  541 + # don't get a NoMethodError (caused by calling that nonexistent
  542 + # failure method).
  543 +
  544 + before do
  545 + expect(@job.payload_object).not_to respond_to(:failure)
  546 + end
  547 +
  548 + it "does not try to run that hook" do
  549 + expect {
  550 + Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
  551 + }.not_to raise_exception
  552 + end
  553 + end
  554 + end
  555 +
  556 + context "and we want to destroy jobs" do
  557 + it_should_behave_like "any failure more than Worker.max_attempts times"
  558 +
  559 + it "is destroyed if it failed more than Worker.max_attempts times" do
  560 + @job.should_receive(:destroy)
  561 + Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
  562 + end
  563 +
  564 + it "is not destroyed if failed fewer than Worker.max_attempts times" do
  565 + @job.should_not_receive(:destroy)
  566 + (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
  567 + end
  568 + end
  569 +
  570 + context "and we don't want to destroy jobs" do
  571 + before do
  572 + Delayed::Worker.destroy_failed_jobs = false
  573 + end
  574 +
  575 + after do
  576 + Delayed::Worker.destroy_failed_jobs = true
  577 + end
  578 +
  579 + it_should_behave_like "any failure more than Worker.max_attempts times"
  580 +
  581 + it "is failed if it failed more than Worker.max_attempts times" do
  582 + expect(@job.reload).not_to be_failed
  583 + Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
  584 + expect(@job.reload).to be_failed
  585 + end
  586 +
  587 + it "is not failed if it failed fewer than Worker.max_attempts times" do
  588 + (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
  589 + expect(@job.reload).not_to be_failed
  590 + end
  591 + end
  592 + end
  593 + end
  594 +end
vendor/plugins/delayed_job/lib/delayed/command.rb
1 -require 'rubygems'  
2 -require 'daemons' 1 +begin
  2 + require 'daemons'
  3 +rescue LoadError
  4 + raise "You need to add gem 'daemons' to your Gemfile if you wish to use it."
  5 +end
3 require 'optparse' 6 require 'optparse'
4 7
5 module Delayed 8 module Delayed
6 class Command 9 class Command
7 attr_accessor :worker_count 10 attr_accessor :worker_count
8 - 11 +
9 def initialize(args) 12 def initialize(args)
10 - @files_to_reopen = []  
11 @options = { 13 @options = {
12 :quiet => true, 14 :quiet => true,
13 :pid_dir => "#{Rails.root}/tmp/pids" 15 :pid_dir => "#{Rails.root}/tmp/pids"
14 } 16 }
15 - 17 +
16 @worker_count = 1 18 @worker_count = 1
17 @monitor = false 19 @monitor = false
18 - 20 +
19 opts = OptionParser.new do |opts| 21 opts = OptionParser.new do |opts|
20 opts.banner = "Usage: #{File.basename($0)} [options] start|stop|restart|run" 22 opts.banner = "Usage: #{File.basename($0)} [options] start|stop|restart|run"
21 23
@@ -44,22 +46,32 @@ module Delayed @@ -44,22 +46,32 @@ module Delayed
44 opts.on('-m', '--monitor', 'Start monitor process.') do 46 opts.on('-m', '--monitor', 'Start monitor process.') do
45 @monitor = true 47 @monitor = true
46 end 48 end
47 -  
48 - 49 + opts.on('--sleep-delay N', "Amount of time to sleep when no jobs are found") do |n|
  50 + @options[:sleep_delay] = n.to_i
  51 + end
  52 + opts.on('--read-ahead N', "Number of jobs from the queue to consider") do |n|
  53 + @options[:read_ahead] = n
  54 + end
  55 + opts.on('-p', '--prefix NAME', "String to be prefixed to worker process names") do |prefix|
  56 + @options[:prefix] = prefix
  57 + end
  58 + opts.on('--queues=queues', "Specify which queue DJ must look up for jobs") do |queues|
  59 + @options[:queues] = queues.split(',')
  60 + end
  61 + opts.on('--queue=queue', "Specify which queue DJ must look up for jobs") do |queue|
  62 + @options[:queues] = queue.split(',')
  63 + end
  64 + opts.on('--exit-on-complete', "Exit when no more jobs are available to run. This will exit if all jobs are scheduled to run in the future.") do
  65 + @options[:exit_on_complete] = true
  66 + end
49 end 67 end
50 @args = opts.parse!(args) 68 @args = opts.parse!(args)
51 end 69 end
52 -  
53 - def daemonize  
54 - Delayed::Worker.backend.before_fork  
55 70
56 - ObjectSpace.each_object(File) do |file|  
57 - @files_to_reopen << file unless file.closed?  
58 - end  
59 - 71 + def daemonize
60 dir = @options[:pid_dir] 72 dir = @options[:pid_dir]
61 Dir.mkdir(dir) unless File.exists?(dir) 73 Dir.mkdir(dir) unless File.exists?(dir)
62 - 74 +
63 if @worker_count > 1 && @options[:identifier] 75 if @worker_count > 1 && @options[:identifier]
64 raise ArgumentError, 'Cannot specify both --number-of-workers and --identifier' 76 raise ArgumentError, 'Cannot specify both --number-of-workers and --identifier'
65 elsif @worker_count == 1 && @options[:identifier] 77 elsif @worker_count == 1 && @options[:identifier]
@@ -72,28 +84,21 @@ module Delayed @@ -72,28 +84,21 @@ module Delayed
72 end 84 end
73 end 85 end
74 end 86 end
75 - 87 +
76 def run_process(process_name, dir) 88 def run_process(process_name, dir)
  89 + Delayed::Worker.before_fork
77 Daemons.run_proc(process_name, :dir => dir, :dir_mode => :normal, :monitor => @monitor, :ARGV => @args) do |*args| 90 Daemons.run_proc(process_name, :dir => dir, :dir_mode => :normal, :monitor => @monitor, :ARGV => @args) do |*args|
  91 + $0 = File.join(@options[:prefix], process_name) if @options[:prefix]
78 run process_name 92 run process_name
79 end 93 end
80 end 94 end
81 - 95 +
82 def run(worker_name = nil) 96 def run(worker_name = nil)
83 Dir.chdir(Rails.root) 97 Dir.chdir(Rails.root)
84 -  
85 - # Re-open file handles  
86 - @files_to_reopen.each do |file|  
87 - begin  
88 - file.reopen file.path, "a+"  
89 - file.sync = true  
90 - rescue ::Exception  
91 - end  
92 - end  
93 -  
94 - Delayed::Worker.logger = Logger.new(File.join(Rails.root, 'log', 'delayed_job.log'))  
95 - Delayed::Worker.backend.after_fork  
96 - 98 +
  99 + Delayed::Worker.after_fork
  100 + Delayed::Worker.logger ||= Logger.new(File.join(Rails.root, 'log', 'delayed_job.log'))
  101 +
97 worker = Delayed::Worker.new(@options) 102 worker = Delayed::Worker.new(@options)
98 worker.name_prefix = "#{worker_name} " 103 worker.name_prefix = "#{worker_name} "
99 worker.start 104 worker.start
@@ -102,6 +107,5 @@ module Delayed @@ -102,6 +107,5 @@ module Delayed
102 STDERR.puts e.message 107 STDERR.puts e.message
103 exit 1 108 exit 1
104 end 109 end
105 -  
106 end 110 end
107 end 111 end
vendor/plugins/delayed_job/lib/delayed/compatibility.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +require 'active_support/version'
  2 +
  3 +module Delayed
  4 + module Compatibility
  5 + if ActiveSupport::VERSION::MAJOR >= 4
  6 + require 'active_support/proxy_object'
  7 +
  8 + def self.executable_prefix
  9 + 'bin'
  10 + end
  11 +
  12 + def self.proxy_object_class
  13 + ActiveSupport::ProxyObject
  14 + end
  15 + else
  16 + require 'active_support/basic_object'
  17 +
  18 + def self.executable_prefix
  19 + 'script'
  20 + end
  21 +
  22 + def self.proxy_object_class
  23 + ActiveSupport::BasicObject
  24 + end
  25 + end
  26 + end
  27 +end
vendor/plugins/delayed_job/lib/delayed/deserialization_error.rb 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +module Delayed
  2 + class DeserializationError < StandardError
  3 + end
  4 +end
vendor/plugins/delayed_job/lib/delayed/exceptions.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +require 'timeout'
  2 +
  3 +module Delayed
  4 + class WorkerTimeout < Timeout::Error
  5 + def message
  6 + "#{super} (Delayed::Worker.max_run_time is only #{Delayed::Worker.max_run_time.to_i} seconds)"
  7 + end
  8 + end
  9 +end
vendor/plugins/delayed_job/lib/delayed/lifecycle.rb 0 → 100644
@@ -0,0 +1,84 @@ @@ -0,0 +1,84 @@
  1 +module Delayed
  2 + class InvalidCallback < Exception; end
  3 +
  4 + class Lifecycle
  5 + EVENTS = {
  6 + :enqueue => [:job],
  7 + :execute => [:worker],
  8 + :loop => [:worker],
  9 + :perform => [:worker, :job],
  10 + :error => [:worker, :job],
  11 + :failure => [:worker, :job],
  12 + :invoke_job => [:job]
  13 + }
  14 +
  15 + def initialize
  16 + @callbacks = EVENTS.keys.inject({}) { |hash, e| hash[e] = Callback.new; hash }
  17 + end
  18 +
  19 + def before(event, &block)
  20 + add(:before, event, &block)
  21 + end
  22 +
  23 + def after(event, &block)
  24 + add(:after, event, &block)
  25 + end
  26 +
  27 + def around(event, &block)
  28 + add(:around, event, &block)
  29 + end
  30 +
  31 + def run_callbacks(event, *args, &block)
  32 + missing_callback(event) unless @callbacks.has_key?(event)
  33 +
  34 + unless EVENTS[event].size == args.size
  35 + raise ArgumentError, "Callback #{event} expects #{EVENTS[event].size} parameter(s): #{EVENTS[event].join(', ')}"
  36 + end
  37 +
  38 + @callbacks[event].execute(*args, &block)
  39 + end
  40 +
  41 + private
  42 +
  43 + def add(type, event, &block)
  44 + missing_callback(event) unless @callbacks.has_key?(event)
  45 +
  46 + @callbacks[event].add(type, &block)
  47 + end
  48 +
  49 + def missing_callback(event)
  50 + raise InvalidCallback, "Unknown callback event: #{event}"
  51 + end
  52 + end
  53 +
  54 + class Callback
  55 + def initialize
  56 + @before = []
  57 + @after = []
  58 +
  59 + # Identity proc. Avoids special cases when there is no existing around chain.
  60 + @around = lambda { |*args, &block| block.call(*args) }
  61 + end
  62 +
  63 + def execute(*args, &block)
  64 + @before.each { |c| c.call(*args) }
  65 + result = @around.call(*args, &block)
  66 + @after.each { |c| c.call(*args) }
  67 + result
  68 + end
  69 +
  70 + def add(type, &callback)
  71 + case type
  72 + when :before
  73 + @before << callback
  74 + when :after
  75 + @after << callback
  76 + when :around
  77 + chain = @around # use a local variable so that the current chain is closed over in the following lambda
  78 + @around = lambda { |*a, &block| chain.call(*a) { |*b| callback.call(*b, &block) } }
  79 + else
  80 + raise InvalidCallback, "Invalid callback type: #{type}"
  81 + end
  82 + end
  83 + end
  84 +end
vendor/plugins/delayed_job/lib/delayed/message_sending.rb
1 -require 'active_support/basic_object' 1 +require 'active_support/core_ext/module/aliasing'
2 2
3 module Delayed 3 module Delayed
4 - class DelayProxy < ActiveSupport::BasicObject  
5 - def initialize(target, options) 4 + class DelayProxy < Delayed::Compatibility.proxy_object_class
  5 + def initialize(payload_class, target, options)
  6 + @payload_class = payload_class
6 @target = target 7 @target = target
7 @options = options 8 @options = options
8 end 9 end
9 10
10 def method_missing(method, *args) 11 def method_missing(method, *args)
11 - if (Rails.env == "test" or Rails.env == "cucumber" and !$DISABLE_DELAYED_JOB_TEST_ENV_RUN)  
12 - @target.send method, *args  
13 - return  
14 - end  
15 - Job.create({  
16 - :payload_object => PerformableMethod.new(@target, method.to_sym, args),  
17 - :priority => ::Delayed::Worker.default_priority  
18 - }.merge(@options)) 12 + Job.enqueue({:payload_object => @payload_class.new(@target, method.to_sym, args)}.merge(@options))
19 end 13 end
20 end 14 end
21 15
22 module MessageSending 16 module MessageSending
23 def delay(options = {}) 17 def delay(options = {})
24 - DelayProxy.new(self, options) 18 + DelayProxy.new(PerformableMethod, self, options)
25 end 19 end
26 alias __delay__ delay 20 alias __delay__ delay
27 - 21 +
28 def send_later(method, *args) 22 def send_later(method, *args)
29 warn "[DEPRECATION] `object.send_later(:method)` is deprecated. Use `object.delay.method" 23 warn "[DEPRECATION] `object.send_later(:method)` is deprecated. Use `object.delay.method"
30 __delay__.__send__(method, *args) 24 __delay__.__send__(method, *args)
@@ -34,17 +28,26 @@ module Delayed @@ -34,17 +28,26 @@ module Delayed
34 warn "[DEPRECATION] `object.send_at(time, :method)` is deprecated. Use `object.delay(:run_at => time).method" 28 warn "[DEPRECATION] `object.send_at(time, :method)` is deprecated. Use `object.delay(:run_at => time).method"
35 __delay__(:run_at => time).__send__(method, *args) 29 __delay__(:run_at => time).__send__(method, *args)
36 end 30 end
37 - 31 +
38 module ClassMethods 32 module ClassMethods
39 - def handle_asynchronously(method)  
40 - return if (Rails.env == "test" or Rails.env == "cucumber") 33 + def handle_asynchronously(method, opts = {})
41 aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1 34 aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1
42 with_method, without_method = "#{aliased_method}_with_delay#{punctuation}", "#{aliased_method}_without_delay#{punctuation}" 35 with_method, without_method = "#{aliased_method}_with_delay#{punctuation}", "#{aliased_method}_without_delay#{punctuation}"
43 define_method(with_method) do |*args| 36 define_method(with_method) do |*args|
44 - delay.__send__(without_method, *args) 37 + curr_opts = opts.clone
  38 + curr_opts.each_key do |key|
  39 + if (val = curr_opts[key]).is_a?(Proc)
  40 + curr_opts[key] = if val.arity == 1
  41 + val.call(self)
  42 + else
  43 + val.call
  44 + end
  45 + end
  46 + end
  47 + delay(curr_opts).__send__(without_method, *args)
45 end 48 end
46 alias_method_chain method, :delay 49 alias_method_chain method, :delay
47 end 50 end
48 end 51 end
49 - end 52 + end
50 end 53 end
vendor/plugins/delayed_job/lib/delayed/performable_mailer.rb 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +require 'mail'
  2 +
  3 +module Delayed
  4 + class PerformableMailer < PerformableMethod
  5 + def perform
  6 + object.send(method_name, *args).deliver
  7 + end
  8 + end
  9 +
  10 + module DelayMail
  11 + def delay(options = {})
  12 + DelayProxy.new(PerformableMailer, self, options)
  13 + end
  14 + end
  15 +end
  16 +
  17 +Mail::Message.class_eval do
  18 + def delay(*args)
  19 + raise RuntimeError, "Use MyMailer.delay.mailer_action(args) to delay sending of emails."
  20 + end
  21 +end
vendor/plugins/delayed_job/lib/delayed/performable_method.rb
  1 +require 'active_support/core_ext/module/delegation'
  2 +
1 module Delayed 3 module Delayed
2 - class PerformableMethod < Struct.new(:object, :method, :args)  
3 - def initialize(object, method, args)  
4 - raise NoMethodError, "undefined method `#{method}' for #{object.inspect}" unless object.respond_to?(method, true) 4 + class PerformableMethod
  5 + attr_accessor :object, :method_name, :args
  6 +
  7 + delegate :method, :to => :object
  8 +
  9 + def initialize(object, method_name, args)
  10 + raise NoMethodError, "undefined method `#{method_name}' for #{object.inspect}" unless object.respond_to?(method_name, true)
5 11
6 - self.object = object  
7 - self.args = args  
8 - self.method = method.to_sym 12 + if object.respond_to?(:new_record?) && object.new_record?
  13 + raise(ArgumentError, 'Jobs cannot be created for records before they\'ve been persisted')
  14 + end
  15 +
  16 + self.object = object
  17 + self.args = args
  18 + self.method_name = method_name.to_sym
9 end 19 end
10 - 20 +
11 def display_name 21 def display_name
12 - "#{object.class}##{method}" 22 + "#{object.class}##{method_name}"
13 end 23 end
14 - 24 +
15 def perform 25 def perform
16 - object.send(method, *args) if object 26 + object.send(method_name, *args) if object
17 end 27 end
18 - 28 +
19 def method_missing(symbol, *args) 29 def method_missing(symbol, *args)
20 - object.respond_to?(symbol) ? object.send(symbol, *args) : super 30 + object.send(symbol, *args)
21 end 31 end
22 - 32 +
23 def respond_to?(symbol, include_private=false) 33 def respond_to?(symbol, include_private=false)
24 - object.respond_to?(symbol, include_private) || super  
25 - end 34 + super || object.respond_to?(symbol, include_private)
  35 + end
26 end 36 end
27 end 37 end
vendor/plugins/delayed_job/lib/delayed/plugin.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +require 'active_support/core_ext/class/attribute'
  2 +
  3 +module Delayed
  4 + class Plugin
  5 + class_attribute :callback_block
  6 +
  7 + def self.callbacks(&block)
  8 + self.callback_block = block
  9 + end
  10 +
  11 + def initialize
  12 + self.class.callback_block.call(Delayed::Worker.lifecycle) if self.class.callback_block
  13 + end
  14 + end
  15 +end
vendor/plugins/delayed_job/lib/delayed/plugins/clear_locks.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +module Delayed
  2 + module Plugins
  3 + class ClearLocks < Plugin
  4 + callbacks do |lifecycle|
  5 + lifecycle.around(:execute) do |worker, &block|
  6 + begin
  7 + block.call(worker)
  8 + ensure
  9 + Delayed::Job.clear_locks!(worker.name)
  10 + end
  11 + end
  12 + end
  13 + end
  14 + end
  15 +end
vendor/plugins/delayed_job/lib/delayed/psych_ext.rb 0 → 100644
@@ -0,0 +1,146 @@ @@ -0,0 +1,146 @@
  1 +if defined?(ActiveRecord)
  2 + ActiveRecord::Base.class_eval do
  3 + if instance_methods.include?(:encode_with)
  4 + def encode_with_override(coder)
  5 + encode_with_without_override(coder)
  6 + coder.tag = "!ruby/ActiveRecord:#{self.class.name}"
  7 + end
  8 + alias_method :encode_with_without_override, :encode_with
  9 + alias_method :encode_with, :encode_with_override
  10 + else
  11 + def encode_with(coder)
  12 + coder["attributes"] = attributes
  13 + coder.tag = "!ruby/ActiveRecord:#{self.class.name}"
  14 + end
  15 + end
  16 + end
  17 +end
  18 +
  19 +class Delayed::PerformableMethod
  20 + # serialize to YAML
  21 + def encode_with(coder)
  22 + coder.map = {
  23 + "object" => object,
  24 + "method_name" => method_name,
  25 + "args" => args
  26 + }
  27 + end
  28 +end
  29 +
  30 +module Psych
  31 + module Visitors
  32 + class YAMLTree
  33 + def visit_Class(klass)
  34 + @emitter.scalar klass.name, nil, '!ruby/class', false, false, Nodes::Scalar::SINGLE_QUOTED
  35 + end
  36 + end
  37 +
  38 + class ToRuby
  39 + def visit_Psych_Nodes_Scalar(o)
  40 + @st[o.anchor] = o.value if o.anchor
  41 +
  42 + if klass = Psych.load_tags[o.tag]
  43 + instance = klass.allocate
  44 +
  45 + if instance.respond_to?(:init_with)
  46 + coder = Psych::Coder.new(o.tag)
  47 + coder.scalar = o.value
  48 + instance.init_with coder
  49 + end
  50 +
  51 + return instance
  52 + end
  53 +
  54 + return o.value if o.quoted
  55 + return @ss.tokenize(o.value) unless o.tag
  56 +
  57 + case o.tag
  58 + when '!binary', 'tag:yaml.org,2002:binary'
  59 + o.value.unpack('m').first
  60 + when '!str', 'tag:yaml.org,2002:str'
  61 + o.value
  62 + when "!ruby/object:DateTime"
  63 + require 'date'
  64 + @ss.parse_time(o.value).to_datetime
  65 + when "!ruby/object:Complex"
  66 + Complex(o.value)
  67 + when "!ruby/object:Rational"
  68 + Rational(o.value)
  69 + when "!ruby/class", "!ruby/module"
  70 + resolve_class o.value
  71 + when "tag:yaml.org,2002:float", "!float"
  72 + Float(@ss.tokenize(o.value))
  73 + when "!ruby/regexp"
  74 + o.value =~ /^\/(.*)\/([mixn]*)$/
  75 + source = $1
  76 + options = 0
  77 + lang = nil
  78 + ($2 || '').split('').each do |option|
  79 + case option
  80 + when 'x' then options |= Regexp::EXTENDED
  81 + when 'i' then options |= Regexp::IGNORECASE
  82 + when 'm' then options |= Regexp::MULTILINE
  83 + when 'n' then options |= Regexp::NOENCODING
  84 + else lang = option
  85 + end
  86 + end
  87 + Regexp.new(*[source, options, lang].compact)
  88 + when "!ruby/range"
  89 + args = o.value.split(/([.]{2,3})/, 2).map { |s|
  90 + accept Nodes::Scalar.new(s)
  91 + }
  92 + args.push(args.delete_at(1) == '...')
  93 + Range.new(*args)
  94 + when /^!ruby\/sym(bol)?:?(.*)?$/
  95 + o.value.to_sym
  96 + else
  97 + @ss.tokenize o.value
  98 + end
  99 + end
  100 +
  101 + def visit_Psych_Nodes_Mapping_with_class(object)
  102 + return revive(Psych.load_tags[object.tag], object) if Psych.load_tags[object.tag]
  103 +
  104 + case object.tag
  105 + when /^!ruby\/ActiveRecord:(.+)$/
  106 + klass = resolve_class($1)
  107 + payload = Hash[*object.children.map { |c| accept c }]
  108 + id = payload["attributes"][klass.primary_key]
  109 + begin
  110 + klass.unscoped.find(id)
  111 + rescue ActiveRecord::RecordNotFound
  112 + raise Delayed::DeserializationError
  113 + end
  114 + when /^!ruby\/Mongoid:(.+)$/
  115 + klass = resolve_class($1)
  116 + payload = Hash[*object.children.map { |c| accept c }]
  117 + begin
  118 + klass.find(payload["attributes"]["_id"])
  119 + rescue Mongoid::Errors::DocumentNotFound
  120 + raise Delayed::DeserializationError
  121 + end
  122 + when /^!ruby\/DataMapper:(.+)$/
  123 + klass = resolve_class($1)
  124 + payload = Hash[*object.children.map { |c| accept c }]
  125 + begin
  126 + primary_keys = klass.properties.select { |p| p.key? }
  127 + key_names = primary_keys.map { |p| p.name.to_s }
  128 + klass.get!(*key_names.map { |k| payload["attributes"][k] })
  129 + rescue DataMapper::ObjectNotFoundError
  130 + raise Delayed::DeserializationError
  131 + end
  132 + else
  133 + visit_Psych_Nodes_Mapping_without_class(object)
  134 + end
  135 + end
  136 + alias_method_chain :visit_Psych_Nodes_Mapping, :class
  137 +
  138 + def resolve_class_with_constantize(klass_name)
  139 + klass_name.constantize
  140 + rescue
  141 + resolve_class_without_constantize(klass_name)
  142 + end
  143 + alias_method_chain :resolve_class, :constantize
  144 + end
  145 + end
  146 +end
vendor/plugins/delayed_job/lib/delayed/railtie.rb
@@ -4,7 +4,9 @@ require &#39;rails&#39; @@ -4,7 +4,9 @@ require &#39;rails&#39;
4 module Delayed 4 module Delayed
5 class Railtie < Rails::Railtie 5 class Railtie < Rails::Railtie
6 initializer :after_initialize do 6 initializer :after_initialize do
7 - Delayed::Worker.guess_backend 7 + ActiveSupport.on_load(:action_mailer) do
  8 + ActionMailer::Base.send(:extend, Delayed::DelayMail)
  9 + end
8 end 10 end
9 11
10 rake_tasks do 12 rake_tasks do
vendor/plugins/delayed_job/lib/delayed/recipes.rb
@@ -6,26 +6,49 @@ @@ -6,26 +6,49 @@
6 # after "deploy:stop", "delayed_job:stop" 6 # after "deploy:stop", "delayed_job:stop"
7 # after "deploy:start", "delayed_job:start" 7 # after "deploy:start", "delayed_job:start"
8 # after "deploy:restart", "delayed_job:restart" 8 # after "deploy:restart", "delayed_job:restart"
  9 +#
  10 +# If you want to use command line options, for example to start multiple workers,
  11 +# define a Capistrano variable delayed_job_args:
  12 +#
  13 +# set :delayed_job_args, "-n 2"
  14 +#
  15 +# If you've got delayed_job workers running on a servers, you can also specify
  16 +# which servers have delayed_job running and should be restarted after deploy.
  17 +#
  18 +# set :delayed_job_server_role, :worker
  19 +#
9 20
10 Capistrano::Configuration.instance.load do 21 Capistrano::Configuration.instance.load do
11 namespace :delayed_job do 22 namespace :delayed_job do
12 def rails_env 23 def rails_env
13 fetch(:rails_env, false) ? "RAILS_ENV=#{fetch(:rails_env)}" : '' 24 fetch(:rails_env, false) ? "RAILS_ENV=#{fetch(:rails_env)}" : ''
14 end 25 end
15 - 26 +
  27 + def args
  28 + fetch(:delayed_job_args, "")
  29 + end
  30 +
  31 + def roles
  32 + fetch(:delayed_job_server_role, :app)
  33 + end
  34 +
  35 + def delayed_job_command
  36 + fetch(:delayed_job_command, "script/delayed_job")
  37 + end
  38 +
16 desc "Stop the delayed_job process" 39 desc "Stop the delayed_job process"
17 - task :stop, :roles => :app do  
18 - run "cd #{current_path};#{rails_env} script/delayed_job stop" 40 + task :stop, :roles => lambda { roles } do
  41 + run "cd #{current_path};#{rails_env} #{delayed_job_command} stop"
19 end 42 end
20 43
21 desc "Start the delayed_job process" 44 desc "Start the delayed_job process"
22 - task :start, :roles => :app do  
23 - run "cd #{current_path};#{rails_env} script/delayed_job start" 45 + task :start, :roles => lambda { roles } do
  46 + run "cd #{current_path};#{rails_env} #{delayed_job_command} start #{args}"
24 end 47 end
25 48
26 desc "Restart the delayed_job process" 49 desc "Restart the delayed_job process"
27 - task :restart, :roles => :app do  
28 - run "cd #{current_path};#{rails_env} script/delayed_job restart" 50 + task :restart, :roles => lambda { roles } do
  51 + run "cd #{current_path};#{rails_env} #{delayed_job_command} restart #{args}"
29 end 52 end
30 end 53 end
31 -end  
32 \ No newline at end of file 54 \ No newline at end of file
  55 +end
vendor/plugins/delayed_job/lib/delayed/serialization/active_record.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +if defined?(ActiveRecord)
  2 + class ActiveRecord::Base
  3 + yaml_as "tag:ruby.yaml.org,2002:ActiveRecord"
  4 +
  5 + def self.yaml_new(klass, tag, val)
  6 + klass.unscoped.find(val['attributes'][klass.primary_key])
  7 + rescue ActiveRecord::RecordNotFound
  8 + raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass} , primary key: #{val['attributes'][klass.primary_key]} "
  9 + end
  10 +
  11 + def to_yaml_properties
  12 + ['@attributes']
  13 + end
  14 + end
  15 +end
vendor/plugins/delayed_job/lib/delayed/syck_ext.rb 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +class Module
  2 + yaml_as "tag:ruby.yaml.org,2002:module"
  3 +
  4 + def self.yaml_new(klass, tag, val)
  5 + val.constantize
  6 + end
  7 +
  8 + def to_yaml(options = {})
  9 + YAML.quick_emit(nil, options) do |out|
  10 + out.scalar(taguri, name, :plain)
  11 + end
  12 + end
  13 +
  14 + def yaml_tag_read_class(name)
  15 + # Constantize the object so that ActiveSupport can attempt
  16 + # its auto loading magic. Will raise LoadError if not successful.
  17 + name.constantize
  18 + name
  19 + end
  20 +end
  21 +
  22 +class Class
  23 + yaml_as "tag:ruby.yaml.org,2002:class"
  24 + remove_method :to_yaml if respond_to?(:to_yaml) && method(:to_yaml).owner == Class # use Module's to_yaml
  25 +end
  26 +
  27 +class Struct
  28 + def self.yaml_tag_read_class(name)
  29 + # Constantize the object so that ActiveSupport can attempt
  30 + # its auto loading magic. Will raise LoadError if not successful.
  31 + name.constantize
  32 + "Struct::#{ name }"
  33 + end
  34 +end
vendor/plugins/delayed_job/lib/delayed/tasks.rb
1 -# Re-definitions are appended to existing tasks  
2 -task :environment  
3 -task :merb_env  
4 -  
5 namespace :jobs do 1 namespace :jobs do
6 desc "Clear the delayed_job queue." 2 desc "Clear the delayed_job queue."
7 - task :clear => [:merb_env, :environment] do 3 + task :clear => :environment do
8 Delayed::Job.delete_all 4 Delayed::Job.delete_all
9 end 5 end
10 6
11 desc "Start a delayed_job worker." 7 desc "Start a delayed_job worker."
12 - task :work => [:merb_env, :environment] do  
13 - Delayed::Worker.new(:min_priority => ENV['MIN_PRIORITY'], :max_priority => ENV['MAX_PRIORITY']).start 8 + task :work => :environment_options do
  9 + Delayed::Worker.new(@worker_options).start
  10 + end
  11 +
  12 + desc "Start a delayed_job worker and exit when all available jobs are complete."
  13 + task :workoff => :environment_options do
  14 + Delayed::Worker.new(@worker_options.merge({:exit_on_complete => true})).start
14 end 15 end
  16 +
  17 + task :environment_options => :environment do
  18 + @worker_options = {
  19 + :min_priority => ENV['MIN_PRIORITY'],
  20 + :max_priority => ENV['MAX_PRIORITY'],
  21 + :queues => (ENV['QUEUES'] || ENV['QUEUE'] || '').split(','),
  22 + :quiet => false
  23 + }
  24 + end
  25 +
  26 + desc "Exit with error status if any jobs older than max_age seconds haven't been attempted yet."
  27 + task :check, [:max_age] => :environment do |_, args|
  28 + args.with_defaults(:max_age => 300)
  29 +
  30 + unprocessed_jobs = Delayed::Job.where('attempts = 0 AND created_at < ?', Time.now - args[:max_age].to_i).count
  31 +
  32 + if unprocessed_jobs > 0
  33 + fail "#{unprocessed_jobs} jobs older than #{args[:max_age]} seconds have not been processed yet"
  34 + end
  35 +
  36 + end
  37 +
15 end 38 end
vendor/plugins/delayed_job/lib/delayed/worker.rb
@@ -2,59 +2,122 @@ require &#39;timeout&#39; @@ -2,59 +2,122 @@ require &#39;timeout&#39;
2 require 'active_support/core_ext/numeric/time' 2 require 'active_support/core_ext/numeric/time'
3 require 'active_support/core_ext/class/attribute_accessors' 3 require 'active_support/core_ext/class/attribute_accessors'
4 require 'active_support/core_ext/kernel' 4 require 'active_support/core_ext/kernel'
  5 +require 'active_support/core_ext/enumerable'
  6 +require 'logger'
  7 +require 'benchmark'
5 8
6 module Delayed 9 module Delayed
7 class Worker 10 class Worker
8 - cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time, :default_priority, :sleep_delay, :logger  
9 - self.sleep_delay = 5  
10 - self.max_attempts = 25  
11 - self.max_run_time = 4.hours  
12 - self.default_priority = 0  
13 - 11 + DEFAULT_LOG_LEVEL = Logger::INFO
  12 + DEFAULT_SLEEP_DELAY = 5
  13 + DEFAULT_MAX_ATTEMPTS = 25
  14 + DEFAULT_MAX_RUN_TIME = 4.hours
  15 + DEFAULT_DEFAULT_PRIORITY = 0
  16 + DEFAULT_DELAY_JOBS = true
  17 + DEFAULT_QUEUES = []
  18 + DEFAULT_READ_AHEAD = 5
  19 +
  20 + cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time,
  21 + :default_priority, :sleep_delay, :logger, :delay_jobs, :queues,
  22 + :read_ahead, :plugins, :destroy_failed_jobs, :exit_on_complete
  23 +
  24 + # Named queue into which jobs are enqueued by default
  25 + cattr_accessor :default_queue_name
  26 +
  27 + cattr_reader :backend
  28 +
  29 + # name_prefix is ignored if name is set directly
  30 + attr_accessor :name_prefix
  31 +
  32 + def self.reset
  33 + self.sleep_delay = DEFAULT_SLEEP_DELAY
  34 + self.max_attempts = DEFAULT_MAX_ATTEMPTS
  35 + self.max_run_time = DEFAULT_MAX_RUN_TIME
  36 + self.default_priority = DEFAULT_DEFAULT_PRIORITY
  37 + self.delay_jobs = DEFAULT_DELAY_JOBS
  38 + self.queues = DEFAULT_QUEUES
  39 + self.read_ahead = DEFAULT_READ_AHEAD
  40 + end
  41 +
  42 + reset
  43 +
  44 + # Add or remove plugins in this list before the worker is instantiated
  45 + self.plugins = [Delayed::Plugins::ClearLocks]
  46 +
14 # By default failed jobs are destroyed after too many attempts. If you want to keep them around 47 # By default failed jobs are destroyed after too many attempts. If you want to keep them around
15 # (perhaps to inspect the reason for the failure), set this to false. 48 # (perhaps to inspect the reason for the failure), set this to false.
16 - cattr_accessor :destroy_failed_jobs  
17 self.destroy_failed_jobs = true 49 self.destroy_failed_jobs = true
18 -  
19 - self.logger = if defined?(Merb::Logger)  
20 - Merb.logger 50 +
  51 + # By default, Signals INT and TERM set @exit, and the worker exits upon completion of the current job.
  52 + # If you would prefer to raise a SignalException and exit immediately you can use this.
  53 + # Be aware daemons uses TERM to stop and restart
  54 + # false - No exceptions will be raised
  55 + # :term - Will only raise an exception on TERM signals but INT will wait for the current job to finish
  56 + # true - Will raise an exception on TERM and INT
  57 + cattr_accessor :raise_signal_exceptions
  58 + self.raise_signal_exceptions = false
  59 +
  60 + self.logger = if defined?(Rails)
  61 + Rails.logger
21 elsif defined?(RAILS_DEFAULT_LOGGER) 62 elsif defined?(RAILS_DEFAULT_LOGGER)
22 RAILS_DEFAULT_LOGGER 63 RAILS_DEFAULT_LOGGER
23 end 64 end
24 65
25 - # name_prefix is ignored if name is set directly  
26 - attr_accessor :name_prefix  
27 -  
28 - cattr_reader :backend  
29 -  
30 def self.backend=(backend) 66 def self.backend=(backend)
31 if backend.is_a? Symbol 67 if backend.is_a? Symbol
  68 + require "delayed/serialization/#{backend}"
32 require "delayed/backend/#{backend}" 69 require "delayed/backend/#{backend}"
33 backend = "Delayed::Backend::#{backend.to_s.classify}::Job".constantize 70 backend = "Delayed::Backend::#{backend.to_s.classify}::Job".constantize
34 end 71 end
35 @@backend = backend 72 @@backend = backend
36 silence_warnings { ::Delayed.const_set(:Job, backend) } 73 silence_warnings { ::Delayed.const_set(:Job, backend) }
37 end 74 end
38 - 75 +
39 def self.guess_backend 76 def self.guess_backend
40 - self.backend ||= if defined?(ActiveRecord)  
41 - :active_record  
42 - elsif defined?(MongoMapper)  
43 - :mongo_mapper  
44 - else  
45 - logger.warn "Could not decide on a backend, defaulting to active_record"  
46 - :active_record 77 + warn "[DEPRECATION] guess_backend is deprecated. Please remove it from your code."
  78 + end
  79 +
  80 + def self.before_fork
  81 + unless @files_to_reopen
  82 + @files_to_reopen = []
  83 + ObjectSpace.each_object(File) do |file|
  84 + @files_to_reopen << file unless file.closed?
  85 + end
47 end 86 end
  87 +
  88 + backend.before_fork
  89 + end
  90 +
  91 + def self.after_fork
  92 + # Re-open file handles
  93 + @files_to_reopen.each do |file|
  94 + begin
  95 + file.reopen file.path, "a+"
  96 + file.sync = true
  97 + rescue ::Exception
  98 + end
  99 + end
  100 +
  101 + backend.after_fork
  102 + end
  103 +
  104 + def self.lifecycle
  105 + @lifecycle ||= Delayed::Lifecycle.new
48 end 106 end
49 107
50 def initialize(options={}) 108 def initialize(options={})
51 - @quiet = options[:quiet]  
52 - self.class.min_priority = options[:min_priority] if options.has_key?(:min_priority)  
53 - self.class.max_priority = options[:max_priority] if options.has_key?(:max_priority) 109 + @quiet = options.has_key?(:quiet) ? options[:quiet] : true
  110 + @failed_reserve_count = 0
  111 +
  112 + [:min_priority, :max_priority, :sleep_delay, :read_ahead, :queues, :exit_on_complete].each do |option|
  113 + self.class.send("#{option}=", options[option]) if options.has_key?(option)
  114 + end
  115 +
  116 + self.plugins.each { |klass| klass.new }
54 end 117 end
55 118
56 # Every worker has a unique name which by default is the pid of the process. There are some 119 # Every worker has a unique name which by default is the pid of the process. There are some
57 - # advantages to overriding this with something which survives worker retarts: Workers can# 120 + # advantages to overriding this with something which survives worker restarts: Workers can
58 # safely resume working on tasks which are locked by themselves. The worker will assume that 121 # safely resume working on tasks which are locked by themselves. The worker will assume that
59 # it crashed before. 122 # it crashed before.
60 def name 123 def name
@@ -69,35 +132,54 @@ module Delayed @@ -69,35 +132,54 @@ module Delayed
69 end 132 end
70 133
71 def start 134 def start
72 - say "Starting job worker" 135 + trap('TERM') do
  136 + say 'Exiting...'
  137 + stop
  138 + raise SignalException.new('TERM') if self.class.raise_signal_exceptions
  139 + end
73 140
74 - trap('TERM') { say 'Exiting...'; $exit = true }  
75 - trap('INT') { say 'Exiting...'; $exit = true } 141 + trap('INT') do
  142 + say 'Exiting...'
  143 + stop
  144 + raise SignalException.new('INT') if self.class.raise_signal_exceptions && self.class.raise_signal_exceptions != :term
  145 + end
76 146
77 - loop do  
78 - result = nil 147 + say "Starting job worker"
79 148
80 - realtime = Benchmark.realtime do  
81 - result = work_off  
82 - end 149 + self.class.lifecycle.run_callbacks(:execute, self) do
  150 + loop do
  151 + self.class.lifecycle.run_callbacks(:loop, self) do
  152 + @realtime = Benchmark.realtime do
  153 + @result = work_off
  154 + end
  155 + end
83 156
84 - count = result.sum 157 + count = @result.sum
85 158
86 - break if $exit 159 + if count.zero?
  160 + if self.class.exit_on_complete
  161 + say "No more jobs available. Exiting"
  162 + break
  163 + else
  164 + sleep(self.class.sleep_delay) unless stop?
  165 + end
  166 + else
  167 + say "#{count} jobs processed at %.4f j/s, %d failed" % [count / @realtime, @result.last]
  168 + end
87 169
88 - if count.zero?  
89 - sleep(@@sleep_delay)  
90 - else  
91 - say "#{count} jobs processed at %.4f j/s, %d failed ..." % [count / realtime, result.last] 170 + break if stop?
92 end 171 end
93 -  
94 - break if $exit  
95 end 172 end
  173 + end
  174 +
  175 + def stop
  176 + @exit = true
  177 + end
96 178
97 - ensure  
98 - Delayed::Job.clear_locks!(name) 179 + def stop?
  180 + !!@exit
99 end 181 end
100 - 182 +
101 # Do num jobs and return stats on success/failure. 183 # Do num jobs and return stats on success/failure.
102 # Exit early if interrupted. 184 # Exit early if interrupted.
103 def work_off(num = 100) 185 def work_off(num = 100)
@@ -112,80 +194,90 @@ module Delayed @@ -112,80 +194,90 @@ module Delayed
112 else 194 else
113 break # leave if no work could be done 195 break # leave if no work could be done
114 end 196 end
115 - break if $exit # leave if we're exiting 197 + break if stop? # leave if we're exiting
116 end 198 end
117 199
118 return [success, failure] 200 return [success, failure]
119 end 201 end
120 - 202 +
121 def run(job) 203 def run(job)
  204 + job_say job, 'RUNNING'
122 runtime = Benchmark.realtime do 205 runtime = Benchmark.realtime do
123 - Timeout.timeout(self.class.max_run_time.to_i) { job.invoke_job } 206 + Timeout.timeout(self.class.max_run_time.to_i, WorkerTimeout) { job.invoke_job }
124 job.destroy 207 job.destroy
125 end 208 end
126 - say "#{job.name} completed after %.4f" % runtime 209 + job_say job, 'COMPLETED after %.4f' % runtime
127 return true # did work 210 return true # did work
128 - rescue Exception => e  
129 - handle_failed_job(job, e) 211 + rescue DeserializationError => error
  212 + job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
  213 + failed(job)
  214 + rescue Exception => error
  215 + self.class.lifecycle.run_callbacks(:error, self, job){ handle_failed_job(job, error) }
130 return false # work failed 216 return false # work failed
131 end 217 end
132 - 218 +
133 # Reschedule the job in the future (when a job fails). 219 # Reschedule the job in the future (when a job fails).
134 # Uses an exponential scale depending on the number of failed attempts. 220 # Uses an exponential scale depending on the number of failed attempts.
135 def reschedule(job, time = nil) 221 def reschedule(job, time = nil)
136 - if (job.attempts += 1) < self.class.max_attempts  
137 - time ||= Job.db_time_now + (job.attempts ** 4) + 5 222 + if (job.attempts += 1) < max_attempts(job)
  223 + time ||= job.reschedule_at
138 job.run_at = time 224 job.run_at = time
139 job.unlock 225 job.unlock
140 job.save! 226 job.save!
141 else 227 else
142 - say "PERMANENTLY removing #{job.name} because of #{job.attempts} consecutive failures.", Logger::INFO  
143 -  
144 - if job.payload_object.respond_to? :on_permanent_failure  
145 - say "Running on_permanent_failure hook"  
146 - failure_method = job.payload_object.method(:on_permanent_failure)  
147 - if failure_method.arity == 1  
148 - failure_method.call(job)  
149 - else  
150 - failure_method.call  
151 - end  
152 - end 228 + job_say job, "REMOVED permanently because of #{job.attempts} consecutive failures", Logger::ERROR
  229 + failed(job)
  230 + end
  231 + end
153 232
154 - self.class.destroy_failed_jobs ? job.destroy : job.update_attributes(:failed_at => Delayed::Job.db_time_now) 233 + def failed(job)
  234 + self.class.lifecycle.run_callbacks(:failure, self, job) do
  235 + job.hook(:failure)
  236 + self.class.destroy_failed_jobs ? job.destroy : job.fail!
155 end 237 end
156 end 238 end
157 239
158 - def say(text, level = Logger::INFO) 240 + def job_say(job, text, level = DEFAULT_LOG_LEVEL)
  241 + text = "Job #{job.name} (id=#{job.id}) #{text}"
  242 + say text, level
  243 + end
  244 +
  245 + def say(text, level = DEFAULT_LOG_LEVEL)
159 text = "[Worker(#{name})] #{text}" 246 text = "[Worker(#{name})] #{text}"
160 puts text unless @quiet 247 puts text unless @quiet
161 logger.add level, "#{Time.now.strftime('%FT%T%z')}: #{text}" if logger 248 logger.add level, "#{Time.now.strftime('%FT%T%z')}: #{text}" if logger
162 end 249 end
163 250
  251 + def max_attempts(job)
  252 + job.max_attempts || self.class.max_attempts
  253 + end
  254 +
164 protected 255 protected
165 - 256 +
166 def handle_failed_job(job, error) 257 def handle_failed_job(job, error)
167 - job.last_error = error.message + "\n" + error.backtrace.join("\n")  
168 - say "#{job.name} failed with #{error.class.name}: #{error.message} - #{job.attempts} failed attempts", Logger::ERROR 258 + job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
  259 + job_say job, "FAILED (#{job.attempts} prior attempts) with #{error.class.name}: #{error.message}", Logger::ERROR
169 reschedule(job) 260 reschedule(job)
170 end 261 end
171 - 262 +
172 # Run the next job we can get an exclusive lock on. 263 # Run the next job we can get an exclusive lock on.
173 # If no jobs are left we return nil 264 # If no jobs are left we return nil
174 def reserve_and_run_one_job 265 def reserve_and_run_one_job
  266 + job = reserve_job
  267 + self.class.lifecycle.run_callbacks(:perform, self, job){ run(job) } if job
  268 + end
175 269
176 - # We get up to 5 jobs from the db. In case we cannot get exclusive access to a job we try the next.  
177 - # this leads to a more even distribution of jobs across the worker processes  
178 - job = Delayed::Job.find_available(name, 5, self.class.max_run_time).detect do |job|  
179 - if job.lock_exclusively!(self.class.max_run_time, name)  
180 - say "acquired lock on #{job.name}"  
181 - true  
182 - else  
183 - say "failed to acquire exclusive lock for #{job.name}", Logger::WARN  
184 - false  
185 - end  
186 - end  
187 -  
188 - run(job) if job 270 + def reserve_job
  271 + job = Delayed::Job.reserve(self)
  272 + @failed_reserve_count = 0
  273 + job
  274 + rescue Exception => error
  275 + say "Error while reserving job: #{error}"
  276 + Delayed::Job.recover_from(error)
  277 + @failed_reserve_count += 1
  278 + raise FatalBackendError if @failed_reserve_count >= 10
  279 + nil
189 end 280 end
190 end 281 end
  282 +
191 end 283 end
vendor/plugins/delayed_job/lib/delayed/yaml_ext.rb
@@ -2,34 +2,9 @@ @@ -2,34 +2,9 @@
2 # Classes, Modules and Structs 2 # Classes, Modules and Structs
3 3
4 require 'yaml' 4 require 'yaml'
5 -  
6 -class Module  
7 - yaml_as "tag:ruby.yaml.org,2002:module"  
8 -  
9 - def self.yaml_new(klass, tag, val)  
10 - val.constantize  
11 - end  
12 -  
13 - def to_yaml( opts = {} )  
14 - YAML::quick_emit( nil, opts ) { |out|  
15 - out.scalar(taguri, self.name, :plain)  
16 - }  
17 - end  
18 -  
19 - def yaml_tag_read_class(name)  
20 - # Constantize the object so that ActiveSupport can attempt  
21 - # its auto loading magic. Will raise LoadError if not successful.  
22 - name.constantize  
23 - name  
24 - end  
25 -  
26 -end  
27 -  
28 -class Struct  
29 - def self.yaml_tag_read_class(name)  
30 - # Constantize the object so that ActiveSupport can attempt  
31 - # its auto loading magic. Will raise LoadError if not successful.  
32 - name.constantize  
33 - "Struct::#{ name }"  
34 - end 5 +if YAML.parser.class.name =~ /syck|yecht/i
  6 + require File.expand_path('../syck_ext', __FILE__)
  7 + require File.expand_path('../serialization/active_record', __FILE__)
  8 +else
  9 + require File.expand_path('../psych_ext', __FILE__)
35 end 10 end
vendor/plugins/delayed_job/lib/delayed_job.rb
1 require 'active_support' 1 require 'active_support'
  2 +require 'delayed/compatibility'
  3 +require 'delayed/exceptions'
  4 +require 'delayed/message_sending'
  5 +require 'delayed/performable_method'
2 6
3 -require File.dirname(__FILE__) + '/delayed/message_sending'  
4 -require File.dirname(__FILE__) + '/delayed/performable_method'  
5 -require File.dirname(__FILE__) + '/delayed/yaml_ext'  
6 -require File.dirname(__FILE__) + '/delayed/backend/base'  
7 -require File.dirname(__FILE__) + '/delayed/worker'  
8 -require File.dirname(__FILE__) + '/delayed/railtie' if defined?(::Rails::Railtie) 7 +if defined?(ActionMailer)
  8 + require 'action_mailer/version'
  9 + require 'delayed/performable_mailer'
  10 +end
9 11
10 -Object.send(:include, Delayed::MessageSending)  
11 -Module.send(:include, Delayed::MessageSending::ClassMethods) 12 +require 'delayed/yaml_ext'
  13 +require 'delayed/lifecycle'
  14 +require 'delayed/plugin'
  15 +require 'delayed/plugins/clear_locks'
  16 +require 'delayed/backend/base'
  17 +require 'delayed/worker'
  18 +require 'delayed/deserialization_error'
  19 +require 'delayed/railtie' if defined?(Rails::Railtie)
12 20
13 -if defined?(Merb::Plugins)  
14 - Merb::Plugins.add_rakefiles File.dirname(__FILE__) / 'delayed' / 'tasks'  
15 -end 21 +Object.send(:include, Delayed::MessageSending)
  22 +Module.send(:include, Delayed::MessageSending::ClassMethods)
vendor/plugins/delayed_job/lib/generators/delayed_job/delayed_job_generator.rb
1 require 'rails/generators' 1 require 'rails/generators'
2 -require 'rails/generators/migration' 2 +require 'delayed/compatibility'
3 3
4 class DelayedJobGenerator < Rails::Generators::Base 4 class DelayedJobGenerator < Rails::Generators::Base
5 5
6 - include Rails::Generators::Migration  
7 -  
8 - def self.source_root  
9 - @source_root ||= File.join(File.dirname(__FILE__), 'templates')  
10 - end 6 + self.source_paths << File.join(File.dirname(__FILE__), 'templates')
11 7
12 - # Implement the required interface for Rails::Generators::Migration.  
13 - #  
14 - def self.next_migration_number(dirname) #:nodoc:  
15 - next_migration_number = current_migration_number(dirname) + 1  
16 - if ActiveRecord::Base.timestamped_migrations  
17 - [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max  
18 - else  
19 - "%.3d" % next_migration_number  
20 - end  
21 - end  
22 -  
23 - def create_script_file  
24 - template 'script', 'script/delayed_job'  
25 - chmod 'script/delayed_job', 0755 8 + def create_executable_file
  9 + template "script", "#{Delayed::Compatibility.executable_prefix}/delayed_job"
  10 + chmod "#{Delayed::Compatibility.executable_prefix}/delayed_job", 0755
26 end 11 end
27 -  
28 - def create_migration_file  
29 - if defined?(ActiveRecord)  
30 - migration_template 'migration.rb', 'db/migrate/create_delayed_jobs.rb'  
31 - end  
32 - end  
33 -  
34 -end  
35 \ No newline at end of file 12 \ No newline at end of file
  13 +end
vendor/plugins/delayed_job/lib/generators/delayed_job/templates/migration.rb
@@ -1,21 +0,0 @@ @@ -1,21 +0,0 @@
1 -class CreateDelayedJobs < ActiveRecord::Migration  
2 - def self.up  
3 - create_table :delayed_jobs, :force => true do |table|  
4 - table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue  
5 - table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually.  
6 - table.text :handler # YAML-encoded string of the object that will do work  
7 - table.text :last_error # reason for last failure (See Note below)  
8 - table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.  
9 - table.datetime :locked_at # Set when a client is working on this object  
10 - table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)  
11 - table.string :locked_by # Who is working on this object (if locked)  
12 - table.timestamps  
13 - end  
14 -  
15 - add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'  
16 - end  
17 -  
18 - def self.down  
19 - drop_table :delayed_jobs  
20 - end  
21 -end  
22 \ No newline at end of file 0 \ No newline at end of file
vendor/plugins/delayed_job/lib/generators/delayed_job/templates/script 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +#!/usr/bin/env ruby
  2 +
  3 +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
  4 +require 'delayed/command'
  5 +Delayed::Command.new(ARGV).daemonize
vendor/plugins/delayed_job/rails/init.rb
@@ -1 +0,0 @@ @@ -1 +0,0 @@
1 -require 'delayed_job'  
vendor/plugins/delayed_job/spec/autoloaded/clazz.rb
@@ -4,4 +4,4 @@ module Autoloaded @@ -4,4 +4,4 @@ module Autoloaded
4 def perform 4 def perform
5 end 5 end
6 end 6 end
7 -end  
8 \ No newline at end of file 7 \ No newline at end of file
  8 +end
vendor/plugins/delayed_job/spec/autoloaded/instance_clazz.rb 0 → 100644
@@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
  1 +module Autoloaded
  2 + class InstanceClazz
  3 + def perform
  4 + end
  5 + end
  6 +end
vendor/plugins/delayed_job/spec/autoloaded/instance_struct.rb 0 → 100644
@@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
  1 +module Autoloaded
  2 + class InstanceStruct < ::Struct.new(nil)
  3 + def perform
  4 + end
  5 + end
  6 +end
vendor/plugins/delayed_job/spec/autoloaded/struct.rb
@@ -4,4 +4,4 @@ module Autoloaded @@ -4,4 +4,4 @@ module Autoloaded
4 def perform 4 def perform
5 end 5 end
6 end 6 end
7 -end  
8 \ No newline at end of file 7 \ No newline at end of file
  8 +end
vendor/plugins/delayed_job/spec/backend/active_record_job_spec.rb
@@ -1,46 +0,0 @@ @@ -1,46 +0,0 @@
1 -require 'spec_helper'  
2 -require 'backend/shared_backend_spec'  
3 -require 'delayed/backend/active_record'  
4 -  
5 -describe Delayed::Backend::ActiveRecord::Job do  
6 - before(:all) do  
7 - @backend = Delayed::Backend::ActiveRecord::Job  
8 - end  
9 -  
10 - before(:each) do  
11 - Delayed::Backend::ActiveRecord::Job.delete_all  
12 - SimpleJob.runs = 0  
13 - end  
14 -  
15 - after do  
16 - Time.zone = nil  
17 - end  
18 -  
19 - it_should_behave_like 'a backend'  
20 -  
21 - context "db_time_now" do  
22 - it "should return time in current time zone if set" do  
23 - Time.zone = 'Eastern Time (US & Canada)'  
24 - %w(EST EDT).should include(Delayed::Job.db_time_now.zone)  
25 - end  
26 -  
27 - it "should return UTC time if that is the AR default" do  
28 - Time.zone = nil  
29 - ActiveRecord::Base.default_timezone = :utc  
30 - Delayed::Backend::ActiveRecord::Job.db_time_now.zone.should == 'UTC'  
31 - end  
32 -  
33 - it "should return local time if that is the AR default" do  
34 - Time.zone = 'Central Time (US & Canada)'  
35 - ActiveRecord::Base.default_timezone = :local  
36 - %w(CST CDT).should include(Delayed::Backend::ActiveRecord::Job.db_time_now.zone)  
37 - end  
38 - end  
39 -  
40 - describe "after_fork" do  
41 - it "should call reconnect on the connection" do  
42 - ActiveRecord::Base.connection.should_receive(:reconnect!)  
43 - Delayed::Backend::ActiveRecord::Job.after_fork  
44 - end  
45 - end  
46 -end  
vendor/plugins/delayed_job/spec/backend/couch_rest_job_spec.rb
@@ -1,15 +0,0 @@ @@ -1,15 +0,0 @@
1 -require 'spec_helper'  
2 -require 'backend/shared_backend_spec'  
3 -require 'delayed/backend/couch_rest'  
4 -  
5 -describe Delayed::Backend::CouchRest::Job do  
6 - before(:all) do  
7 - @backend = Delayed::Backend::CouchRest::Job  
8 - end  
9 -  
10 - before(:each) do  
11 - @backend.delete_all  
12 - end  
13 -  
14 - it_should_behave_like 'a backend'  
15 -end  
vendor/plugins/delayed_job/spec/backend/data_mapper_job_spec.rb
@@ -1,16 +0,0 @@ @@ -1,16 +0,0 @@
1 -require 'spec_helper'  
2 -require 'backend/shared_backend_spec'  
3 -require 'delayed/backend/data_mapper'  
4 -  
5 -describe Delayed::Backend::DataMapper::Job do  
6 - before(:all) do  
7 - @backend = Delayed::Backend::DataMapper::Job  
8 - end  
9 -  
10 - before(:each) do  
11 - # reset database before each example is run  
12 - DataMapper.auto_migrate!  
13 - end  
14 -  
15 - it_should_behave_like 'a backend'  
16 -end  
vendor/plugins/delayed_job/spec/backend/mongo_mapper_job_spec.rb
@@ -1,94 +0,0 @@ @@ -1,94 +0,0 @@
1 -require 'spec_helper'  
2 -require 'backend/shared_backend_spec'  
3 -require 'delayed/backend/mongo_mapper'  
4 -  
5 -describe Delayed::Backend::MongoMapper::Job do  
6 - before(:all) do  
7 - @backend = Delayed::Backend::MongoMapper::Job  
8 - end  
9 -  
10 - before(:each) do  
11 - MongoMapper.database.collections.each(&:remove)  
12 - end  
13 -  
14 - it_should_behave_like 'a backend'  
15 -  
16 - describe "indexes" do  
17 - it "should have combo index on priority and run_at" do  
18 - @backend.collection.index_information.detect { |index| index[0] == 'priority_1_run_at_1' }.should_not be_nil  
19 - end  
20 -  
21 - it "should have index on locked_by" do  
22 - @backend.collection.index_information.detect { |index| index[0] == 'locked_by_1' }.should_not be_nil  
23 - end  
24 - end  
25 -  
26 - describe "delayed method" do  
27 - class MongoStoryReader  
28 - def read(story)  
29 - "Epilog: #{story.tell}"  
30 - end  
31 - end  
32 -  
33 - class MongoStory  
34 - include ::MongoMapper::Document  
35 - key :text, String  
36 -  
37 - def tell  
38 - text  
39 - end  
40 - end  
41 -  
42 - it "should ignore not found errors because they are permanent" do  
43 - story = MongoStory.create :text => 'Once upon a time...'  
44 - job = story.delay.tell  
45 - story.destroy  
46 - lambda { job.invoke_job }.should_not raise_error  
47 - end  
48 -  
49 - it "should store the object as string" do  
50 - story = MongoStory.create :text => 'Once upon a time...'  
51 - job = story.delay.tell  
52 -  
53 - job.payload_object.class.should == Delayed::PerformableMethod  
54 - job.payload_object.object.should == story  
55 - job.payload_object.method.should == :tell  
56 - job.payload_object.args.should == []  
57 - job.payload_object.perform.should == 'Once upon a time...'  
58 - end  
59 -  
60 - it "should store arguments as string" do  
61 - story = MongoStory.create :text => 'Once upon a time...'  
62 - job = MongoStoryReader.new.delay.read(story)  
63 - job.payload_object.class.should == Delayed::PerformableMethod  
64 - job.payload_object.method.should == :read  
65 - job.payload_object.args.should == [story]  
66 - job.payload_object.perform.should == 'Epilog: Once upon a time...'  
67 - end  
68 - end  
69 -  
70 - describe "before_fork" do  
71 - after do  
72 - MongoMapper.connection.connect_to_master  
73 - end  
74 -  
75 - it "should disconnect" do  
76 - lambda do  
77 - Delayed::Backend::MongoMapper::Job.before_fork  
78 - end.should change { !!MongoMapper.connection.connected? }.from(true).to(false)  
79 - end  
80 - end  
81 -  
82 - describe "after_fork" do  
83 - before do  
84 - MongoMapper.connection.close  
85 - end  
86 -  
87 - it "should call reconnect" do  
88 - lambda do  
89 - Delayed::Backend::MongoMapper::Job.after_fork  
90 - end.should change { !!MongoMapper.connection.connected? }.from(false).to(true)  
91 - end  
92 - end  
93 -  
94 -end  
vendor/plugins/delayed_job/spec/backend/shared_backend_spec.rb
@@ -1,279 +0,0 @@ @@ -1,279 +0,0 @@
1 -class NamedJob < Struct.new(:perform)  
2 - def display_name  
3 - 'named_job'  
4 - end  
5 -end  
6 -  
7 -shared_examples_for 'a backend' do  
8 - def create_job(opts = {})  
9 - @backend.create(opts.merge(:payload_object => SimpleJob.new))  
10 - end  
11 -  
12 - before do  
13 - Delayed::Worker.max_priority = nil  
14 - Delayed::Worker.min_priority = nil  
15 - Delayed::Worker.default_priority = 99  
16 - SimpleJob.runs = 0  
17 - end  
18 -  
19 - it "should set run_at automatically if not set" do  
20 - @backend.create(:payload_object => ErrorJob.new ).run_at.should_not be_nil  
21 - end  
22 -  
23 - it "should not set run_at automatically if already set" do  
24 - later = @backend.db_time_now + 5.minutes  
25 - @backend.create(:payload_object => ErrorJob.new, :run_at => later).run_at.should be_close(later, 1)  
26 - end  
27 -  
28 - it "should raise ArgumentError when handler doesn't respond_to :perform" do  
29 - lambda { @backend.enqueue(Object.new) }.should raise_error(ArgumentError)  
30 - end  
31 -  
32 - it "should increase count after enqueuing items" do  
33 - @backend.enqueue SimpleJob.new  
34 - @backend.count.should == 1  
35 - end  
36 -  
37 - it "should be able to set priority when enqueuing items" do  
38 - @job = @backend.enqueue SimpleJob.new, 5  
39 - @job.priority.should == 5  
40 - end  
41 -  
42 - it "should use default priority when it is not set" do  
43 - @job = @backend.enqueue SimpleJob.new  
44 - @job.priority.should == 99  
45 - end  
46 -  
47 - it "should be able to set run_at when enqueuing items" do  
48 - later = @backend.db_time_now + 5.minutes  
49 - @job = @backend.enqueue SimpleJob.new, 5, later  
50 - @job.run_at.should be_close(later, 1)  
51 - end  
52 -  
53 - it "should work with jobs in modules" do  
54 - M::ModuleJob.runs = 0  
55 - job = @backend.enqueue M::ModuleJob.new  
56 - lambda { job.invoke_job }.should change { M::ModuleJob.runs }.from(0).to(1)  
57 - end  
58 -  
59 - describe "payload_object" do  
60 - it "should raise a DeserializationError when the job class is totally unknown" do  
61 - job = @backend.new :handler => "--- !ruby/object:JobThatDoesNotExist {}"  
62 - lambda { job.payload_object }.should raise_error(Delayed::Backend::DeserializationError)  
63 - end  
64 -  
65 - it "should raise a DeserializationError when the job struct is totally unknown" do  
66 - job = @backend.new :handler => "--- !ruby/struct:StructThatDoesNotExist {}"  
67 - lambda { job.payload_object }.should raise_error(Delayed::Backend::DeserializationError)  
68 - end  
69 -  
70 - it "should autoload classes that are unknown at runtime" do  
71 - job = @backend.new :handler => "--- !ruby/object:Autoloaded::Clazz {}"  
72 - lambda { job.payload_object }.should_not raise_error(Delayed::Backend::DeserializationError)  
73 - end  
74 -  
75 - it "should autoload structs that are unknown at runtime" do  
76 - job = @backend.new :handler => "--- !ruby/struct:Autoloaded::Struct {}"  
77 - lambda { job.payload_object }.should_not raise_error(Delayed::Backend::DeserializationError)  
78 - end  
79 - end  
80 -  
81 - describe "find_available" do  
82 - it "should not find failed jobs" do  
83 - @job = create_job :attempts => 50, :failed_at => @backend.db_time_now  
84 - @backend.find_available('worker', 5, 1.second).should_not include(@job)  
85 - end  
86 -  
87 - it "should not find jobs scheduled for the future" do  
88 - @job = create_job :run_at => (@backend.db_time_now + 1.minute)  
89 - @backend.find_available('worker', 5, 4.hours).should_not include(@job)  
90 - end  
91 -  
92 - it "should not find jobs locked by another worker" do  
93 - @job = create_job(:locked_by => 'other_worker', :locked_at => @backend.db_time_now - 1.minute)  
94 - @backend.find_available('worker', 5, 4.hours).should_not include(@job)  
95 - end  
96 -  
97 - it "should find open jobs" do  
98 - @job = create_job  
99 - @backend.find_available('worker', 5, 4.hours).should include(@job)  
100 - end  
101 -  
102 - it "should find expired jobs" do  
103 - @job = create_job(:locked_by => 'worker', :locked_at => @backend.db_time_now - 2.minutes)  
104 - @backend.find_available('worker', 5, 1.minute).should include(@job)  
105 - end  
106 -  
107 - it "should find own jobs" do  
108 - @job = create_job(:locked_by => 'worker', :locked_at => (@backend.db_time_now - 1.minutes))  
109 - @backend.find_available('worker', 5, 4.hours).should include(@job)  
110 - end  
111 -  
112 - it "should find only the right amount of jobs" do  
113 - 10.times { create_job }  
114 - @backend.find_available('worker', 7, 4.hours).should have(7).jobs  
115 - end  
116 - end  
117 -  
118 - context "when another worker is already performing an task, it" do  
119 -  
120 - before :each do  
121 - @job = @backend.create :payload_object => SimpleJob.new, :locked_by => 'worker1', :locked_at => @backend.db_time_now - 5.minutes  
122 - end  
123 -  
124 - it "should not allow a second worker to get exclusive access" do  
125 - @job.lock_exclusively!(4.hours, 'worker2').should == false  
126 - end  
127 -  
128 - it "should allow a second worker to get exclusive access if the timeout has passed" do  
129 - @job.lock_exclusively!(1.minute, 'worker2').should == true  
130 - end  
131 -  
132 - it "should be able to get access to the task if it was started more then max_age ago" do  
133 - @job.locked_at = 5.hours.ago  
134 - @job.save  
135 -  
136 - @job.lock_exclusively! 4.hours, 'worker2'  
137 - @job.reload  
138 - @job.locked_by.should == 'worker2'  
139 - @job.locked_at.should > 1.minute.ago  
140 - end  
141 -  
142 - it "should not be found by another worker" do  
143 - @backend.find_available('worker2', 1, 6.minutes).length.should == 0  
144 - end  
145 -  
146 - it "should be found by another worker if the time has expired" do  
147 - @backend.find_available('worker2', 1, 4.minutes).length.should == 1  
148 - end  
149 -  
150 - it "should be able to get exclusive access again when the worker name is the same" do  
151 - @job.lock_exclusively!(5.minutes, 'worker1').should be_true  
152 - @job.lock_exclusively!(5.minutes, 'worker1').should be_true  
153 - @job.lock_exclusively!(5.minutes, 'worker1').should be_true  
154 - end  
155 - end  
156 -  
157 - context "when another worker has worked on a task since the job was found to be available, it" do  
158 -  
159 - before :each do  
160 - @job = @backend.create :payload_object => SimpleJob.new  
161 - @job_copy_for_worker_2 = @backend.find(@job.id)  
162 - end  
163 -  
164 - it "should not allow a second worker to get exclusive access if already successfully processed by worker1" do  
165 - @job.destroy  
166 - @job_copy_for_worker_2.lock_exclusively!(4.hours, 'worker2').should == false  
167 - end  
168 -  
169 - it "should not allow a second worker to get exclusive access if failed to be processed by worker1 and run_at time is now in future (due to backing off behaviour)" do  
170 - @job.update_attributes(:attempts => 1, :run_at => 1.day.from_now)  
171 - @job_copy_for_worker_2.lock_exclusively!(4.hours, 'worker2').should == false  
172 - end  
173 - end  
174 -  
175 - context "#name" do  
176 - it "should be the class name of the job that was enqueued" do  
177 - @backend.create(:payload_object => ErrorJob.new ).name.should == 'ErrorJob'  
178 - end  
179 -  
180 - it "should be the method that will be called if its a performable method object" do  
181 - job = @backend.new(:payload_object => NamedJob.new)  
182 - job.name.should == 'named_job'  
183 - end  
184 -  
185 - it "should be the instance method that will be called if its a performable method object" do  
186 - @job = Story.create(:text => "...").delay.save  
187 - @job.name.should == 'Story#save'  
188 - end  
189 - end  
190 -  
191 - context "worker prioritization" do  
192 - before(:each) do  
193 - Delayed::Worker.max_priority = nil  
194 - Delayed::Worker.min_priority = nil  
195 - end  
196 -  
197 - it "should fetch jobs ordered by priority" do  
198 - 10.times { @backend.enqueue SimpleJob.new, rand(10) }  
199 - jobs = @backend.find_available('worker', 10)  
200 - jobs.size.should == 10  
201 - jobs.each_cons(2) do |a, b|  
202 - a.priority.should <= b.priority  
203 - end  
204 - end  
205 -  
206 - it "should only find jobs greater than or equal to min priority" do  
207 - min = 5  
208 - Delayed::Worker.min_priority = min  
209 - 10.times {|i| @backend.enqueue SimpleJob.new, i }  
210 - jobs = @backend.find_available('worker', 10)  
211 - jobs.each {|job| job.priority.should >= min}  
212 - end  
213 -  
214 - it "should only find jobs less than or equal to max priority" do  
215 - max = 5  
216 - Delayed::Worker.max_priority = max  
217 - 10.times {|i| @backend.enqueue SimpleJob.new, i }  
218 - jobs = @backend.find_available('worker', 10)  
219 - jobs.each {|job| job.priority.should <= max}  
220 - end  
221 - end  
222 -  
223 - context "clear_locks!" do  
224 - before do  
225 - @job = create_job(:locked_by => 'worker', :locked_at => @backend.db_time_now)  
226 - end  
227 -  
228 - it "should clear locks for the given worker" do  
229 - @backend.clear_locks!('worker')  
230 - @backend.find_available('worker2', 5, 1.minute).should include(@job)  
231 - end  
232 -  
233 - it "should not clear locks for other workers" do  
234 - @backend.clear_locks!('worker1')  
235 - @backend.find_available('worker1', 5, 1.minute).should_not include(@job)  
236 - end  
237 - end  
238 -  
239 - context "unlock" do  
240 - before do  
241 - @job = create_job(:locked_by => 'worker', :locked_at => @backend.db_time_now)  
242 - end  
243 -  
244 - it "should clear locks" do  
245 - @job.unlock  
246 - @job.locked_by.should be_nil  
247 - @job.locked_at.should be_nil  
248 - end  
249 - end  
250 -  
251 - context "large handler" do  
252 - before do  
253 - text = "Lorem ipsum dolor sit amet. " * 1000  
254 - @job = @backend.enqueue Delayed::PerformableMethod.new(text, :length, {})  
255 - end  
256 -  
257 - it "should have an id" do  
258 - @job.id.should_not be_nil  
259 - end  
260 - end  
261 -  
262 - describe "yaml serialization" do  
263 - it "should reload changed attributes" do  
264 - job = @backend.enqueue SimpleJob.new  
265 - yaml = job.to_yaml  
266 - job.priority = 99  
267 - job.save  
268 - YAML.load(yaml).priority.should == 99  
269 - end  
270 -  
271 - it "should ignore destroyed records" do  
272 - job = @backend.enqueue SimpleJob.new  
273 - yaml = job.to_yaml  
274 - job.destroy  
275 - lambda { YAML.load(yaml).should be_nil }.should_not raise_error  
276 - end  
277 - end  
278 -  
279 -end  
vendor/plugins/delayed_job/spec/delayed/backend/test.rb 0 → 100644
@@ -0,0 +1,112 @@ @@ -0,0 +1,112 @@
  1 +require 'ostruct'
  2 +
  3 +# An in-memory backend suitable only for testing. Tries to behave as if it were an ORM.
  4 +module Delayed
  5 + module Backend
  6 + module Test
  7 + class Job
  8 + attr_accessor :id
  9 + attr_accessor :priority
  10 + attr_accessor :attempts
  11 + attr_accessor :handler
  12 + attr_accessor :last_error
  13 + attr_accessor :run_at
  14 + attr_accessor :locked_at
  15 + attr_accessor :locked_by
  16 + attr_accessor :failed_at
  17 + attr_accessor :queue
  18 +
  19 + include Delayed::Backend::Base
  20 +
  21 + cattr_accessor :id
  22 + self.id = 0
  23 +
  24 + def initialize(hash = {})
  25 + self.attempts = 0
  26 + self.priority = 0
  27 + self.id = (self.class.id += 1)
  28 + hash.each{|k,v| send(:"#{k}=", v)}
  29 + end
  30 +
  31 + @jobs = []
  32 + def self.all
  33 + @jobs
  34 + end
  35 +
  36 + def self.count
  37 + all.size
  38 + end
  39 +
  40 + def self.delete_all
  41 + all.clear
  42 + end
  43 +
  44 + def self.create(attrs = {})
  45 + new(attrs).tap do |o|
  46 + o.save
  47 + end
  48 + end
  49 +
  50 + def self.create!(*args); create(*args); end
  51 +
  52 + def self.clear_locks!(worker_name)
  53 + all.select{|j| j.locked_by == worker_name}.each {|j| j.locked_by = nil; j.locked_at = nil}
  54 + end
  55 +
  56 + # Find a few candidate jobs to run (in case some immediately get locked by others).
  57 + def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)
  58 + jobs = all.select do |j|
  59 + j.run_at <= db_time_now &&
  60 + (j.locked_at.nil? || j.locked_at < db_time_now - max_run_time || j.locked_by == worker_name) &&
  61 + !j.failed?
  62 + end
  63 +
  64 + jobs = jobs.select{|j| Worker.queues.include?(j.queue)} if Worker.queues.any?
  65 + jobs = jobs.select{|j| j.priority >= Worker.min_priority} if Worker.min_priority
  66 + jobs = jobs.select{|j| j.priority <= Worker.max_priority} if Worker.max_priority
  67 + jobs.sort_by{|j| [j.priority, j.run_at]}[0..limit-1]
  68 + end
  69 +
  70 + # Lock this job for this worker.
  71 + # Returns true if we have the lock, false otherwise.
  72 + def lock_exclusively!(max_run_time, worker)
  73 + now = self.class.db_time_now
  74 + if locked_by != worker
  75 + # We don't own this job so we will update the locked_by name and the locked_at
  76 + self.locked_at = now
  77 + self.locked_by = worker
  78 + end
  79 +
  80 + return true
  81 + end
  82 +
  83 + def self.db_time_now
  84 + Time.current
  85 + end
  86 +
  87 + def update_attributes(attrs = {})
  88 + attrs.each{|k,v| send(:"#{k}=", v)}
  89 + save
  90 + end
  91 +
  92 + def destroy
  93 + self.class.all.delete(self)
  94 + end
  95 +
  96 + def save
  97 + self.run_at ||= Time.current
  98 +
  99 + self.class.all << self unless self.class.all.include?(self)
  100 + true
  101 + end
  102 +
  103 + def save!; save; end
  104 +
  105 + def reload
  106 + reset
  107 + self
  108 + end
  109 + end
  110 + end
  111 + end
  112 +end
vendor/plugins/delayed_job/spec/delayed/serialization/test.rb 0 → 100644
vendor/plugins/delayed_job/spec/helper.rb 0 → 100644
@@ -0,0 +1,61 @@ @@ -0,0 +1,61 @@
  1 +require 'logger'
  2 +require 'rspec'
  3 +
  4 +require 'action_mailer'
  5 +require 'active_support/dependencies'
  6 +require 'active_record'
  7 +
  8 +require 'delayed_job'
  9 +require 'delayed/backend/shared_spec'
  10 +
  11 +require 'simplecov'
  12 +require 'coveralls'
  13 +
  14 +SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
  15 + SimpleCov::Formatter::HTMLFormatter,
  16 + Coveralls::SimpleCov::Formatter
  17 +]
  18 +SimpleCov.start
  19 +
  20 +Delayed::Worker.logger = Logger.new('/tmp/dj.log')
  21 +ENV['RAILS_ENV'] = 'test'
  22 +
  23 +Delayed::Worker.backend = :test
  24 +
  25 +# Add this directory so the ActiveSupport autoloading works
  26 +ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
  27 +
  28 +# Add this to simulate Railtie initializer being executed
  29 +ActionMailer::Base.send(:extend, Delayed::DelayMail)
  30 +
  31 +
  32 +# Used to test interactions between DJ and an ORM
  33 +ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
  34 +ActiveRecord::Base.logger = Delayed::Worker.logger
  35 +ActiveRecord::Migration.verbose = false
  36 +
  37 +ActiveRecord::Schema.define do
  38 + create_table :stories, :primary_key => :story_id, :force => true do |table|
  39 + table.string :text
  40 + table.boolean :scoped, :default => true
  41 + end
  42 +end
  43 +
  44 +class Story < ActiveRecord::Base
  45 + self.primary_key = 'story_id'
  46 + def tell; text; end
  47 + def whatever(n, _); tell*n; end
  48 + default_scope { where(:scoped => true) }
  49 +
  50 + handle_asynchronously :whatever
  51 +end
  52 +
  53 +RSpec.configure do |config|
  54 + config.after(:each) do
  55 + Delayed::Worker.reset
  56 + end
  57 +
  58 + config.expect_with :rspec do |c|
  59 + c.syntax = :expect
  60 + end
  61 +end
vendor/plugins/delayed_job/spec/lifecycle_spec.rb 0 → 100644
@@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
  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.new { 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 + callback.should_receive(:call).with(*arguments).ordered
  17 + behavior.should_receive(:inside!).ordered
  18 + lifecycle.run_callbacks :execute, *arguments, &wrapped_block
  19 + end
  20 + end
  21 +
  22 + describe "after callbacks" do
  23 + before(:each) do
  24 + lifecycle.after(:execute, &callback)
  25 + end
  26 +
  27 + it "executes after wrapped block" do
  28 + behavior.should_receive(:inside!).ordered
  29 + callback.should_receive(:call).with(*arguments).ordered
  30 + lifecycle.run_callbacks :execute, *arguments, &wrapped_block
  31 + end
  32 + end
  33 +
  34 + describe "around callbacks" do
  35 + before(:each) do
  36 + lifecycle.around(:execute) do |*args, &block|
  37 + behavior.before!
  38 + block.call(*args)
  39 + behavior.after!
  40 + end
  41 + end
  42 +
  43 + it "wraps a block" do
  44 + behavior.should_receive(:before!).ordered
  45 + behavior.should_receive(:inside!).ordered
  46 + behavior.should_receive(:after!).ordered
  47 + lifecycle.run_callbacks :execute, *arguments, &wrapped_block
  48 + end
  49 +
  50 + it "executes multiple callbacks in order" do
  51 + behavior.should_receive(:one).ordered
  52 + behavior.should_receive(:two).ordered
  53 + behavior.should_receive(:three).ordered
  54 +
  55 + lifecycle.around(:execute) { |*args, &block| behavior.one; block.call(*args) }
  56 + lifecycle.around(:execute) { |*args, &block| behavior.two; block.call(*args) }
  57 + lifecycle.around(:execute) { |*args, &block| behavior.three; block.call(*args) }
  58 +
  59 + lifecycle.run_callbacks(:execute, *arguments, &wrapped_block)
  60 + end
  61 + end
  62 +
  63 + it "raises if callback is executed with wrong number of parameters" do
  64 + lifecycle.before(:execute, &callback)
  65 + expect { lifecycle.run_callbacks(:execute, 1,2,3) {} }.to raise_error(ArgumentError, /1 parameter/)
  66 + end
  67 +end
vendor/plugins/delayed_job/spec/message_sending_spec.rb
1 -require 'spec_helper' 1 +require 'helper'
2 2
3 describe Delayed::MessageSending do 3 describe Delayed::MessageSending do
4 describe "handle_asynchronously" do 4 describe "handle_asynchronously" do
5 - class Story < ActiveRecord::Base  
6 - def tell!(arg)  
7 - end 5 + class Story
  6 + def tell!(arg);end
8 handle_asynchronously :tell! 7 handle_asynchronously :tell!
9 end 8 end
10 -  
11 - it "should alias original method" do  
12 - Story.new.should respond_to(:tell_without_delay!)  
13 - Story.new.should respond_to(:tell_with_delay!) 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!)
14 end 13 end
15 -  
16 - it "should create a PerformableMethod" do  
17 - story = Story.create!  
18 - lambda { 14 +
  15 + it "creates a PerformableMethod" do
  16 + story = Story.create
  17 + expect {
19 job = story.tell!(1) 18 job = story.tell!(1)
20 - job.payload_object.class.should == Delayed::PerformableMethod  
21 - job.payload_object.method.should == :tell_without_delay!  
22 - job.payload_object.args.should == [1]  
23 - }.should change { Delayed::Job.count } 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 + }.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.new { self.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.new {|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
24 end 58 end
25 end 59 end
26 60
27 context "delay" do 61 context "delay" do
28 - it "should create a new PerformableMethod job" do  
29 - lambda { 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 {
30 job = "hello".delay.count('l') 76 job = "hello".delay.count('l')
31 - job.payload_object.class.should == Delayed::PerformableMethod  
32 - job.payload_object.method.should == :count  
33 - job.payload_object.args.should == ['l']  
34 - }.should change { Delayed::Job.count }.by(1) 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 + }.to change { Delayed::Job.count }.by(1)
35 end 81 end
36 82
37 - it "should set default priority" do 83 + it "sets default priority" do
38 Delayed::Worker.default_priority = 99 84 Delayed::Worker.default_priority = 99
39 - job = Object.delay.to_s  
40 - job.priority.should == 99  
41 - Delayed::Worker.default_priority = 0 85 + job = FairyTail.delay.to_s
  86 + expect(job.priority).to eq(99)
42 end 87 end
43 88
44 - it "should set job options" do 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
45 run_at = Time.parse('2010-05-03 12:55 AM') 96 run_at = Time.parse('2010-05-03 12:55 AM')
46 - job = Object.delay(:priority => 20, :run_at => run_at).to_s  
47 - job.run_at.should == run_at  
48 - job.priority.should == 20 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 {
  106 + expect {
  107 + fairy_tail.delay.tell
  108 + }.to change(fairy_tail, :happy_ending).from(nil).to(true)
  109 + }.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 {
  116 + expect {
  117 + fairy_tail.delay.tell
  118 + }.not_to change(fairy_tail, :happy_ending)
  119 + }.to change { Delayed::Job.count }.by(1)
49 end 120 end
50 end 121 end
51 end 122 end
vendor/plugins/delayed_job/spec/performable_mailer_spec.rb 0 → 100644
@@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
  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 {
  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 + }.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 {
  25 + MyMailer.signup('john@example.com').delay
  26 + }.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 + mailer_class.should_receive(:signup).with('john@example.com')
  38 + email.should_receive(:deliver)
  39 + mailer.perform
  40 + end
  41 + end
  42 + end
  43 +
  44 +end
vendor/plugins/delayed_job/spec/performable_method_spec.rb
1 -require 'spec_helper' 1 +require 'helper'
2 2
3 describe Delayed::PerformableMethod do 3 describe Delayed::PerformableMethod do
4 describe "perform" do 4 describe "perform" do
5 before do 5 before do
6 @method = Delayed::PerformableMethod.new("foo", :count, ['o']) 6 @method = Delayed::PerformableMethod.new("foo", :count, ['o'])
7 end 7 end
8 - 8 +
9 context "with the persisted record cannot be found" do 9 context "with the persisted record cannot be found" do
10 before do 10 before do
11 @method.object = nil 11 @method.object = nil
12 end 12 end
13 -  
14 - it "should be a no-op if object is nil" do  
15 - lambda { @method.perform }.should_not raise_error 13 +
  14 + it "does nothing if object is nil" do
  15 + expect{@method.perform}.not_to raise_error
16 end 16 end
17 end 17 end
18 -  
19 - it "should call the method on the object" do 18 +
  19 + it "calls the method on the object" do
20 @method.object.should_receive(:count).with('o') 20 @method.object.should_receive(:count).with('o')
21 @method.perform 21 @method.perform
22 end 22 end
23 -  
24 - it "should respond to on_permanent_failure when implemented and target object is called via object.delay.do_something" do  
25 - @method = Delayed::PerformableMethod.new(OnPermanentFailureJob.new, :perform, [])  
26 - @method.respond_to?(:on_permanent_failure).should be_true  
27 - @method.object.should_receive(:on_permanent_failure)  
28 - @method.on_permanent_failure  
29 - end  
30 end 23 end
31 24
32 - it "should raise a NoMethodError if target method doesn't exist" do  
33 - lambda { 25 + it "raises a NoMethodError if target method doesn't exist" do
  26 + expect {
34 Delayed::PerformableMethod.new(Object, :method_that_does_not_exist, []) 27 Delayed::PerformableMethod.new(Object, :method_that_does_not_exist, [])
35 - }.should raise_error(NoMethodError) 28 + }.to raise_error(NoMethodError)
36 end 29 end
37 -  
38 - it "should not raise NoMethodError if target method is private" do 30 +
  31 + it "does not raise NoMethodError if target method is private" do
39 clazz = Class.new do 32 clazz = Class.new do
40 def private_method 33 def private_method
41 end 34 end
42 private :private_method 35 private :private_method
43 end 36 end
44 - lambda { 37 + expect {
45 Delayed::PerformableMethod.new(clazz.new, :private_method, []) 38 Delayed::PerformableMethod.new(clazz.new, :private_method, [])
46 - }.should_not raise_error(NoMethodError) 39 + }.not_to raise_error
  40 + end
  41 +
  42 + describe "hooks" do
  43 + %w(before after success).each do |hook|
  44 + it "delegates #{hook} hook to object" do
  45 + story = Story.create
  46 + job = story.delay.tell
  47 +
  48 + story.should_receive(hook).with(job)
  49 + job.invoke_job
  50 + end
  51 + end
  52 +
  53 + %w(before after success).each do |hook|
  54 + it "delegates #{hook} hook to object" do
  55 + story = Story.create
  56 + job = story.delay.tell
  57 +
  58 + story.should_receive(hook).with(job)
  59 + job.invoke_job
  60 + end
  61 + end
  62 +
  63 + it "delegates enqueue hook to object" do
  64 + story = Story.create
  65 + story.should_receive(:enqueue).with(an_instance_of(Delayed::Job))
  66 + story.delay.tell
  67 + end
  68 +
  69 + it "delegates error hook to object" do
  70 + story = Story.create
  71 + story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
  72 + story.should_receive(:tell).and_raise(RuntimeError)
  73 + expect { story.delay.tell.invoke_job }.to raise_error
  74 + end
  75 +
  76 + it "delegates error hook to object when delay_jobs = false" do
  77 + story = Story.create
  78 + story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
  79 + story.should_receive(:tell).and_raise(RuntimeError)
  80 + expect { story.delay.tell.invoke_job }.to raise_error
  81 + end
  82 +
  83 + it "delegates failure hook to object" do
  84 + method = Delayed::PerformableMethod.new("object", :size, [])
  85 + method.object.should_receive(:failure)
  86 + method.failure
  87 + end
  88 +
  89 + context 'with delay_job == false' do
  90 + before do
  91 + Delayed::Worker.delay_jobs = false
  92 + end
  93 +
  94 + after do
  95 + Delayed::Worker.delay_jobs = true
  96 + end
  97 +
  98 + %w(before after success).each do |hook|
  99 + it "delegates #{hook} hook to object" do
  100 + story = Story.create
  101 + story.should_receive(hook).with(an_instance_of(Delayed::Job))
  102 + story.delay.tell
  103 + end
  104 + end
  105 +
  106 + %w(before after success).each do |hook|
  107 + it "delegates #{hook} hook to object" do
  108 + story = Story.create
  109 + story.should_receive(hook).with(an_instance_of(Delayed::Job))
  110 + story.delay.tell
  111 + end
  112 + end
  113 +
  114 + it "delegates error hook to object" do
  115 + story = Story.create
  116 + story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
  117 + story.should_receive(:tell).and_raise(RuntimeError)
  118 + expect { story.delay.tell }.to raise_error
  119 + end
  120 +
  121 + it "delegates error hook to object when delay_jobs = false" do
  122 + story = Story.create
  123 + story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError))
  124 + story.should_receive(:tell).and_raise(RuntimeError)
  125 + expect { story.delay.tell }.to raise_error
  126 + end
  127 +
  128 + it "delegates failure hook to object when delay_jobs = false" do
  129 + Delayed::Worker.delay_jobs = false
  130 + method = Delayed::PerformableMethod.new("object", :size, [])
  131 + method.object.should_receive(:failure)
  132 + method.failure
  133 + end
  134 + end
47 end 135 end
48 end 136 end
vendor/plugins/delayed_job/spec/sample_jobs.rb
  1 +class NamedJob < Struct.new(:perform)
  2 + def display_name
  3 + 'named_job'
  4 + end
  5 +end
  6 +
1 class SimpleJob 7 class SimpleJob
2 cattr_accessor :runs; self.runs = 0 8 cattr_accessor :runs; self.runs = 0
3 def perform; @@runs += 1; end 9 def perform; @@runs += 1; end
@@ -6,20 +12,64 @@ end @@ -6,20 +12,64 @@ end
6 class ErrorJob 12 class ErrorJob
7 cattr_accessor :runs; self.runs = 0 13 cattr_accessor :runs; self.runs = 0
8 def perform; raise 'did not work'; end 14 def perform; raise 'did not work'; end
9 -end 15 +end
  16 +
  17 +class CustomRescheduleJob < Struct.new(:offset)
  18 + cattr_accessor :runs; self.runs = 0
  19 + def perform; raise 'did not work'; end
  20 + def reschedule_at(time, attempts); time + offset; end
  21 +end
10 22
11 class LongRunningJob 23 class LongRunningJob
12 def perform; sleep 250; end 24 def perform; sleep 250; end
13 end 25 end
14 26
15 class OnPermanentFailureJob < SimpleJob 27 class OnPermanentFailureJob < SimpleJob
16 - def on_permanent_failure  
17 - end 28 + def failure; end
  29 + def max_attempts; 1; end
18 end 30 end
19 31
20 module M 32 module M
21 class ModuleJob 33 class ModuleJob
22 cattr_accessor :runs; self.runs = 0 34 cattr_accessor :runs; self.runs = 0
23 - def perform; @@runs += 1; end 35 + def perform; @@runs += 1; end
  36 + end
  37 +end
  38 +
  39 +class CallbackJob
  40 + cattr_accessor :messages
  41 +
  42 + def enqueue(job)
  43 + self.class.messages << 'enqueue'
  44 + end
  45 +
  46 + def before(job)
  47 + self.class.messages << 'before'
  48 + end
  49 +
  50 + def perform
  51 + self.class.messages << 'perform'
  52 + end
  53 +
  54 + def after(job)
  55 + self.class.messages << 'after'
  56 + end
  57 +
  58 + def success(job)
  59 + self.class.messages << 'success'
  60 + end
  61 +
  62 + def error(job, error)
  63 + self.class.messages << "error: #{error.class}"
  64 + end
  65 +
  66 + def failure(job)
  67 + self.class.messages << 'failure'
  68 + end
  69 +end
  70 +
  71 +class EnqueueJobMod < SimpleJob
  72 + def enqueue(job)
  73 + job.run_at = 20.minutes.from_now
24 end 74 end
25 end 75 end
vendor/plugins/delayed_job/spec/setup/active_record.rb
@@ -1,33 +0,0 @@ @@ -1,33 +0,0 @@
1 -require 'active_record'  
2 -  
3 -ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')  
4 -ActiveRecord::Base.logger = Delayed::Worker.logger  
5 -ActiveRecord::Migration.verbose = false  
6 -  
7 -ActiveRecord::Schema.define do  
8 - create_table :delayed_jobs, :force => true do |table|  
9 - table.integer :priority, :default => 0  
10 - table.integer :attempts, :default => 0  
11 - table.text :handler  
12 - table.text :last_error  
13 - table.datetime :run_at  
14 - table.datetime :locked_at  
15 - table.datetime :failed_at  
16 - table.string :locked_by  
17 - table.timestamps  
18 - end  
19 -  
20 - add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'  
21 -  
22 - create_table :stories, :force => true do |table|  
23 - table.string :text  
24 - end  
25 -end  
26 -  
27 -# Purely useful for test cases...  
28 -class Story < ActiveRecord::Base  
29 - def tell; text; end  
30 - def whatever(n, _); tell*n; end  
31 -  
32 - handle_asynchronously :whatever  
33 -end  
vendor/plugins/delayed_job/spec/setup/couch_rest.rb
@@ -1,7 +0,0 @@ @@ -1,7 +0,0 @@
1 -require 'couchrest'  
2 -require 'delayed/backend/couch_rest'  
3 -  
4 -Delayed::Backend::CouchRest::Job.use_database CouchRest::Server.new.database!('delayed_job_spec')  
5 -  
6 -# try to perform a query to check that we can connect  
7 -Delayed::Backend::CouchRest::Job.all  
8 \ No newline at end of file 0 \ No newline at end of file
vendor/plugins/delayed_job/spec/setup/data_mapper.rb
@@ -1,8 +0,0 @@ @@ -1,8 +0,0 @@
1 -require 'dm-core'  
2 -require 'dm-validations'  
3 -  
4 -require 'delayed/backend/data_mapper'  
5 -  
6 -DataMapper.logger = Delayed::Worker.logger  
7 -DataMapper.setup(:default, "sqlite3::memory:")  
8 -DataMapper.auto_migrate!  
9 \ No newline at end of file 0 \ No newline at end of file
vendor/plugins/delayed_job/spec/setup/mongo_mapper.rb
@@ -1,17 +0,0 @@ @@ -1,17 +0,0 @@
1 -require 'mongo_mapper'  
2 -  
3 -MongoMapper.config = {  
4 - RAILS_ENV => {'database' => 'delayed_job'}  
5 -}  
6 -MongoMapper.connect RAILS_ENV  
7 -  
8 -unless defined?(Story)  
9 - class Story  
10 - include ::MongoMapper::Document  
11 - def tell; text; end  
12 - def whatever(n, _); tell*n; end  
13 - def self.count; end  
14 -  
15 - handle_asynchronously :whatever  
16 - end  
17 -end  
vendor/plugins/delayed_job/spec/spec_helper.rb
@@ -1,31 +0,0 @@ @@ -1,31 +0,0 @@
1 -$:.unshift(File.dirname(__FILE__) + '/../lib')  
2 -  
3 -require 'rubygems'  
4 -require 'spec'  
5 -require 'logger'  
6 -  
7 -gem 'activerecord', ENV['RAILS_VERSION'] if ENV['RAILS_VERSION']  
8 -  
9 -require 'delayed_job'  
10 -require 'sample_jobs'  
11 -  
12 -Delayed::Worker.logger = Logger.new('/tmp/dj.log')  
13 -RAILS_ENV = 'test'  
14 -  
15 -# determine the available backends  
16 -BACKENDS = []  
17 -Dir.glob("#{File.dirname(__FILE__)}/setup/*.rb") do |backend|  
18 - begin  
19 - backend = File.basename(backend, '.rb')  
20 - require "setup/#{backend}"  
21 - require "backend/#{backend}_job_spec"  
22 - BACKENDS << backend.to_sym  
23 - rescue Exception  
24 - puts "Unable to load #{backend} backend: #{$!}"  
25 - end  
26 -end  
27 -  
28 -Delayed::Worker.backend = BACKENDS.first  
29 -  
30 -# Add this directory so the ActiveSupport autoloading works  
31 -ActiveSupport::Dependencies.load_paths << File.dirname(__FILE__)  
vendor/plugins/delayed_job/spec/test_backend_spec.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  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
vendor/plugins/delayed_job/spec/worker_spec.rb
1 -require 'spec_helper' 1 +require 'helper'
2 2
3 describe Delayed::Worker do 3 describe Delayed::Worker do
4 - def job_create(opts = {})  
5 - Delayed::Job.create(opts.merge(:payload_object => SimpleJob.new))  
6 - end  
7 -  
8 describe "backend=" do 4 describe "backend=" do
9 before do 5 before do
10 @clazz = Class.new 6 @clazz = Class.new
11 Delayed::Worker.backend = @clazz 7 Delayed::Worker.backend = @clazz
12 end 8 end
13 9
14 - it "should set the Delayed::Job constant to the backend" do  
15 - Delayed::Job.should == @clazz 10 + after do
  11 + Delayed::Worker.backend = :test
16 end 12 end
17 -  
18 - it "should set backend with a symbol" do  
19 - Delayed::Worker.backend = :active_record  
20 - Delayed::Worker.backend.should == Delayed::Backend::ActiveRecord::Job 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 21 end
22 end 22 end
23 -  
24 - BACKENDS.each do |backend|  
25 - describe "with the #{backend} backend" do  
26 - before do  
27 - Delayed::Worker.backend = backend  
28 - Delayed::Job.delete_all  
29 23
30 - @worker = Delayed::Worker.new(:max_priority => nil, :min_priority => nil, :quiet => true) 24 + describe "job_say" do
  25 + before do
  26 + @worker = Delayed::Worker.new
  27 + @job = double('job', :id => 123, :name => 'ExampleJob')
  28 + end
31 29
32 - SimpleJob.runs = 0  
33 - end  
34 -  
35 - describe "running a job" do  
36 - it "should fail after Worker.max_run_time" do  
37 - begin  
38 - old_max_run_time = Delayed::Worker.max_run_time  
39 - Delayed::Worker.max_run_time = 1.second  
40 - @job = Delayed::Job.create :payload_object => LongRunningJob.new  
41 - @worker.run(@job)  
42 - @job.reload.last_error.should =~ /expired/  
43 - @job.attempts.should == 1  
44 - ensure  
45 - Delayed::Worker.max_run_time = old_max_run_time  
46 - end  
47 - end  
48 - end  
49 -  
50 - context "worker prioritization" do  
51 - before(:each) do  
52 - @worker = Delayed::Worker.new(:max_priority => 5, :min_priority => -5, :quiet => true)  
53 - end 30 + it "logs with job name and id" do
  31 + @worker.should_receive(:say).
  32 + with('Job ExampleJob (id=123) message', Delayed::Worker::DEFAULT_LOG_LEVEL)
  33 + @worker.job_say(@job, 'message')
  34 + end
  35 + end
54 36
55 - it "should only work_off jobs that are >= min_priority" do  
56 - job_create(:priority => -10)  
57 - job_create(:priority => 0)  
58 - @worker.work_off 37 + context "worker read-ahead" do
  38 + before do
  39 + @read_ahead = Delayed::Worker.read_ahead
  40 + end
59 41
60 - SimpleJob.runs.should == 1  
61 - end 42 + after do
  43 + Delayed::Worker.read_ahead = @read_ahead
  44 + end
62 45
63 - it "should only work_off jobs that are <= max_priority" do  
64 - job_create(:priority => 10)  
65 - job_create(:priority => 0) 46 + it "reads five jobs" do
  47 + Delayed::Job.should_receive(:find_available).with(anything, 5, anything).and_return([])
  48 + Delayed::Job.reserve(Delayed::Worker.new)
  49 + end
66 50
67 - @worker.work_off 51 + it "reads a configurable number of jobs" do
  52 + Delayed::Worker.read_ahead = 15
  53 + Delayed::Job.should_receive(:find_available).with(anything, Delayed::Worker.read_ahead, anything).and_return([])
  54 + Delayed::Job.reserve(Delayed::Worker.new)
  55 + end
  56 + end
68 57
69 - SimpleJob.runs.should == 1  
70 - end  
71 - end 58 + context "worker exit on complete" do
  59 + before do
  60 + Delayed::Worker.exit_on_complete = true
  61 + end
72 62
73 - context "while running with locked and expired jobs" do  
74 - before(:each) do  
75 - @worker.name = 'worker1'  
76 - end  
77 -  
78 - it "should not run jobs locked by another worker" do  
79 - job_create(:locked_by => 'other_worker', :locked_at => (Delayed::Job.db_time_now - 1.minutes))  
80 - lambda { @worker.work_off }.should_not change { SimpleJob.runs }  
81 - end  
82 -  
83 - it "should run open jobs" do  
84 - job_create  
85 - lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1)  
86 - end  
87 -  
88 - it "should run expired jobs" do  
89 - expired_time = Delayed::Job.db_time_now - (1.minutes + Delayed::Worker.max_run_time)  
90 - job_create(:locked_by => 'other_worker', :locked_at => expired_time)  
91 - lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1)  
92 - end  
93 -  
94 - it "should run own jobs" do  
95 - job_create(:locked_by => @worker.name, :locked_at => (Delayed::Job.db_time_now - 1.minutes))  
96 - lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1)  
97 - end  
98 - end  
99 -  
100 - describe "failed jobs" do  
101 - before do  
102 - # reset defaults  
103 - Delayed::Worker.destroy_failed_jobs = true  
104 - Delayed::Worker.max_attempts = 25  
105 -  
106 - @job = Delayed::Job.enqueue ErrorJob.new  
107 - end  
108 -  
109 - it "should record last_error when destroy_failed_jobs = false, max_attempts = 1" do  
110 - Delayed::Worker.destroy_failed_jobs = false  
111 - Delayed::Worker.max_attempts = 1  
112 - @worker.run(@job)  
113 - @job.reload  
114 - @job.last_error.should =~ /did not work/  
115 - @job.last_error.should =~ /worker_spec.rb/  
116 - @job.attempts.should == 1  
117 - @job.failed_at.should_not be_nil  
118 - end  
119 -  
120 - it "should re-schedule jobs after failing" do  
121 - @worker.run(@job)  
122 - @job.reload  
123 - @job.last_error.should =~ /did not work/  
124 - @job.last_error.should =~ /sample_jobs.rb:8:in `perform'/  
125 - @job.attempts.should == 1  
126 - @job.run_at.should > Delayed::Job.db_time_now - 10.minutes  
127 - @job.run_at.should < Delayed::Job.db_time_now + 10.minutes  
128 - end  
129 - end  
130 -  
131 - context "reschedule" do  
132 - before do  
133 - @job = Delayed::Job.create :payload_object => SimpleJob.new  
134 - end  
135 -  
136 - share_examples_for "any failure more than Worker.max_attempts times" do  
137 - context "when the job's payload has an #on_permanent_failure hook" do  
138 - before do  
139 - @job = Delayed::Job.create :payload_object => OnPermanentFailureJob.new  
140 - @job.payload_object.should respond_to :on_permanent_failure  
141 - end  
142 -  
143 - it "should run that hook" do  
144 - @job.payload_object.should_receive :on_permanent_failure  
145 - Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }  
146 - end  
147 - end  
148 -  
149 - context "when the job's payload has no #on_permanent_failure hook" do  
150 - # It's a little tricky to test this in a straightforward way,  
151 - # because putting a should_not_receive expectation on  
152 - # @job.payload_object.on_permanent_failure makes that object  
153 - # incorrectly return true to  
154 - # payload_object.respond_to? :on_permanent_failure, which is what  
155 - # reschedule uses to decide whether to call on_permanent_failure.  
156 - # So instead, we just make sure that the payload_object as it  
157 - # already stands doesn't respond_to? on_permanent_failure, then  
158 - # shove it through the iterated reschedule loop and make sure we  
159 - # don't get a NoMethodError (caused by calling that nonexistent  
160 - # on_permanent_failure method).  
161 -  
162 - before do  
163 - @job.payload_object.should_not respond_to(:on_permanent_failure)  
164 - end  
165 -  
166 - it "should not try to run that hook" do  
167 - lambda do  
168 - Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }  
169 - end.should_not raise_exception(NoMethodError)  
170 - end  
171 - end  
172 - end  
173 -  
174 - context "and we want to destroy jobs" do  
175 - before do  
176 - Delayed::Worker.destroy_failed_jobs = true  
177 - end  
178 -  
179 - it_should_behave_like "any failure more than Worker.max_attempts times"  
180 -  
181 - it "should be destroyed if it failed more than Worker.max_attempts times" do  
182 - @job.should_receive(:destroy)  
183 - Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }  
184 - end  
185 -  
186 - it "should not be destroyed if failed fewer than Worker.max_attempts times" do  
187 - @job.should_not_receive(:destroy)  
188 - (Delayed::Worker.max_attempts - 1).times { @worker.reschedule(@job) }  
189 - end  
190 - end  
191 -  
192 - context "and we don't want to destroy jobs" do  
193 - before do  
194 - Delayed::Worker.destroy_failed_jobs = false  
195 - end  
196 -  
197 - it_should_behave_like "any failure more than Worker.max_attempts times"  
198 -  
199 - it "should be failed if it failed more than Worker.max_attempts times" do  
200 - @job.reload.failed_at.should == nil  
201 - Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }  
202 - @job.reload.failed_at.should_not == nil  
203 - end  
204 -  
205 - it "should not be failed if it failed fewer than Worker.max_attempts times" do  
206 - (Delayed::Worker.max_attempts - 1).times { @worker.reschedule(@job) }  
207 - @job.reload.failed_at.should == nil  
208 - end  
209 - end 63 + after do
  64 + Delayed::Worker.exit_on_complete = false
  65 + end
  66 +
  67 + it "exits the loop when no jobs are available" do
  68 + worker = Delayed::Worker.new
  69 + Timeout::timeout(2) do
  70 + worker.start
210 end 71 end
211 end 72 end
212 end 73 end
213 - 74 +
  75 + context "worker job reservation" do
  76 + before do
  77 + Delayed::Worker.exit_on_complete = true
  78 + end
  79 +
  80 + after do
  81 + Delayed::Worker.exit_on_complete = false
  82 + end
  83 +
  84 + it "handles error during job reservation" do
  85 + Delayed::Job.should_receive(:reserve).and_raise(Exception)
  86 + Delayed::Worker.new.work_off
  87 + end
  88 +
  89 + it "gives up after 10 backend failures" do
  90 + Delayed::Job.stub(:reserve).and_raise(Exception)
  91 + worker = Delayed::Worker.new
  92 + 9.times { worker.work_off }
  93 + expect(lambda { worker.work_off }).to raise_exception
  94 + end
  95 +
  96 + it "allows the backend to attempt recovery from reservation errors" do
  97 + Delayed::Job.should_receive(:reserve).and_raise(Exception)
  98 + Delayed::Job.should_receive(:recover_from).with(instance_of(Exception))
  99 + Delayed::Worker.new.work_off
  100 + end
  101 + end
214 end 102 end
vendor/plugins/delayed_job/spec/yaml_ext_spec.rb 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +require 'helper'
  2 +
  3 +describe "YAML" do
  4 + it "autoloads classes" do
  5 + expect {
  6 + yaml = "--- !ruby/class Autoloaded::Clazz\n"
  7 + expect(YAML.load(yaml)).to eq(Autoloaded::Clazz)
  8 + }.not_to raise_error
  9 + end
  10 +
  11 + it "autoloads the class of a struct" do
  12 + expect {
  13 + yaml = "--- !ruby/class Autoloaded::Struct\n"
  14 + expect(YAML.load(yaml)).to eq(Autoloaded::Struct)
  15 + }.not_to raise_error
  16 + end
  17 +
  18 + it "autoloads the class for the instance of a struct" do
  19 + expect {
  20 + yaml = "--- !ruby/struct:Autoloaded::InstanceStruct {}"
  21 + expect(YAML.load(yaml).class).to eq(Autoloaded::InstanceStruct)
  22 + }.not_to raise_error
  23 + end
  24 +
  25 + it "autoloads the class for the instance" do
  26 + expect {
  27 + yaml = "--- !ruby/object:Autoloaded::InstanceClazz {}\n"
  28 + expect(YAML.load(yaml).class).to eq(Autoloaded::InstanceClazz)
  29 + }.not_to raise_error
  30 + end
  31 +
  32 + it "does not throw an uninitialized constant Syck::Syck when using YAML.load with poorly formed yaml" do
  33 + expect{ YAML.load(YAML.dump("foo: *bar"))}.not_to raise_error
  34 + end
  35 +end
vendor/plugins/delayed_job/tasks/jobs.rake
@@ -1 +0,0 @@ @@ -1 +0,0 @@
1 -require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'delayed', 'tasks'))  
vendor/plugins/delayed_job_active_record/.rspec 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 +--color
  2 +--fail-fast
vendor/plugins/delayed_job_active_record/.travis.yml 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +language: ruby
  2 +before_script:
  3 + - mysql -e 'create database delayed_job_test;'
  4 + - psql -c 'create database delayed_job_test;' -U postgres
  5 +script: bundle exec rspec
  6 +gemfile:
  7 + - gemfiles/mysql/3-0.gemfile
  8 + - gemfiles/mysql/3-1.gemfile
  9 + - gemfiles/mysql/3-2.gemfile
  10 + - gemfiles/mysql/4-0.gemfile
  11 + - gemfiles/postgresql/3-0.gemfile
  12 + - gemfiles/postgresql/3-1.gemfile
  13 + - gemfiles/postgresql/3-2.gemfile
  14 + - gemfiles/postgresql/4-0.gemfile
  15 + - gemfiles/sqlite3/3-0.gemfile
  16 + - gemfiles/sqlite3/3-1.gemfile
  17 + - gemfiles/sqlite3/3-2.gemfile
  18 + - gemfiles/sqlite3/4-0.gemfile
  19 + - gemfiles/sqlite3/4-0-protected_attributes.gemfile
  20 +rvm:
  21 + - rbx-19mode
  22 + - jruby-19mode
  23 + - 1.9.3
  24 + - 2.0.0
  25 +matrix:
  26 + allow_failures:
  27 + - rvm: rbx-19mode
  28 + - rvm: jruby-19mode
vendor/plugins/delayed_job_active_record/CONTRIBUTING.md 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +## How to contribute
  2 +
  3 +If you find what looks like a bug:
  4 +
  5 +* Search the [mailing list](http://groups.google.com/group/delayed_job) to see if anyone else had the same issue.
  6 +* Check the [GitHub issue tracker](http://github.com/collectiveidea/delayed_job_active_record/issues/) to see if anyone else has reported issue.
  7 +* If you don't see anything, create an issue with information on how to reproduce it.
  8 +
  9 +If you want to contribute an enhancement or a fix:
  10 +
  11 +* Fork the project on github.
  12 +* Make your changes with tests.
  13 +* Commit the changes without making changes to the Rakefile or any other files that aren't related to your enhancement or fix
  14 +* Send a pull request.
vendor/plugins/delayed_job_active_record/Gemfile 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'activerecord-jdbcmysql-adapter'
  8 + gem 'jdbc-mysql'
  9 +
  10 + gem 'activerecord-jdbcpostgresql-adapter'
  11 + gem 'jdbc-postgres'
  12 +
  13 + gem 'activerecord-jdbcsqlite3-adapter'
  14 + gem 'jdbc-sqlite3'
  15 + end
  16 +
  17 + platforms :ruby, :mswin, :mingw do
  18 + gem 'mysql', '~> 2.8.1'
  19 + gem 'pg'
  20 + gem 'sqlite3'
  21 + end
  22 +
  23 + gem 'coveralls', :require => false
  24 + gem 'rspec', '>= 2.11'
  25 + gem 'simplecov', :require => false
  26 +end
  27 +
  28 +gemspec
vendor/plugins/delayed_job_active_record/LICENSE.md 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +Copyright (c) 2005 Tobias Lütke
  2 +
  3 +Permission is hereby granted, free of charge, to any person obtaining
  4 +a copy of this software and associated documentation files (the
  5 +"Software"), to deal in the Software without restriction, including
  6 +without limitation the rights to use, copy, modify, merge, publish,
  7 +distribute, sublicense, and/or sell copies of the Software, and to
  8 +permit persons to whom the Software is furnished to do so, subject to
  9 +the following conditions:
  10 +
  11 +The above copyright notice and this permission notice shall be
  12 +included in all copies or substantial portions of the Software.
  13 +
  14 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
  17 +NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
vendor/plugins/delayed_job_active_record/README.md 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +# DelayedJob ActiveRecord Backend
  2 +
  3 +[![Gem Version](https://badge.fury.io/rb/delayed_job_active_record.png)](https://rubygems.org/gems/delayed_job_active_record)
  4 +[![Build Status](https://travis-ci.org/collectiveidea/delayed_job_active_record.png)](https://travis-ci.org/collectiveidea/delayed_job_active_record)
  5 +[![Dependency Status](https://gemnasium.com/collectiveidea/delayed_job_active_record.png)](https://gemnasium.com/collectiveidea/delayed_job_active_record)
  6 +[![Code Climate](https://codeclimate.com/github/collectiveidea/delayed_job_active_record.png)](https://codeclimate.com/github/collectiveidea/delayed_job_active_record)
  7 +[![Coverage Status](https://coveralls.io/repos/collectiveidea/delayed_job_active_record/badge.png?branch=master)](https://coveralls.io/r/collectiveidea/delayed_job_active_record)
  8 +
  9 +## Installation
  10 +
  11 +Add the gem to your Gemfile:
  12 +
  13 + gem 'delayed_job_active_record'
  14 +
  15 +Run `bundle install`.
  16 +
  17 +If you're using Rails, run the generator to create the migration for the
  18 +delayed_job table.
  19 +
  20 + rails g delayed_job:active_record
  21 + rake db:migrate
  22 +
  23 +## Upgrading from 2.x to 3.0.0
  24 +
  25 +If you're upgrading from Delayed Job 2.x, run the upgrade generator to create a
  26 +migration to add a column to your delayed_jobs table.
  27 +
  28 + rails g delayed_job:upgrade
  29 + rake db:migrate
  30 +
  31 +That's it. Use [delayed_job as normal](http://github.com/collectiveidea/delayed_job).
vendor/plugins/delayed_job_active_record/Rakefile 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +# -*- encoding: utf-8 -*-
  2 +require "bundler/gem_helper"
  3 +Bundler::GemHelper.install_tasks
  4 +
  5 +require "rspec/core/rake_task"
  6 +
  7 +ADAPTERS = %w(mysql postgresql sqlite3)
  8 +
  9 +ADAPTERS.each do |adapter|
  10 + desc "Run RSpec code examples for #{adapter} adapter"
  11 + RSpec::Core::RakeTask.new(adapter => "#{adapter}:adapter")
  12 +
  13 + namespace adapter do
  14 + task :adapter do
  15 + ENV["ADAPTER"] = adapter
  16 + end
  17 + end
  18 +end
  19 +
  20 +task :coverage do
  21 + ENV["COVERAGE"] = "true"
  22 +end
  23 +
  24 +task :adapter do
  25 + ENV["ADAPTER"] = nil
  26 +end
  27 +
  28 +Rake::Task[:spec].enhance do
  29 + require "simplecov"
  30 + require "coveralls"
  31 +
  32 + Coveralls::SimpleCov::Formatter.new.format(SimpleCov.result)
  33 +end
  34 +
  35 +task default: ([:coverage] + ADAPTERS + [:adapter])
vendor/plugins/delayed_job_active_record/delayed_job_active_record.gemspec 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +# coding: utf-8
  2 +
  3 +Gem::Specification.new do |spec|
  4 + spec.add_dependency 'activerecord', ['>= 3.0', '< 4.1']
  5 + spec.add_dependency 'delayed_job', ['>= 3.0', '< 4.1']
  6 + spec.authors = ["Brian Ryckbost", "Matt Griffin", "Erik Michaels-Ober"]
  7 + spec.description = 'ActiveRecord backend for Delayed::Job, originally authored by Tobias Lütke'
  8 + spec.email = ['bryckbost@gmail.com', 'matt@griffinonline.org', 'sferik@gmail.com']
  9 + spec.files = %w(CONTRIBUTING.md LICENSE.md README.md Rakefile delayed_job_active_record.gemspec)
  10 + spec.files += Dir.glob("lib/**/*.rb")
  11 + spec.files += Dir.glob("spec/**/*")
  12 + spec.homepage = 'http://github.com/collectiveidea/delayed_job_active_record'
  13 + spec.licenses = ['MIT']
  14 + spec.name = 'delayed_job_active_record'
  15 + spec.require_paths = ['lib']
  16 + spec.summary = 'ActiveRecord backend for DelayedJob'
  17 + spec.test_files = Dir.glob("spec/**/*")
  18 + spec.version = '4.0.0'
  19 +end
vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-0.gemfile 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'activerecord-jdbc-adapter'
  8 + gem 'activerecord-jdbcmysql-adapter'
  9 + gem 'jdbc-mysql'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'mysql', '~> 2.8.1'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11'
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 3.0.0"
  21 +end
  22 +
  23 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-1.gemfile 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'activerecord-jdbc-adapter'
  8 + gem 'activerecord-jdbcmysql-adapter'
  9 + gem 'jdbc-mysql'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'mysql', '~> 2.8.1'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11'
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 3.1.0"
  21 +end
  22 +
  23 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-2.gemfile 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'activerecord-jdbc-adapter'
  8 + gem 'activerecord-jdbcmysql-adapter'
  9 + gem 'jdbc-mysql'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'mysql', '~> 2.8.1'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11'
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 3.2.0"
  21 +end
  22 +
  23 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/gemfiles/mysql/4-0.gemfile 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'activerecord-jdbc-adapter'
  8 + gem 'activerecord-jdbcmysql-adapter'
  9 + gem 'jdbc-mysql'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'mysql', '~> 2.9'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11'
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 4.0.0.beta"
  21 +
  22 + gem 'delayed_job', "~> 4.0.0.beta"
  23 +end
  24 +
  25 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-0.gemfile 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'activerecord-jdbc-adapter'
  8 + gem 'activerecord-jdbcpostgresql-adapter'
  9 + gem 'jdbc-postgres'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'pg'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11'
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 3.0.0"
  21 +end
  22 +
  23 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-1.gemfile 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'activerecord-jdbc-adapter'
  8 + gem 'activerecord-jdbcpostgresql-adapter'
  9 + gem 'jdbc-postgres'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'pg'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11'
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 3.1.0"
  21 +end
  22 +
  23 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-2.gemfile 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'activerecord-jdbc-adapter'
  8 + gem 'activerecord-jdbcpostgresql-adapter'
  9 + gem 'jdbc-postgres'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'pg'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11'
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 3.2.0"
  21 +end
  22 +
  23 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/gemfiles/postgresql/4-0.gemfile 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'activerecord-jdbc-adapter'
  8 + gem 'activerecord-jdbcpostgresql-adapter'
  9 + gem 'jdbc-postgres'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'pg'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11'
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 4.0.0.beta"
  21 +
  22 + gem 'delayed_job', "~> 4.0.0.beta"
  23 +end
  24 +
  25 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-0.gemfile 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'jruby-openssl'
  8 + gem 'activerecord-jdbc-adapter'
  9 + gem 'activerecord-jdbcsqlite3-adapter'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'sqlite3'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11'
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 3.0.0"
  21 +end
  22 +
  23 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-1.gemfile 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'jruby-openssl'
  8 + gem 'activerecord-jdbc-adapter'
  9 + gem 'activerecord-jdbcsqlite3-adapter'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'sqlite3'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11'
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 3.1.0"
  21 +end
  22 +
  23 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-2.gemfile 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'jruby-openssl'
  8 + gem 'activerecord-jdbc-adapter'
  9 + gem 'activerecord-jdbcsqlite3-adapter'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'sqlite3'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11'
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 3.2.0"
  21 +end
  22 +
  23 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/4-0-protected_attributes.gemfile 0 → 100644
@@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'jruby-openssl'
  8 + gem 'activerecord-jdbc-adapter'
  9 + gem 'activerecord-jdbcsqlite3-adapter'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'sqlite3'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11', :require => false
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 4.0.0.beta"
  21 + gem 'protected_attributes'
  22 +
  23 + gem 'delayed_job', "~> 4.0.0.beta", :require => false
  24 +end
  25 +
  26 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/4-0.gemfile 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +gem 'rake'
  4 +
  5 +group :test do
  6 + platforms :jruby do
  7 + gem 'jruby-openssl'
  8 + gem 'activerecord-jdbc-adapter'
  9 + gem 'activerecord-jdbcsqlite3-adapter'
  10 + end
  11 +
  12 + platforms :ruby, :mswin, :mingw do
  13 + gem 'sqlite3'
  14 + end
  15 +
  16 + gem 'coveralls', :require => false
  17 + gem 'rspec', '>= 2.11'
  18 + gem 'simplecov', :require => false
  19 +
  20 + gem 'activerecord', "~> 4.0.0.beta"
  21 +
  22 + gem 'delayed_job', "~> 4.0.0.beta"
  23 +end
  24 +
  25 +gemspec :path => "../../"
vendor/plugins/delayed_job_active_record/lib/delayed/backend/active_record.rb 0 → 100644
@@ -0,0 +1,111 @@ @@ -0,0 +1,111 @@
  1 +require 'active_record/version'
  2 +module Delayed
  3 + module Backend
  4 + module ActiveRecord
  5 + # A job object that is persisted to the database.
  6 + # Contains the work object as a YAML field.
  7 + class Job < ::ActiveRecord::Base
  8 + include Delayed::Backend::Base
  9 +
  10 + if ::ActiveRecord::VERSION::MAJOR < 4 || defined?(::ActiveRecord::MassAssignmentSecurity)
  11 + attr_accessible :priority, :run_at, :queue, :payload_object,
  12 + :failed_at, :locked_at, :locked_by, :handler
  13 + end
  14 +
  15 + scope :by_priority, lambda { order('priority ASC, run_at ASC') }
  16 +
  17 + before_save :set_default_run_at
  18 +
  19 + def self.set_delayed_job_table_name
  20 + delayed_job_table_name = "#{::ActiveRecord::Base.table_name_prefix}delayed_jobs"
  21 + self.table_name = delayed_job_table_name
  22 + end
  23 +
  24 + self.set_delayed_job_table_name
  25 +
  26 + def self.ready_to_run(worker_name, max_run_time)
  27 + where('(run_at <= ? AND (locked_at IS NULL OR locked_at < ?) OR locked_by = ?) AND failed_at IS NULL', db_time_now, db_time_now - max_run_time, worker_name)
  28 + end
  29 +
  30 + def self.before_fork
  31 + ::ActiveRecord::Base.clear_all_connections!
  32 + end
  33 +
  34 + def self.after_fork
  35 + ::ActiveRecord::Base.establish_connection
  36 + end
  37 +
  38 + # When a worker is exiting, make sure we don't have any locked jobs.
  39 + def self.clear_locks!(worker_name)
  40 + where(:locked_by => worker_name).update_all(:locked_by => nil, :locked_at => nil)
  41 + end
  42 +
  43 + def self.reserve(worker, max_run_time = Worker.max_run_time)
  44 + # scope to filter to records that are "ready to run"
  45 + ready_scope = self.ready_to_run(worker.name, max_run_time)
  46 +
  47 + # scope to filter to the single next eligible job
  48 + ready_scope = ready_scope.where('priority >= ?', Worker.min_priority) if Worker.min_priority
  49 + ready_scope = ready_scope.where('priority <= ?', Worker.max_priority) if Worker.max_priority
  50 + ready_scope = ready_scope.where(:queue => Worker.queues) if Worker.queues.any?
  51 + ready_scope = ready_scope.by_priority
  52 +
  53 + now = self.db_time_now
  54 +
  55 + # Optimizations for faster lookups on some common databases
  56 + case self.connection.adapter_name
  57 + when "PostgreSQL"
  58 + # Custom SQL required for PostgreSQL because postgres does not support UPDATE...LIMIT
  59 + # This locks the single record 'FOR UPDATE' in the subquery (http://www.postgresql.org/docs/9.0/static/sql-select.html#SQL-FOR-UPDATE-SHARE)
  60 + # Note: active_record would attempt to generate UPDATE...LIMIT like sql for postgres if we use a .limit() filter, but it would not use
  61 + # 'FOR UPDATE' and we would have many locking conflicts
  62 + quoted_table_name = self.connection.quote_table_name(self.table_name)
  63 + subquery_sql = ready_scope.limit(1).lock(true).select('id').to_sql
  64 + reserved = self.find_by_sql(["UPDATE #{quoted_table_name} SET locked_at = ?, locked_by = ? WHERE id IN (#{subquery_sql}) RETURNING *", now, worker.name])
  65 + reserved[0]
  66 + when "MySQL", "Mysql2"
  67 + # This works on MySQL and possibly some other DBs that support UPDATE...LIMIT. It uses separate queries to lock and return the job
  68 + count = ready_scope.limit(1).update_all(:locked_at => now, :locked_by => worker.name)
  69 + return nil if count == 0
  70 + self.where(:locked_at => now, :locked_by => worker.name, :failed_at => nil).first
  71 + when "MSSQL", "Teradata"
  72 + # The MSSQL driver doesn't generate a limit clause when update_all is called directly
  73 + subsubquery_sql = ready_scope.limit(1).to_sql
  74 + # select("id") doesn't generate a subquery, so force a subquery
  75 + subquery_sql = "SELECT id FROM (#{subsubquery_sql}) AS x"
  76 + quoted_table_name = self.connection.quote_table_name(self.table_name)
  77 + sql = ["UPDATE #{quoted_table_name} SET locked_at = ?, locked_by = ? WHERE id IN (#{subquery_sql})", now, worker.name]
  78 + count = self.connection.execute(sanitize_sql(sql))
  79 + return nil if count == 0
  80 + # MSSQL JDBC doesn't support OUTPUT INSERTED.* for returning a result set, so query locked row
  81 + self.where(:locked_at => now, :locked_by => worker.name, :failed_at => nil).first
  82 + else
  83 + # This is our old fashion, tried and true, but slower lookup
  84 + ready_scope.limit(worker.read_ahead).detect do |job|
  85 + count = ready_scope.where(:id => job.id).update_all(:locked_at => now, :locked_by => worker.name)
  86 + count == 1 && job.reload
  87 + end
  88 + end
  89 + end
  90 +
  91 + # Get the current time (GMT or local depending on DB)
  92 + # Note: This does not ping the DB to get the time, so all your clients
  93 + # must have syncronized clocks.
  94 + def self.db_time_now
  95 + if Time.zone
  96 + Time.zone.now
  97 + elsif ::ActiveRecord::Base.default_timezone == :utc
  98 + Time.now.utc
  99 + else
  100 + Time.now
  101 + end
  102 + end
  103 +
  104 + def reload(*args)
  105 + reset
  106 + super
  107 + end
  108 + end
  109 + end
  110 + end
  111 +end
vendor/plugins/delayed_job_active_record/lib/delayed_job_active_record.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +require 'active_record'
  2 +require 'delayed_job'
  3 +require 'delayed/backend/active_record'
  4 +
  5 +Delayed::Worker.backend = :active_record
vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/active_record_generator.rb 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 +require 'generators/delayed_job/delayed_job_generator'
  2 +require 'generators/delayed_job/next_migration_version'
  3 +require 'rails/generators/migration'
  4 +require 'rails/generators/active_record'
  5 +
  6 +# Extend the DelayedJobGenerator so that it creates an AR migration
  7 +module DelayedJob
  8 + class ActiveRecordGenerator < ::DelayedJobGenerator
  9 + include Rails::Generators::Migration
  10 + extend NextMigrationVersion
  11 +
  12 + self.source_paths << File.join(File.dirname(__FILE__), 'templates')
  13 +
  14 + def create_migration_file
  15 + migration_template 'migration.rb', 'db/migrate/create_delayed_jobs.rb'
  16 + end
  17 +
  18 + def self.next_migration_number dirname
  19 + ActiveRecord::Generators::Base.next_migration_number dirname
  20 + end
  21 + end
  22 +end
vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/next_migration_version.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +module DelayedJob
  2 + module NextMigrationVersion
  3 + # while methods have moved around this has been the implementation
  4 + # since ActiveRecord 3.0
  5 + def next_migration_number(dirname)
  6 + next_migration_number = current_migration_number(dirname) + 1
  7 + if ActiveRecord::Base.timestamped_migrations
  8 + [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
  9 + else
  10 + "%.3d" % next_migration_number
  11 + end
  12 + end
  13 + end
  14 +end
vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/templates/migration.rb 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 +class CreateDelayedJobs < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :delayed_jobs, :force => true do |table|
  4 + table.integer :priority, :default => 0, :null => false # Allows some jobs to jump to the front of the queue
  5 + table.integer :attempts, :default => 0, :null => false # Provides for retries, but still fail eventually.
  6 + table.text :handler, :null => false # YAML-encoded string of the object that will do work
  7 + table.text :last_error # reason for last failure (See Note below)
  8 + table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
  9 + table.datetime :locked_at # Set when a client is working on this object
  10 + table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
  11 + table.string :locked_by # Who is working on this object (if locked)
  12 + table.string :queue # The name of the queue this job is in
  13 + table.timestamps
  14 + end
  15 +
  16 + add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
  17 + end
  18 +
  19 + def self.down
  20 + drop_table :delayed_jobs
  21 + end
  22 +end
vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/templates/upgrade_migration.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class AddQueueToDelayedJobs < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :delayed_jobs, :queue, :string
  4 + end
  5 +
  6 + def self.down
  7 + remove_column :delayed_jobs, :queue
  8 + end
  9 +end
vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/upgrade_generator.rb 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 +require 'generators/delayed_job/delayed_job_generator'
  2 +require 'generators/delayed_job/next_migration_version'
  3 +require 'rails/generators/migration'
  4 +require 'rails/generators/active_record'
  5 +
  6 +# Extend the DelayedJobGenerator so that it creates an AR migration
  7 +module DelayedJob
  8 + class UpgradeGenerator < ::DelayedJobGenerator
  9 + include Rails::Generators::Migration
  10 + extend NextMigrationVersion
  11 +
  12 + self.source_paths << File.join(File.dirname(__FILE__), 'templates')
  13 +
  14 + def create_migration_file
  15 + migration_template 'upgrade_migration.rb', 'db/migrate/add_queue_to_delayed_jobs.rb'
  16 + end
  17 +
  18 + def self.next_migration_number dirname
  19 + ActiveRecord::Generators::Base.next_migration_number dirname
  20 + end
  21 + end
  22 +end
vendor/plugins/delayed_job_active_record/spec/database.yml 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +mysql:
  2 + adapter: mysql
  3 + database: delayed_job_test
  4 + username: root
  5 + encoding: utf8
  6 +
  7 +postgresql:
  8 + adapter: postgresql
  9 + database: delayed_job_test
  10 + username: postgres
  11 +
  12 +sqlite3:
  13 + adapter: sqlite3
  14 + database: ":memory:"
vendor/plugins/delayed_job_active_record/spec/delayed/backend/active_record_spec.rb 0 → 100644
@@ -0,0 +1,81 @@ @@ -0,0 +1,81 @@
  1 +require 'helper'
  2 +require 'delayed/backend/active_record'
  3 +
  4 +describe Delayed::Backend::ActiveRecord::Job do
  5 + it_behaves_like 'a delayed_job backend'
  6 +
  7 + context "db_time_now" do
  8 + after do
  9 + Time.zone = nil
  10 + ActiveRecord::Base.default_timezone = :local
  11 + end
  12 +
  13 + it "returns time in current time zone if set" do
  14 + Time.zone = 'Eastern Time (US & Canada)'
  15 + expect(%(EST EDT)).to include(Delayed::Job.db_time_now.zone)
  16 + end
  17 +
  18 + it "returns UTC time if that is the AR default" do
  19 + Time.zone = nil
  20 + ActiveRecord::Base.default_timezone = :utc
  21 + expect(Delayed::Backend::ActiveRecord::Job.db_time_now.zone).to eq 'UTC'
  22 + end
  23 +
  24 + it "returns local time if that is the AR default" do
  25 + Time.zone = 'Central Time (US & Canada)'
  26 + ActiveRecord::Base.default_timezone = :local
  27 + expect(%w(CST CDT)).to include(Delayed::Backend::ActiveRecord::Job.db_time_now.zone)
  28 + end
  29 + end
  30 +
  31 + describe "after_fork" do
  32 + it "calls reconnect on the connection" do
  33 + ActiveRecord::Base.should_receive(:establish_connection)
  34 + Delayed::Backend::ActiveRecord::Job.after_fork
  35 + end
  36 + end
  37 +
  38 + describe "enqueue" do
  39 + it "allows enqueue hook to modify job at DB level" do
  40 + later = described_class.db_time_now + 20.minutes
  41 + job = Delayed::Backend::ActiveRecord::Job.enqueue :payload_object => EnqueueJobMod.new
  42 + expect(Delayed::Backend::ActiveRecord::Job.find(job.id).run_at).to be_within(1).of(later)
  43 + end
  44 + end
  45 +
  46 + if ::ActiveRecord::VERSION::MAJOR < 4 || defined?(::ActiveRecord::MassAssignmentSecurity)
  47 + context "ActiveRecord::Base.send(:attr_accessible, nil)" do
  48 + before do
  49 + Delayed::Backend::ActiveRecord::Job.send(:attr_accessible, nil)
  50 + end
  51 +
  52 + after do
  53 + Delayed::Backend::ActiveRecord::Job.send(:attr_accessible, *Delayed::Backend::ActiveRecord::Job.new.attributes.keys)
  54 + end
  55 +
  56 + it "is still accessible" do
  57 + job = Delayed::Backend::ActiveRecord::Job.enqueue :payload_object => EnqueueJobMod.new
  58 + expect(Delayed::Backend::ActiveRecord::Job.find(job.id).handler).to_not be_blank
  59 + end
  60 + end
  61 + end
  62 +
  63 + context "ActiveRecord::Base.table_name_prefix" do
  64 + it "when prefix is not set, use 'delayed_jobs' as table name" do
  65 + ::ActiveRecord::Base.table_name_prefix = nil
  66 + Delayed::Backend::ActiveRecord::Job.set_delayed_job_table_name
  67 +
  68 + expect(Delayed::Backend::ActiveRecord::Job.table_name).to eq 'delayed_jobs'
  69 + end
  70 +
  71 + it "when prefix is set, prepend it before default table name" do
  72 + ::ActiveRecord::Base.table_name_prefix = 'custom_'
  73 + Delayed::Backend::ActiveRecord::Job.set_delayed_job_table_name
  74 +
  75 + expect(Delayed::Backend::ActiveRecord::Job.table_name).to eq 'custom_delayed_jobs'
  76 +
  77 + ::ActiveRecord::Base.table_name_prefix = nil
  78 + Delayed::Backend::ActiveRecord::Job.set_delayed_job_table_name
  79 + end
  80 + end
  81 +end
vendor/plugins/delayed_job_active_record/spec/delayed/serialization/active_record_spec.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +require 'helper'
  2 +
  3 +describe ActiveRecord do
  4 + it "loads classes with non-default primary key" do
  5 + expect {
  6 + YAML.load(Story.create.to_yaml)
  7 + }.not_to raise_error
  8 + end
  9 +
  10 + it "loads classes even if not in default scope" do
  11 + expect {
  12 + YAML.load(Story.create(:scoped => false).to_yaml)
  13 + }.not_to raise_error
  14 + end
  15 +end
vendor/plugins/delayed_job_active_record/spec/helper.rb 0 → 100644
@@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
  1 +require 'simplecov'
  2 +require 'coveralls'
  3 +
  4 +SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
  5 + SimpleCov::Formatter::HTMLFormatter,
  6 + Coveralls::SimpleCov::Formatter
  7 +]
  8 +SimpleCov.start
  9 +
  10 +require 'logger'
  11 +require 'rspec'
  12 +
  13 +begin
  14 + require 'protected_attributes'
  15 +rescue LoadError
  16 +end
  17 +require 'delayed_job_active_record'
  18 +require 'delayed/backend/shared_spec'
  19 +
  20 +Delayed::Worker.logger = Logger.new('/tmp/dj.log')
  21 +ENV['RAILS_ENV'] = 'test'
  22 +
  23 +db_adapter, gemfile = ENV["ADAPTER"], ENV["BUNDLE_GEMFILE"]
  24 +db_adapter ||= gemfile && gemfile[%r(gemfiles/(.*?)/)] && $1
  25 +db_adapter ||= 'sqlite3'
  26 +
  27 +config = YAML.load(File.read('spec/database.yml'))
  28 +ActiveRecord::Base.establish_connection config[db_adapter]
  29 +ActiveRecord::Base.logger = Delayed::Worker.logger
  30 +ActiveRecord::Migration.verbose = false
  31 +
  32 +ActiveRecord::Schema.define do
  33 + create_table :delayed_jobs, :force => true do |table|
  34 + table.integer :priority, :default => 0
  35 + table.integer :attempts, :default => 0
  36 + table.text :handler
  37 + table.text :last_error
  38 + table.datetime :run_at
  39 + table.datetime :locked_at
  40 + table.datetime :failed_at
  41 + table.string :locked_by
  42 + table.string :queue
  43 + table.timestamps
  44 + end
  45 +
  46 + add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
  47 +
  48 + create_table :stories, :primary_key => :story_id, :force => true do |table|
  49 + table.string :text
  50 + table.boolean :scoped, :default => true
  51 + end
  52 +end
  53 +
  54 +# Purely useful for test cases...
  55 +class Story < ActiveRecord::Base
  56 + if ::ActiveRecord::VERSION::MAJOR < 4 && ActiveRecord::VERSION::MINOR < 2
  57 + set_primary_key :story_id
  58 + else
  59 + self.primary_key = :story_id
  60 + end
  61 + def tell; text; end
  62 + def whatever(n, _); tell*n; end
  63 + default_scope { where(:scoped => true) }
  64 +
  65 + handle_asynchronously :whatever
  66 +end
  67 +
  68 +# Add this directory so the ActiveSupport autoloading works
  69 +ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)