From e227e0e3d4402574e317db2fd372483a07e8c2ea Mon Sep 17 00:00:00 2001 From: Rodrigo Souto Date: Tue, 24 Sep 2013 15:31:57 +0000 Subject: [PATCH] rails3: upgrading delayed_job --- config/initializers/delayed_job_config.rb | 1 + db/migrate/20130924152827_add_queue_to_delayed_jobs.rb | 9 +++++++++ db/schema.rb | 17 ++++++++++------- script/delayed_job | 2 -- vendor/plugins/delayed_job/.rspec | 3 +++ vendor/plugins/delayed_job/.travis.yml | 22 ++++++++++++++++++++++ vendor/plugins/delayed_job/CHANGELOG.md | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job/CONTRIBUTING.md | 27 +++++++++++++++++++++++++++ vendor/plugins/delayed_job/Gemfile | 22 ++++++++++++++++++++++ vendor/plugins/delayed_job/LICENSE.md | 20 ++++++++++++++++++++ vendor/plugins/delayed_job/MIT-LICENSE | 20 -------------------- vendor/plugins/delayed_job/README.md | 346 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job/README.textile | 209 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- vendor/plugins/delayed_job/Rakefile | 54 ++++++------------------------------------------------ vendor/plugins/delayed_job/VERSION | 1 - vendor/plugins/delayed_job/benchmarks.rb | 32 ++++++-------------------------- vendor/plugins/delayed_job/contrib/delayed_job.monitrc | 2 +- vendor/plugins/delayed_job/contrib/delayed_job_multiple.monitrc | 17 ++++++++++++++--- vendor/plugins/delayed_job/contrib/delayed_job_rails_4.monitrc | 14 ++++++++++++++ vendor/plugins/delayed_job/contrib/delayed_job_rails_4_multiple.monitrc | 34 ++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job/delayed_job.gemspec | 136 ++++++++++++++-------------------------------------------------------------------------------------------------------------------------- vendor/plugins/delayed_job/generators/delayed_job/delayed_job_generator.rb | 22 ---------------------- vendor/plugins/delayed_job/generators/delayed_job/templates/migration.rb | 21 --------------------- vendor/plugins/delayed_job/init.rb | 5 ----- vendor/plugins/delayed_job/lib/delayed/backend/active_record.rb | 101 ----------------------------------------------------------------------------------------------------- vendor/plugins/delayed_job/lib/delayed/backend/base.rb | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ vendor/plugins/delayed_job/lib/delayed/backend/couch_rest.rb | 109 ------------------------------------------------------------------------------------------------------------- vendor/plugins/delayed_job/lib/delayed/backend/data_mapper.rb | 121 ------------------------------------------------------------------------------------------------------------------------- vendor/plugins/delayed_job/lib/delayed/backend/mongo_mapper.rb | 106 ---------------------------------------------------------------------------------------------------------- vendor/plugins/delayed_job/lib/delayed/backend/shared_spec.rb | 594 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job/lib/delayed/command.rb | 68 ++++++++++++++++++++++++++++++++++++-------------------------------- vendor/plugins/delayed_job/lib/delayed/compatibility.rb | 27 +++++++++++++++++++++++++++ vendor/plugins/delayed_job/lib/delayed/deserialization_error.rb | 4 ++++ vendor/plugins/delayed_job/lib/delayed/exceptions.rb | 9 +++++++++ vendor/plugins/delayed_job/lib/delayed/lifecycle.rb | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job/lib/delayed/message_sending.rb | 39 +++++++++++++++++++++------------------ vendor/plugins/delayed_job/lib/delayed/performable_mailer.rb | 21 +++++++++++++++++++++ vendor/plugins/delayed_job/lib/delayed/performable_method.rb | 40 +++++++++++++++++++++++++--------------- vendor/plugins/delayed_job/lib/delayed/plugin.rb | 15 +++++++++++++++ vendor/plugins/delayed_job/lib/delayed/plugins/clear_locks.rb | 15 +++++++++++++++ vendor/plugins/delayed_job/lib/delayed/psych_ext.rb | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job/lib/delayed/railtie.rb | 4 +++- vendor/plugins/delayed_job/lib/delayed/recipes.rb | 39 +++++++++++++++++++++++++++++++-------- vendor/plugins/delayed_job/lib/delayed/serialization/active_record.rb | 15 +++++++++++++++ vendor/plugins/delayed_job/lib/delayed/syck_ext.rb | 34 ++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job/lib/delayed/tasks.rb | 37 ++++++++++++++++++++++++++++++------- vendor/plugins/delayed_job/lib/delayed/worker.rb | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------- vendor/plugins/delayed_job/lib/delayed/yaml_ext.rb | 35 +++++------------------------------ vendor/plugins/delayed_job/lib/delayed_job.rb | 29 ++++++++++++++++++----------- vendor/plugins/delayed_job/lib/generators/delayed_job/delayed_job_generator.rb | 34 ++++++---------------------------- vendor/plugins/delayed_job/lib/generators/delayed_job/templates/migration.rb | 21 --------------------- vendor/plugins/delayed_job/lib/generators/delayed_job/templates/script | 5 +++++ vendor/plugins/delayed_job/rails/init.rb | 1 - vendor/plugins/delayed_job/spec/autoloaded/clazz.rb | 2 +- vendor/plugins/delayed_job/spec/autoloaded/instance_clazz.rb | 6 ++++++ vendor/plugins/delayed_job/spec/autoloaded/instance_struct.rb | 6 ++++++ vendor/plugins/delayed_job/spec/autoloaded/struct.rb | 2 +- vendor/plugins/delayed_job/spec/backend/active_record_job_spec.rb | 46 ---------------------------------------------- vendor/plugins/delayed_job/spec/backend/couch_rest_job_spec.rb | 15 --------------- vendor/plugins/delayed_job/spec/backend/data_mapper_job_spec.rb | 16 ---------------- vendor/plugins/delayed_job/spec/backend/mongo_mapper_job_spec.rb | 94 ---------------------------------------------------------------------------------------------- vendor/plugins/delayed_job/spec/backend/shared_backend_spec.rb | 279 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- vendor/plugins/delayed_job/spec/delayed/backend/test.rb | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job/spec/delayed/serialization/test.rb | 0 vendor/plugins/delayed_job/spec/helper.rb | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job/spec/lifecycle_spec.rb | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job/spec/message_sending_spec.rb | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ vendor/plugins/delayed_job/spec/performable_mailer_spec.rb | 44 ++++++++++++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job/spec/performable_method_spec.rb | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------- vendor/plugins/delayed_job/spec/sample_jobs.rb | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- vendor/plugins/delayed_job/spec/setup/active_record.rb | 33 --------------------------------- vendor/plugins/delayed_job/spec/setup/couch_rest.rb | 7 ------- vendor/plugins/delayed_job/spec/setup/data_mapper.rb | 8 -------- vendor/plugins/delayed_job/spec/setup/mongo_mapper.rb | 17 ----------------- vendor/plugins/delayed_job/spec/spec_helper.rb | 31 ------------------------------- vendor/plugins/delayed_job/spec/test_backend_spec.rb | 13 +++++++++++++ vendor/plugins/delayed_job/spec/worker_spec.rb | 270 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- vendor/plugins/delayed_job/spec/yaml_ext_spec.rb | 35 +++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job/tasks/jobs.rake | 1 - vendor/plugins/delayed_job_active_record/.rspec | 2 ++ vendor/plugins/delayed_job_active_record/.travis.yml | 28 ++++++++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/CONTRIBUTING.md | 14 ++++++++++++++ vendor/plugins/delayed_job_active_record/Gemfile | 28 ++++++++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/LICENSE.md | 20 ++++++++++++++++++++ vendor/plugins/delayed_job_active_record/README.md | 31 +++++++++++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/Rakefile | 35 +++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/delayed_job_active_record.gemspec | 19 +++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-0.gemfile | 23 +++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-1.gemfile | 23 +++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-2.gemfile | 23 +++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/mysql/4-0.gemfile | 25 +++++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-0.gemfile | 23 +++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-1.gemfile | 23 +++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-2.gemfile | 23 +++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/postgresql/4-0.gemfile | 25 +++++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-0.gemfile | 23 +++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-1.gemfile | 23 +++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-2.gemfile | 23 +++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/4-0-protected_attributes.gemfile | 26 ++++++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/4-0.gemfile | 25 +++++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/lib/delayed/backend/active_record.rb | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/lib/delayed_job_active_record.rb | 5 +++++ vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/active_record_generator.rb | 22 ++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/next_migration_version.rb | 14 ++++++++++++++ vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/templates/migration.rb | 22 ++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/templates/upgrade_migration.rb | 9 +++++++++ vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/upgrade_generator.rb | 22 ++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/spec/database.yml | 14 ++++++++++++++ vendor/plugins/delayed_job_active_record/spec/delayed/backend/active_record_spec.rb | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vendor/plugins/delayed_job_active_record/spec/delayed/serialization/active_record_spec.rb | 15 +++++++++++++++ vendor/plugins/delayed_job_active_record/spec/helper.rb | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 111 files changed, 3703 insertions(+), 2006 deletions(-) create mode 100644 db/migrate/20130924152827_add_queue_to_delayed_jobs.rb create mode 100644 vendor/plugins/delayed_job/.rspec create mode 100644 vendor/plugins/delayed_job/.travis.yml create mode 100644 vendor/plugins/delayed_job/CHANGELOG.md create mode 100644 vendor/plugins/delayed_job/CONTRIBUTING.md create mode 100644 vendor/plugins/delayed_job/Gemfile create mode 100644 vendor/plugins/delayed_job/LICENSE.md delete mode 100644 vendor/plugins/delayed_job/MIT-LICENSE create mode 100644 vendor/plugins/delayed_job/README.md delete mode 100644 vendor/plugins/delayed_job/README.textile delete mode 100644 vendor/plugins/delayed_job/VERSION create mode 100644 vendor/plugins/delayed_job/contrib/delayed_job_rails_4.monitrc create mode 100644 vendor/plugins/delayed_job/contrib/delayed_job_rails_4_multiple.monitrc delete mode 100644 vendor/plugins/delayed_job/generators/delayed_job/delayed_job_generator.rb delete mode 100644 vendor/plugins/delayed_job/generators/delayed_job/templates/migration.rb delete mode 100644 vendor/plugins/delayed_job/init.rb delete mode 100644 vendor/plugins/delayed_job/lib/delayed/backend/active_record.rb delete mode 100644 vendor/plugins/delayed_job/lib/delayed/backend/couch_rest.rb delete mode 100644 vendor/plugins/delayed_job/lib/delayed/backend/data_mapper.rb delete mode 100644 vendor/plugins/delayed_job/lib/delayed/backend/mongo_mapper.rb create mode 100644 vendor/plugins/delayed_job/lib/delayed/backend/shared_spec.rb create mode 100644 vendor/plugins/delayed_job/lib/delayed/compatibility.rb create mode 100644 vendor/plugins/delayed_job/lib/delayed/deserialization_error.rb create mode 100644 vendor/plugins/delayed_job/lib/delayed/exceptions.rb create mode 100644 vendor/plugins/delayed_job/lib/delayed/lifecycle.rb create mode 100644 vendor/plugins/delayed_job/lib/delayed/performable_mailer.rb create mode 100644 vendor/plugins/delayed_job/lib/delayed/plugin.rb create mode 100644 vendor/plugins/delayed_job/lib/delayed/plugins/clear_locks.rb create mode 100644 vendor/plugins/delayed_job/lib/delayed/psych_ext.rb create mode 100644 vendor/plugins/delayed_job/lib/delayed/serialization/active_record.rb create mode 100644 vendor/plugins/delayed_job/lib/delayed/syck_ext.rb delete mode 100644 vendor/plugins/delayed_job/lib/generators/delayed_job/templates/migration.rb create mode 100644 vendor/plugins/delayed_job/lib/generators/delayed_job/templates/script delete mode 100644 vendor/plugins/delayed_job/rails/init.rb create mode 100644 vendor/plugins/delayed_job/spec/autoloaded/instance_clazz.rb create mode 100644 vendor/plugins/delayed_job/spec/autoloaded/instance_struct.rb delete mode 100644 vendor/plugins/delayed_job/spec/backend/active_record_job_spec.rb delete mode 100644 vendor/plugins/delayed_job/spec/backend/couch_rest_job_spec.rb delete mode 100644 vendor/plugins/delayed_job/spec/backend/data_mapper_job_spec.rb delete mode 100644 vendor/plugins/delayed_job/spec/backend/mongo_mapper_job_spec.rb delete mode 100644 vendor/plugins/delayed_job/spec/backend/shared_backend_spec.rb create mode 100644 vendor/plugins/delayed_job/spec/delayed/backend/test.rb create mode 100644 vendor/plugins/delayed_job/spec/delayed/serialization/test.rb create mode 100644 vendor/plugins/delayed_job/spec/helper.rb create mode 100644 vendor/plugins/delayed_job/spec/lifecycle_spec.rb create mode 100644 vendor/plugins/delayed_job/spec/performable_mailer_spec.rb delete mode 100644 vendor/plugins/delayed_job/spec/setup/active_record.rb delete mode 100644 vendor/plugins/delayed_job/spec/setup/couch_rest.rb delete mode 100644 vendor/plugins/delayed_job/spec/setup/data_mapper.rb delete mode 100644 vendor/plugins/delayed_job/spec/setup/mongo_mapper.rb delete mode 100644 vendor/plugins/delayed_job/spec/spec_helper.rb create mode 100644 vendor/plugins/delayed_job/spec/test_backend_spec.rb create mode 100644 vendor/plugins/delayed_job/spec/yaml_ext_spec.rb delete mode 100644 vendor/plugins/delayed_job/tasks/jobs.rake create mode 100644 vendor/plugins/delayed_job_active_record/.rspec create mode 100644 vendor/plugins/delayed_job_active_record/.travis.yml create mode 100644 vendor/plugins/delayed_job_active_record/CONTRIBUTING.md create mode 100644 vendor/plugins/delayed_job_active_record/Gemfile create mode 100644 vendor/plugins/delayed_job_active_record/LICENSE.md create mode 100644 vendor/plugins/delayed_job_active_record/README.md create mode 100644 vendor/plugins/delayed_job_active_record/Rakefile create mode 100644 vendor/plugins/delayed_job_active_record/delayed_job_active_record.gemspec create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-0.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-1.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-2.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/mysql/4-0.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-0.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-1.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-2.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/postgresql/4-0.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-0.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-1.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-2.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/4-0-protected_attributes.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/4-0.gemfile create mode 100644 vendor/plugins/delayed_job_active_record/lib/delayed/backend/active_record.rb create mode 100644 vendor/plugins/delayed_job_active_record/lib/delayed_job_active_record.rb create mode 100644 vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/active_record_generator.rb create mode 100644 vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/next_migration_version.rb create mode 100644 vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/templates/migration.rb create mode 100644 vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/templates/upgrade_migration.rb create mode 100644 vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/upgrade_generator.rb create mode 100644 vendor/plugins/delayed_job_active_record/spec/database.yml create mode 100644 vendor/plugins/delayed_job_active_record/spec/delayed/backend/active_record_spec.rb create mode 100644 vendor/plugins/delayed_job_active_record/spec/delayed/serialization/active_record_spec.rb create mode 100644 vendor/plugins/delayed_job_active_record/spec/helper.rb diff --git a/config/initializers/delayed_job_config.rb b/config/initializers/delayed_job_config.rb index 9edc6a8..6e1533f 100644 --- a/config/initializers/delayed_job_config.rb +++ b/config/initializers/delayed_job_config.rb @@ -1,3 +1,4 @@ +require 'delayed_job' Delayed::Worker.backend = :active_record Delayed::Worker.max_attempts = 2 Delayed::Worker.max_run_time = 10.minutes diff --git a/db/migrate/20130924152827_add_queue_to_delayed_jobs.rb b/db/migrate/20130924152827_add_queue_to_delayed_jobs.rb new file mode 100644 index 0000000..072c8d4 --- /dev/null +++ b/db/migrate/20130924152827_add_queue_to_delayed_jobs.rb @@ -0,0 +1,9 @@ +class AddQueueToDelayedJobs < ActiveRecord::Migration + def self.up + add_column :delayed_jobs, :queue, :string + end + + def self.down + remove_column :delayed_jobs, :queue + end +end diff --git a/db/schema.rb b/db/schema.rb index 8d39bb1..1866b38 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,15 +1,17 @@ -# This file is auto-generated from the current state of the database. Instead of editing this file, -# please use the migrations feature of Active Record to incrementally modify your database, and -# then regenerate this schema definition. +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. # -# Note that this schema.rb definition is the authoritative source for your database schema. If you need -# to create the application database on another system, you should be using db:schema:load, not running -# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations # you'll amass, the slower it'll run and the greater likelihood for issues). # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130606110602) do +ActiveRecord::Schema.define(:version => 20130924152827) do create_table "abuse_reports", :force => true do |t| t.integer "reporter_id" @@ -245,6 +247,7 @@ ActiveRecord::Schema.define(:version => 20130606110602) do t.string "locked_by" t.datetime "created_at" t.datetime "updated_at" + t.string "queue" end add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority" diff --git a/script/delayed_job b/script/delayed_job index 513d609..04ce568 100755 --- a/script/delayed_job +++ b/script/delayed_job @@ -7,7 +7,5 @@ # etc. The actual feed update logic is DelayedJob plugin. require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) -require 'daemons' require 'delayed/command' - Delayed::Command.new(ARGV).daemonize diff --git a/vendor/plugins/delayed_job/.rspec b/vendor/plugins/delayed_job/.rspec new file mode 100644 index 0000000..0ea59b0 --- /dev/null +++ b/vendor/plugins/delayed_job/.rspec @@ -0,0 +1,3 @@ +--color +--fail-fast +--order random diff --git a/vendor/plugins/delayed_job/.travis.yml b/vendor/plugins/delayed_job/.travis.yml new file mode 100644 index 0000000..c3148f2 --- /dev/null +++ b/vendor/plugins/delayed_job/.travis.yml @@ -0,0 +1,22 @@ +language: ruby +only: + - master +rvm: + - jruby-19mode + - rbx-19mode + - 1.9.2 + - 1.9.3 + - 2.0.0 +env: + matrix: + - "RAILS_VERSION=\"~> 3.0.0\"" + - "RAILS_VERSION=\"~> 3.1.0\"" + - "RAILS_VERSION=\"~> 3.2.0\"" + - "RAILS_VERSION=\"~> 4.0.0\"" +matrix: + exclude: + - rvm: 1.9.2 + env: "RAILS_VERSION=\"~> 4.0.0\"" + allow_failures: + - rvm: jruby-19mode + - rvm: rbx-19mode \ No newline at end of file diff --git a/vendor/plugins/delayed_job/CHANGELOG.md b/vendor/plugins/delayed_job/CHANGELOG.md new file mode 100644 index 0000000..e898264 --- /dev/null +++ b/vendor/plugins/delayed_job/CHANGELOG.md @@ -0,0 +1,170 @@ +4.0.0 - 2013-07-30 +================== +* Rails 4 compatibility +* Reverted threaded startup due to daemons incompatibilities +* Attempt to recover from job reservation errors + +4.0.0.beta2 - 2013-05-28 +======================== +* Rails 4 compatibility +* Threaded startup script for faster multi-worker startup +* YAML compatibility changes +* Added jobs:check rake task + +4.0.0.beta1 - 2013-03-02 +======================== +* Rails 4 compatibility + +3.0.5 - 2013-01-28 +================== +* Better job timeout error logging +* psych support for delayed_job_data_mapper deserialization +* User can configure the worker to raise a SignalException on TERM and/or INT +* Add the ability to run all available jobs and exit when complete + +3.0.4 - 2012-11-09 +================== +* Allow the app to specify a default queue name +* Capistrano script now allows user to specify the DJ command, allowing the user to add "bundle exec" if necessary +* Persisted record check is now more general + +3.0.3 - 2012-05-25 +================== +* Fix a bug where the worker would not respect the exit condition +* Properly handle sleep delay command line argument + +3.0.2 - 2012-04-02 +================== +* Fix deprecation warnings +* Raise ArgumentError if attempting to enqueue a performable method on an object that hasn't been persisted yet +* Allow the number of jobs read at a time to be configured from the command line using --read-ahead +* Allow custom logger to be configured through Delayed::Worker.logger +* Various documentation improvements + +3.0.1 - 2012-01-24 +================== +* Added RecordNotFound message to deserialization error +* Direct JRuby's yecht parser to syck extensions +* Updated psych extensions for better compatibility with ruby 1.9.2 +* Updated syck extension for increased compatibility with class methods +* Test grooming + +3.0.0 - 2011-12-30 +================== +* New: Named queues +* New: Job/Worker lifecycle callbacks +* Change: daemons is no longer a runtime dependency +* Change: Active Record backend support is provided by a separate gem +* Change: Enqueue hook is called before jobs are saved so that they may be modified +* Fix problem deserializing models that use a custom primary key column +* Fix deserializing AR models when the object isn't in the default scope +* Fix hooks not getting called when delay_jobs is false + +2.1.4 - 2011-02-11 +================== +* Working around issues when psych is loaded, fixes issues with bundler 1.0.10 and Rails 3.0.4 +* Added -p/--prefix option to help differentiate multiple delayed job workers on the same host. + +2.1.3 - 2011-01-20 +================== +* Revert worker contention fix due to regressions +* Added Delayed::Worker.delay_jobs flag to support running jobs immediately + +2.1.2 - 2010-12-01 +================== +* Remove contention between multiple workers by performing an update to lock a job before fetching it +* Job payloads may implement #max_attempts to control how many times it should be retried +* Fix for loading ActionMailer extension +* Added 'delayed_job_server_role' Capistrano variable to allow delayed_job to run on its own worker server + set :delayed_job_server_role, :worker +* Fix `rake jobs:work` so it outputs to the console + +2.1.1 - 2010-11-14 +================== +* Fix issue with worker name not getting properly set when locking a job +* Fixes for YAML serialization + +2.1.0 - 2010-11-14 +================== +* Added enqueue, before, after, success, error, and failure. See the README +* Remove Merb support +* Remove all non Active Record backends into separate gems. See https://github.com/collectiveidea/delayed_job/wiki/Backends +* remove rails 2 support. delayed_job 2.1 will only support Rails 3 +* New pure-YAML serialization +* Added Rails 3 railtie and generator +* Changed @@sleep_delay to self.class.sleep_delay to be consistent with other class variable usage +* Added --sleep-delay command line option + +2.0.8 - Unreleased +================== +* Backport fix for deserialization errors that bring down the daemon + +2.0.7 - 2011-02-10 +================== +* Fixed missing generators and recipes for Rails 2.x + +2.0.6 - 2011-01-20 +================== +* Revert worker contention fix due to regressions + +2.0.5 - 2010-12-01 +================== +* Added #reschedule_at hook on payload to determine when the job should be rescheduled [backported from 2.1] +* Added --sleep-delay command line option [backported from 2.1] +* Added 'delayed_job_server_role' Capistrano variable to allow delayed_job to run on its own worker server + set :delayed_job_server_role, :worker +* Changed AR backend to reserve jobs using an UPDATE query to reduce worker contention [backported from 2.1] + +2.0.4 - 2010-11-14 +================== +* Fix issue where dirty tracking prevented job from being properly unlocked +* Add delayed_job_args variable for Capistrano recipe to allow configuration of started workers (e.g. "-n 2 --max-priority 10") +* Added options to handle_asynchronously +* Added Delayed::Worker.default_priority +* Allow private methods to be delayed +* Fixes for Ruby 1.9 +* Added -m command line option to start a monitor process +* normalize logging in worker +* Deprecate #send_later and #send_at in favor of new #delay method +* Added @#delay@ to Object that allows you to delay any method and pass options: + options = {:priority => 19, :run_at => 5.minutes.from_now} + UserMailer.delay(options).deliver_confirmation(@user) + +2.0.3 - 2010-04-16 +================== +* Fix initialization for Rails 2.x + +2.0.2 - 2010-04-08 +================== +* 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 ] +* 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 ] +* Fixed Delayed::Command to create tmp/pids directory [ "8ec8ca41":http://github.com/collectiveidea/delayed_job/commit/8ec8ca41 ] +* Railtie to perform Rails 3 initialization [ "3e0fc41f":http://github.com/collectiveidea/delayed_job/commit/3e0fc41f ] +* Added on_permanent_failure hook [ "d2f14cd6":http://github.com/collectiveidea/delayed_job/commit/d2f14cd6 ] + +2.0.1 - 2010-04-03 +================== +* Bug fix for using ActiveRecord backend with daemon [martinbtt] + +2.0.0 - 2010-04-03 +================== +* Multiple backend support (See README for more details) +* Added MongoMapper backend [zbelzer, moneypools] +* Added DataMapper backend [lpetre] +* 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. +* 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] +* Remove EvaledJob. Implement your own if you need this functionality. +* Only use Time.zone if it is set. Closes #20 +* Fix for last_error recording when destroy_failed_jobs = false, max_attempts = 1 +* Implemented worker name_prefix to maintain dynamic nature of pid detection +* Some Rails 3 compatibility fixes [fredwu] + +1.8.5 - 2010-03-15 +================== +* Set auto_flushing=true on Rails logger to fix logging in production +* Fix error message when trying to send_later on a method that doesn't exist +* Don't use rails_env in capistrano if it's not set. closes #22 +* Delayed job should append to delayed_job.log not overwrite +* Version bump to 1.8.5 +* fixing Time.now to be Time.zone.now if set to honor the app set local TimeZone +* Replaced @Worker::SLEEP@, @Job::MAX_ATTEMPTS@, and @Job::MAX_RUN_TIME@ with class methods that can be overridden. diff --git a/vendor/plugins/delayed_job/CONTRIBUTING.md b/vendor/plugins/delayed_job/CONTRIBUTING.md new file mode 100644 index 0000000..8e0f531 --- /dev/null +++ b/vendor/plugins/delayed_job/CONTRIBUTING.md @@ -0,0 +1,27 @@ +How to contribute +================= + +If you find what looks like a bug: + +* Search the "mailing list":http://groups.google.com/group/delayed_job to see + if anyone else had the same issue. +* Check the "GitHub issue tracker":http://github.com/collectiveidea/delayed_job/issues/ + to see if anyone else has reported issue. +* Make sure you are using the latest version of delayed_job + ![Gem Version](https://badge.fury.io/rb/delayed_job.png) +* Make sure you are using the latest backend gem for delayed_job + * Active Record ![Gem Version](https://badge.fury.io/rb/delayed_job_active_record.png) + * Mongoid ![Gem Version](https://badge.fury.io/rb/delayed_job_mongoid.png) +* If you are still having an issue, create an issue including: + * Ruby version + * Gemfile.lock contents or at least major gem versions, such as Rails version + * Steps to reproduce the issue + * Full backtrace for any errors encountered + +If you want to contribute an enhancement or a fix: + +* Fork the project on GitHub. +* Make your changes with tests. +* Commit the changes without making changes to the Rakefile or any other files + that aren't related to your enhancement or fix. +* Send a pull request. diff --git a/vendor/plugins/delayed_job/Gemfile b/vendor/plugins/delayed_job/Gemfile new file mode 100644 index 0000000..4ac0c53 --- /dev/null +++ b/vendor/plugins/delayed_job/Gemfile @@ -0,0 +1,22 @@ +source 'https://rubygems.org' + +gem 'rake' + +platforms :ruby do + gem 'sqlite3' +end + +platforms :jruby do + gem 'jruby-openssl' + gem 'activerecord-jdbcsqlite3-adapter' +end + +group :test do + gem 'activerecord', (ENV['RAILS_VERSION'] || ['>= 3.0', '< 4.1']) + gem 'actionmailer', (ENV['RAILS_VERSION'] || ['>= 3.0', '< 4.1']) + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false +end + +gemspec diff --git a/vendor/plugins/delayed_job/LICENSE.md b/vendor/plugins/delayed_job/LICENSE.md new file mode 100644 index 0000000..759ff02 --- /dev/null +++ b/vendor/plugins/delayed_job/LICENSE.md @@ -0,0 +1,20 @@ +Copyright (c) 2005 Tobias Lütke + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND +NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/plugins/delayed_job/MIT-LICENSE b/vendor/plugins/delayed_job/MIT-LICENSE deleted file mode 100644 index 926fda1..0000000 --- a/vendor/plugins/delayed_job/MIT-LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2005 Tobias Luetke - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND -NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/plugins/delayed_job/README.md b/vendor/plugins/delayed_job/README.md new file mode 100644 index 0000000..8d2224f --- /dev/null +++ b/vendor/plugins/delayed_job/README.md @@ -0,0 +1,346 @@ +Delayed::Job +============ +[![Gem Version](https://badge.fury.io/rb/delayed_job.png)][gem] +[![Build Status](https://secure.travis-ci.org/collectiveidea/delayed_job.png?branch=master)][travis] +[![Dependency Status](https://gemnasium.com/collectiveidea/delayed_job.png?travis)][gemnasium] +[![Code Climate](https://codeclimate.com/github/collectiveidea/delayed_job.png)][codeclimate] +[![Coverage Status](https://coveralls.io/repos/collectiveidea/delayed_job/badge.png?branch=master)][coveralls] + +[gem]: https://rubygems.org/gems/delayed_job +[travis]: http://travis-ci.org/collectiveidea/delayed_job +[gemnasium]: https://gemnasium.com/collectiveidea/delayed_job +[codeclimate]: https://codeclimate.com/github/collectiveidea/delayed_job +[coveralls]: https://coveralls.io/r/collectiveidea/delayed_job + +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. Amongst those tasks are: + +* sending massive newsletters +* image resizing +* http downloads +* updating smart collections +* updating solr, our search server, after product changes +* batch imports +* spam checks + +[Follow us on Twitter][twitter] to get updates and notices about new releases. + +[twitter]: https://twitter.com/delayedjob + +Installation +============ +delayed_job 3.0.0 only supports Rails 3.0+. See the [2.0 +branch](https://github.com/collectiveidea/delayed_job/tree/v2.0) for Rails 2. + +delayed_job supports multiple backends for storing the job queue. [See the wiki +for other backends](http://wiki.github.com/collectiveidea/delayed_job/backends). + +If you plan to use delayed_job with Active Record, add `delayed_job_active_record` to your `Gemfile`. + +```ruby +gem 'delayed_job_active_record' +``` + +If you plan to use delayed_job with Mongoid, add `delayed_job_mongoid` to your `Gemfile`. + +```ruby +gem 'delayed_job_mongoid' +``` + +Run `bundle install` to install the backend and delayed_job gems. + +The Active Record backend requires a jobs table. You can create that table by +running the following command: + + rails generate delayed_job:active_record + rake db:migrate + +Rails 4 +======= +If you are using the protected_attributes gem, it must appear before delayed_job in your gemfile. + +Upgrading from 2.x to 3.0.0 on Active Record +============================================ +Delayed Job 3.0.0 introduces a new column to the delayed_jobs table. + +If you're upgrading from Delayed Job 2.x, run the upgrade generator to create a migration to add the column. + + rails generate delayed_job:upgrade + rake db:migrate + +Queuing Jobs +============ +Call `.delay.method(params)` on any object and it will be processed in the background. + +```ruby +# without delayed_job +@user.activate!(@device) + +# with delayed_job +@user.delay.activate!(@device) +``` + +If a method should always be run in the background, you can call +`#handle_asynchronously` after the method declaration: + +```ruby +class Device + def deliver + # long running method + end + handle_asynchronously :deliver +end + +device = Device.new +device.deliver +``` + +handle_asynchronously can take as options anything you can pass to delay. In +addition, the values can be Proc objects allowing call time evaluation of the +value. For some examples: + +```ruby +class LongTasks + def send_mailer + # Some other code + end + handle_asynchronously :send_mailer, :priority => 20 + + def in_the_future + # Some other code + end + # 5.minutes.from_now will be evaluated when in_the_future is called + handle_asynchronously :in_the_future, :run_at => Proc.new { 5.minutes.from_now } + + def self.when_to_run + 2.hours.from_now + end + + def call_a_class_method + # Some other code + end + handle_asynchronously :call_a_class_method, :run_at => Proc.new { when_to_run } + + attr_reader :how_important + + def call_an_instance_method + # Some other code + end + handle_asynchronously :call_an_instance_method, :priority => Proc.new {|i| i.how_important } +end +``` + +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`. + +Rails 3 Mailers +=============== +Due to how mailers are implemented in Rails 3, we had to do a little work around to get delayed_job to work. + +```ruby +# without delayed_job +Notifier.signup(@user).deliver + +# with delayed_job +Notifier.delay.signup(@user) +``` + +Remove the `.deliver` method to make it work. It's not ideal, but it's the best +we could do for now. + +Named Queues +============ +DJ 3 introduces Resque-style named queues while still retaining DJ-style +priority. The goal is to provide a system for grouping tasks to be worked by +separate pools of workers, which may be scaled and controlled individually. + +Jobs can be assigned to a queue by setting the `queue` option: + +```ruby +object.delay(:queue => 'tracking').method + +Delayed::Job.enqueue job, :queue => 'tracking' + +handle_asynchronously :tweet_later, :queue => 'tweets' +``` + +Running Jobs +============ +`script/delayed_job` can be used to manage a background process which will +start working off jobs. + +To do so, add `gem "daemons"` to your `Gemfile` and make sure you've run `rails +generate delayed_job`. + +You can then do the following: + + RAILS_ENV=production script/delayed_job start + RAILS_ENV=production script/delayed_job stop + + # Runs two workers in separate processes. + RAILS_ENV=production script/delayed_job -n 2 start + RAILS_ENV=production script/delayed_job stop + + # Set the --queue or --queues option to work from a particular queue. + RAILS_ENV=production script/delayed_job --queue=tracking start + RAILS_ENV=production script/delayed_job --queues=mailers,tasks start + + # Runs all available jobs and then exits + RAILS_ENV=production script/delayed_job start --exit-on-complete + # or to run in the foreground + RAILS_ENV=production script/delayed_job run --exit-on-complete + +**Rails 4:** *replace script/delayed_job with bin/delayed_job* + +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. + +You can also invoke `rake jobs:work` which will start working off jobs. You can +cancel the rake task with `CTRL-C`. + +If you want to just run all available jobs and exit you can use `rake jobs:workoff` + +Work off queues by setting the `QUEUE` or `QUEUES` environment variable. + + QUEUE=tracking rake jobs:work + QUEUES=mailers,tasks rake jobs:work + +Restarting delayed_job +====================== + +The following syntax will restart delayed jobs: + + RAILS_ENV=production script/delayed_job restart + +To restart multiple delayed_job workers: + + RAILS_ENV=production script/delayed_job -n2 restart + +**Rails 4:** *replace script/delayed_job with bin/delayed_job* + + + +Custom Jobs +=========== +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. + +```ruby +class NewsletterJob < Struct.new(:text, :emails) + def perform + emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) } + end +end + +Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.find(:all).collect(&:email)) +``` +To set a per-job max attempts that overrides the Delayed::Worker.max_attempts you can define a max_attempts method on the job +```ruby +class NewsletterJob < Struct.new(:text, :emails) + def perform + emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) } + end + + def max_attempts + return 3 + end +end +```` + + +Hooks +===== +You can define hooks on your job that will be called at different stages in the process: + +```ruby +class ParanoidNewsletterJob < NewsletterJob + def enqueue(job) + record_stat 'newsletter_job/enqueue' + end + + def perform + emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) } + end + + def before(job) + record_stat 'newsletter_job/start' + end + + def after(job) + record_stat 'newsletter_job/after' + end + + def success(job) + record_stat 'newsletter_job/success' + end + + def error(job, exception) + Airbrake.notify(exception) + end + + def failure(job) + page_sysadmin_in_the_middle_of_the_night + end +end +``` + +Gory Details +============ +The library revolves around a delayed_jobs table which looks as follows: + +```ruby +create_table :delayed_jobs, :force => true do |table| + table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue + table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually. + table.text :handler # YAML-encoded string of the object that will do work + table.text :last_error # reason for last failure (See Note below) + table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. + table.datetime :locked_at # Set when a client is working on this object + table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) + table.string :locked_by # Who is working on this object (if locked) + table.string :queue # The name of the queue this job is in + table.timestamps +end +``` + +On failure, the job is scheduled again in 5 seconds + N ** 4, where N is the number of retries. + +The default Worker.max_attempts is 25. After this, the job either deleted (default), or left in the database with "failed_at" set. +With the default of 25 attempts, the last retry will be 20 days later, with the last interval being almost 100 hours. + +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 +make sure your job doesn't exceed this time. You should set this to the longest time you think the job could take. + +By default, it will delete failed jobs (and it always deletes successful jobs). If you want to keep failed jobs, set +Delayed::Worker.destroy_failed_jobs = false. The failed jobs will be marked with non-null failed_at. + +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. + +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. + +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. + +It is possible to disable delayed jobs for testing purposes. Set Delayed::Worker.delay_jobs = false to execute all jobs realtime. + +Here is an example of changing job parameters in Rails: + +```ruby +# config/initializers/delayed_job_config.rb +Delayed::Worker.destroy_failed_jobs = false +Delayed::Worker.sleep_delay = 60 +Delayed::Worker.max_attempts = 3 +Delayed::Worker.max_run_time = 5.minutes +Delayed::Worker.read_ahead = 10 +Delayed::Worker.default_queue_name = 'default' +Delayed::Worker.delay_jobs = !Rails.env.test? +``` + +Cleaning up +=========== +You can invoke `rake jobs:clear` to delete all jobs in the queue. + +Mailing List +============ +Join us on the [mailing list](http://groups.google.com/group/delayed_job) diff --git a/vendor/plugins/delayed_job/README.textile b/vendor/plugins/delayed_job/README.textile deleted file mode 100644 index c021939..0000000 --- a/vendor/plugins/delayed_job/README.textile +++ /dev/null @@ -1,209 +0,0 @@ -h1. Delayed::Job - -Delated_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. Amongst those tasks are: - -* sending massive newsletters -* image resizing -* http downloads -* updating smart collections -* batch imports -* spam checks - -h2. Installation - -To install as a gem, add the following to @config/environment.rb@: - -
-config.gem 'delayed_job'
-
- -Rake tasks are not automatically loaded from gems, so you'll need to add the following to your Rakefile: - -
-begin
-  require 'delayed/tasks'
-rescue LoadError
-  STDERR.puts "Run `rake gems:install` to install delayed_job"
-end
-
- -To install as a plugin: - -
-script/plugin install git://github.com/collectiveidea/delayed_job.git
-
- -After delayed_job is installed, you will need to setup the backend. - -h2. Backends - -delayed_job supports multiple backends for storing the job queue. There are currently implementations for Active Record, MongoMapper, and DataMapper. - -h3. Active Record - -The default is Active Record, which requires a jobs table. - -
-$ script/generate delayed_job
-$ rake db:migrate
-
- -h3. MongoMapper - -You must use @MongoMapper.setup@ in the initializer: - -
-config = YAML::load(File.read(Rails.root.join('config/mongo.yml')))
-MongoMapper.setup(config, Rails.env)
-
-Delayed::Worker.backend = :mongo_mapper
-
- -h3. DataMapper - -
-# config/initializers/delayed_job.rb
-Delayed::Worker.backend = :data_mapper
-Delayed::Worker.backend.auto_upgrade!
-
- -h2. Queuing Jobs - -Call @.delay.method(params)@ on any object and it will be processed in the background. - -
-# without delayed_job
-Notifier.deliver_signup(@user)
-
-# with delayed_job
-Notifier.delay.deliver_signup @user
-
- -If a method should always be run in the background, you can call @#handle_asynchronously@ after the method declaration: - -
-class Device
-  def deliver
-    # long running method
-  end
-  handle_asynchronously :deliver
-end
-
-device = Device.new
-device.deliver
-
- -h2. Running Jobs - -@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`. - -
-$ RAILS_ENV=production script/delayed_job start
-$ RAILS_ENV=production script/delayed_job stop
-
-# Runs two workers in separate processes.
-$ RAILS_ENV=production script/delayed_job -n 2 start
-$ RAILS_ENV=production script/delayed_job stop
-
- -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. - -You can also invoke @rake jobs:work@ which will start working off jobs. You can cancel the rake task with @CTRL-C@. - -h2. Custom Jobs - -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. - -
-class NewsletterJob < Struct.new(:text, :emails)
-  def perform
-    emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
-  end    
-end  
-  
-Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.find(:all).collect(&:email))
-
- -You can also add an optional on_permanent_failure method which will run if the job has failed too many times to be retried: - -
-class ParanoidNewsletterJob < NewsletterJob
-  def perform
-    emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
-  end    
-
-  def on_permanent_failure
-    page_sysadmin_in_the_middle_of_the_night
-  end
-end  
-
- -h2. Gory Details - -The library evolves around a delayed_jobs table which looks as follows: - -
-create_table :delayed_jobs, :force => true do |table|
-  table.integer  :priority, :default => 0      # Allows some jobs to jump to the front of the queue
-  table.integer  :attempts, :default => 0      # Provides for retries, but still fail eventually.
-  table.text     :handler                      # YAML-encoded string of the object that will do work
-  table.text   :last_error                   # reason for last failure (See Note below)
-  table.datetime :run_at                       # When to run. Could be Time.zone.now for immediately, or sometime in the future.
-  table.datetime :locked_at                    # Set when a client is working on this object
-  table.datetime :failed_at                    # Set when all retries have failed (actually, by default, the record is deleted instead)
-  table.string   :locked_by                    # Who is working on this object (if locked)
-  table.timestamps
-end
-
- -On failure, the job is scheduled again in 5 seconds + N ** 4, where N is the number of retries. - -The default Worker.max_attempts is 25. After this, the job either deleted (default), or left in the database with "failed_at" set. -With the default of 25 attempts, the last retry will be 20 days later, with the last interval being almost 100 hours. - -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 -make sure your job doesn't exceed this time. You should set this to the longest time you think the job could take. - -By default, it will delete failed jobs (and it always deletes successful jobs). If you want to keep failed jobs, set -Delayed::Worker.destroy_failed_jobs = false. The failed jobs will be marked with non-null failed_at. - -Here is an example of changing job parameters in Rails: - -
-# config/initializers/delayed_job_config.rb
-Delayed::Worker.destroy_failed_jobs = false
-Delayed::Worker.sleep_delay = 60
-Delayed::Worker.max_attempts = 3
-Delayed::Worker.max_run_time = 5.minutes
-
- -h3. Cleaning up - -You can invoke @rake jobs:clear@ to delete all jobs in the queue. - -h2. Mailing List - -Join us on the mailing list at http://groups.google.com/group/delayed_job - -h2. How to contribute - -If you find what looks like a bug: - -# Check the GitHub issue tracker to see if anyone else has had the same issue. - http://github.com/collectiveidea/delayed_job/issues/ -# If you don't see anything, create an issue with information on how to reproduce it. - -If you want to contribute an enhancement or a fix: - -# Fork the project on github. - http://github.com/collectiveidea/delayed_job/ -# Make your changes with tests. -# Commit the changes without making changes to the Rakefile, VERSION, or any other files that aren't related to your enhancement or fix -# Send a pull request. - -h3. Changelog - -See http://wiki.github.com/collectiveidea/delayed_job/changelog for a list of changes. - diff --git a/vendor/plugins/delayed_job/Rakefile b/vendor/plugins/delayed_job/Rakefile index 15ad3a3..9b760a9 100644 --- a/vendor/plugins/delayed_job/Rakefile +++ b/vendor/plugins/delayed_job/Rakefile @@ -1,53 +1,11 @@ # -*- encoding: utf-8 -*- -begin - require 'jeweler' -rescue LoadError - puts "Jeweler not available. Install it with: sudo gem install jeweler" - exit 1 -end - -Jeweler::Tasks.new do |s| - s.name = "delayed_job" - s.summary = "Database-backed asynchronous priority queue system -- Extracted from Shopify" - s.email = "tobi@leetsoft.com" - s.homepage = "http://github.com/collectiveidea/delayed_job" - 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)." - s.authors = ["Brandon Keepers", "Tobias Lütke"] - - s.has_rdoc = true - s.rdoc_options = ["--main", "README.textile", "--inline-source", "--line-numbers"] - s.extra_rdoc_files = ["README.textile"] - - s.test_files = Dir['spec/*_spec.rb'] - - s.add_dependency "daemons" - s.add_development_dependency "rspec" - s.add_development_dependency "sqlite3-ruby" - s.add_development_dependency "activerecord" - s.add_development_dependency "mongo_mapper" - s.add_development_dependency "dm-core" - s.add_development_dependency "dm-observer" - s.add_development_dependency "dm-aggregates" - s.add_development_dependency "dm-validations" - s.add_development_dependency "do_sqlite3" - s.add_development_dependency "couchrest" -end - -require 'spec/rake/spectask' - - -task :default do - %w(2.3.5 3.0.0.beta3).each do |version| - puts "Running specs with Rails #{version}" - system("RAILS_VERSION=#{version} rake -s spec;") - end -end +require 'bundler/setup' +Bundler::GemHelper.install_tasks +require 'rspec/core/rake_task' desc 'Run the specs' -Spec::Rake::SpecTask.new(:spec) do |t| - t.libs << 'lib' - t.pattern = 'spec/*_spec.rb' - t.verbose = true +RSpec::Core::RakeTask.new do |r| + r.verbose = false end -task :spec => :check_dependencies +task :default => :spec diff --git a/vendor/plugins/delayed_job/VERSION b/vendor/plugins/delayed_job/VERSION deleted file mode 100644 index 654910c..0000000 --- a/vendor/plugins/delayed_job/VERSION +++ /dev/null @@ -1 +0,0 @@ -2.1.0.pre diff --git a/vendor/plugins/delayed_job/benchmarks.rb b/vendor/plugins/delayed_job/benchmarks.rb index ccaa7e4..17bbf9f 100644 --- a/vendor/plugins/delayed_job/benchmarks.rb +++ b/vendor/plugins/delayed_job/benchmarks.rb @@ -1,33 +1,13 @@ -$:.unshift(File.dirname(__FILE__) + '/lib') -require 'rubygems' +require 'spec/helper' require 'logger' -require 'delayed_job' require 'benchmark' -RAILS_ENV = 'test' - -Delayed::Worker.logger = Logger.new('/dev/null') - -BACKENDS = [] -Dir.glob("#{File.dirname(__FILE__)}/spec/setup/*.rb") do |backend| - begin - backend = File.basename(backend, '.rb') - require "spec/setup/#{backend}" - BACKENDS << backend.to_sym - rescue LoadError - puts "Unable to load #{backend} backend! #{$!}" - end -end - +# Delayed::Worker.logger = Logger.new('/dev/null') Benchmark.bm(10) do |x| - BACKENDS.each do |backend| - require "spec/setup/#{backend}" - Delayed::Worker.backend = backend - - n = 10000 - n.times { "foo".delay.length } + Delayed::Job.delete_all + n = 10000 + n.times { "foo".delay.length } - x.report(backend.to_s) { Delayed::Worker.new(:quiet => true).work_off(n) } - end + x.report { Delayed::Worker.new(:quiet => true).work_off(n) } end diff --git a/vendor/plugins/delayed_job/contrib/delayed_job.monitrc b/vendor/plugins/delayed_job/contrib/delayed_job.monitrc index e9e0c44..429c26a 100644 --- a/vendor/plugins/delayed_job/contrib/delayed_job.monitrc +++ b/vendor/plugins/delayed_job/contrib/delayed_job.monitrc @@ -11,4 +11,4 @@ check process delayed_job with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.pid start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start" - stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop" \ No newline at end of file + stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop" diff --git a/vendor/plugins/delayed_job/contrib/delayed_job_multiple.monitrc b/vendor/plugins/delayed_job/contrib/delayed_job_multiple.monitrc index aca4ed2..2f63e72 100644 --- a/vendor/plugins/delayed_job/contrib/delayed_job_multiple.monitrc +++ b/vendor/plugins/delayed_job/contrib/delayed_job_multiple.monitrc @@ -3,21 +3,32 @@ # To use: # 1. copy to /var/www/apps/{app_name}/shared/delayed_job.monitrc # 2. replace {app_name} as appropriate +# you might also need to change the program strings to +# "/bin/su - {username} -c '/usr/bin/env ...'" +# to load your shell environment. +# # 3. add this to your /etc/monit/monitrc # # include /var/www/apps/{app_name}/shared/delayed_job.monitrc +# +# The processes are grouped so that monit can act on them as a whole, e.g. +# +# monit -g delayed_job restart check process delayed_job_0 with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.0.pid start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 0" stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 0" - + group delayed_job + check process delayed_job_1 with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.1.pid start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 1" stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 1" - + group delayed_job + check process delayed_job_2 with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.2.pid start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job start -i 2" - stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 2" \ No newline at end of file + stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/delayed_job stop -i 2" + group delayed_job diff --git a/vendor/plugins/delayed_job/contrib/delayed_job_rails_4.monitrc b/vendor/plugins/delayed_job/contrib/delayed_job_rails_4.monitrc new file mode 100644 index 0000000..e0b2a4d --- /dev/null +++ b/vendor/plugins/delayed_job/contrib/delayed_job_rails_4.monitrc @@ -0,0 +1,14 @@ +# an example Monit configuration file for delayed_job +# See: http://stackoverflow.com/questions/1226302/how-to-monitor-delayedjob-with-monit/1285611 +# +# To use: +# 1. copy to /var/www/apps/{app_name}/shared/delayed_job.monitrc +# 2. replace {app_name} as appropriate +# 3. add this to your /etc/monit/monitrc +# +# include /var/www/apps/{app_name}/shared/delayed_job.monitrc + +check process delayed_job + with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.pid + start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job start" + stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job stop" diff --git a/vendor/plugins/delayed_job/contrib/delayed_job_rails_4_multiple.monitrc b/vendor/plugins/delayed_job/contrib/delayed_job_rails_4_multiple.monitrc new file mode 100644 index 0000000..6db1ca6 --- /dev/null +++ b/vendor/plugins/delayed_job/contrib/delayed_job_rails_4_multiple.monitrc @@ -0,0 +1,34 @@ +# an example Monit configuration file for delayed_job running multiple processes +# +# To use: +# 1. copy to /var/www/apps/{app_name}/shared/delayed_job.monitrc +# 2. replace {app_name} as appropriate +# you might also need to change the program strings to +# "/bin/su - {username} -c '/usr/bin/env ...'" +# to load your shell environment. +# +# 3. add this to your /etc/monit/monitrc +# +# include /var/www/apps/{app_name}/shared/delayed_job.monitrc +# +# The processes are grouped so that monit can act on them as a whole, e.g. +# +# monit -g delayed_job restart + +check process delayed_job_0 + with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.0.pid + start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job start -i 0" + stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job stop -i 0" + group delayed_job + +check process delayed_job_1 + with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.1.pid + start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job start -i 1" + stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job stop -i 1" + group delayed_job + +check process delayed_job_2 + with pidfile /var/www/apps/{app_name}/shared/pids/delayed_job.2.pid + start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job start -i 2" + stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/bin/delayed_job stop -i 2" + group delayed_job diff --git a/vendor/plugins/delayed_job/delayed_job.gemspec b/vendor/plugins/delayed_job/delayed_job.gemspec index 966c43e..8faccb4 100644 --- a/vendor/plugins/delayed_job/delayed_job.gemspec +++ b/vendor/plugins/delayed_job/delayed_job.gemspec @@ -1,125 +1,17 @@ -# Generated by jeweler -# DO NOT EDIT THIS FILE DIRECTLY -# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command # -*- encoding: utf-8 -*- -Gem::Specification.new do |s| - s.name = %q{delayed_job} - s.version = "2.1.0.pre" - - s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= - s.authors = ["Brandon Keepers", "Tobias L\303\274tke"] - s.date = %q{2010-05-21} - 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. - -This gem is collectiveidea's fork (http://github.com/collectiveidea/delayed_job).} - s.email = %q{tobi@leetsoft.com} - s.extra_rdoc_files = [ - "README.textile" - ] - s.files = [ - ".gitignore", - "MIT-LICENSE", - "README.textile", - "Rakefile", - "VERSION", - "benchmarks.rb", - "contrib/delayed_job.monitrc", - "contrib/delayed_job_multiple.monitrc", - "delayed_job.gemspec", - "generators/delayed_job/delayed_job_generator.rb", - "generators/delayed_job/templates/migration.rb", - "generators/delayed_job/templates/script", - "init.rb", - "lib/delayed/backend/active_record.rb", - "lib/delayed/backend/base.rb", - "lib/delayed/backend/couch_rest.rb", - "lib/delayed/backend/data_mapper.rb", - "lib/delayed/backend/mongo_mapper.rb", - "lib/delayed/command.rb", - "lib/delayed/message_sending.rb", - "lib/delayed/performable_method.rb", - "lib/delayed/railtie.rb", - "lib/delayed/recipes.rb", - "lib/delayed/tasks.rb", - "lib/delayed/worker.rb", - "lib/delayed/yaml_ext.rb", - "lib/delayed_job.rb", - "lib/generators/delayed_job/delayed_job_generator.rb", - "lib/generators/delayed_job/templates/migration.rb", - "lib/generators/delayed_job/templates/script", - "rails/init.rb", - "recipes/delayed_job.rb", - "spec/autoloaded/clazz.rb", - "spec/autoloaded/struct.rb", - "spec/backend/active_record_job_spec.rb", - "spec/backend/couch_rest_job_spec.rb", - "spec/backend/data_mapper_job_spec.rb", - "spec/backend/mongo_mapper_job_spec.rb", - "spec/backend/shared_backend_spec.rb", - "spec/message_sending_spec.rb", - "spec/performable_method_spec.rb", - "spec/sample_jobs.rb", - "spec/setup/active_record.rb", - "spec/setup/couch_rest.rb", - "spec/setup/data_mapper.rb", - "spec/setup/mongo_mapper.rb", - "spec/spec_helper.rb", - "spec/worker_spec.rb", - "tasks/jobs.rake" - ] - s.homepage = %q{http://github.com/collectiveidea/delayed_job} - s.rdoc_options = ["--main", "README.textile", "--inline-source", "--line-numbers"] - s.require_paths = ["lib"] - s.rubygems_version = %q{1.3.6} - s.summary = %q{Database-backed asynchronous priority queue system -- Extracted from Shopify} - s.test_files = [ - "spec/message_sending_spec.rb", - "spec/performable_method_spec.rb", - "spec/worker_spec.rb" - ] - - if s.respond_to? :specification_version then - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION - s.specification_version = 3 - - if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - else - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - end - else - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - end +Gem::Specification.new do |spec| + spec.add_dependency 'activesupport', ['>= 3.0', '< 4.1'] + spec.authors = ["Brandon Keepers", "Brian Ryckbost", "Chris Gaffney", "David Genord II", "Erik Michaels-Ober", "Matt Griffin", "Steve Richert", "Tobias Lütke"] + spec.description = "Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background. It is a direct extraction from Shopify where the job table is responsible for a multitude of core tasks." + spec.email = ['brian@collectiveidea.com'] + spec.files = %w(CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md Rakefile delayed_job.gemspec) + spec.files += Dir.glob('{contrib,lib,recipes,spec}/**/*') + spec.homepage = 'http://github.com/collectiveidea/delayed_job' + spec.licenses = ['MIT'] + spec.name = 'delayed_job' + spec.require_paths = ['lib'] + spec.summary = 'Database-backed asynchronous priority queue system -- Extracted from Shopify' + spec.test_files = Dir.glob('spec/**/*') + spec.version = '4.0.0' end - diff --git a/vendor/plugins/delayed_job/generators/delayed_job/delayed_job_generator.rb b/vendor/plugins/delayed_job/generators/delayed_job/delayed_job_generator.rb deleted file mode 100644 index 5c6efd5..0000000 --- a/vendor/plugins/delayed_job/generators/delayed_job/delayed_job_generator.rb +++ /dev/null @@ -1,22 +0,0 @@ -class DelayedJobGenerator < Rails::Generator::Base - default_options :skip_migration => false - - def manifest - record do |m| - m.template 'script', 'script/delayed_job', :chmod => 0755 - if !options[:skip_migration] && defined?(ActiveRecord) - m.migration_template "migration.rb", 'db/migrate', - :migration_file_name => "create_delayed_jobs" - end - end - end - -protected - - def add_options!(opt) - opt.separator '' - opt.separator 'Options:' - opt.on("--skip-migration", "Don't generate a migration") { |v| options[:skip_migration] = v } - end - -end diff --git a/vendor/plugins/delayed_job/generators/delayed_job/templates/migration.rb b/vendor/plugins/delayed_job/generators/delayed_job/templates/migration.rb deleted file mode 100644 index ac579df..0000000 --- a/vendor/plugins/delayed_job/generators/delayed_job/templates/migration.rb +++ /dev/null @@ -1,21 +0,0 @@ -class CreateDelayedJobs < ActiveRecord::Migration - def self.up - create_table :delayed_jobs, :force => true do |table| - table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue - table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually. - table.text :handler # YAML-encoded string of the object that will do work - table.text :last_error # reason for last failure (See Note below) - table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. - table.datetime :locked_at # Set when a client is working on this object - table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) - table.string :locked_by # Who is working on this object (if locked) - table.timestamps - end - - add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority' - end - - def self.down - drop_table :delayed_jobs - end -end \ No newline at end of file diff --git a/vendor/plugins/delayed_job/init.rb b/vendor/plugins/delayed_job/init.rb deleted file mode 100644 index d84fc69..0000000 --- a/vendor/plugins/delayed_job/init.rb +++ /dev/null @@ -1,5 +0,0 @@ -require File.join(File.dirname(__FILE__), 'rails', 'init') - -config.after_initialize do - Delayed::Worker.guess_backend -end diff --git a/vendor/plugins/delayed_job/lib/delayed/backend/active_record.rb b/vendor/plugins/delayed_job/lib/delayed/backend/active_record.rb deleted file mode 100644 index 92070b5..0000000 --- a/vendor/plugins/delayed_job/lib/delayed/backend/active_record.rb +++ /dev/null @@ -1,101 +0,0 @@ -require 'active_record' -require 'active_record/version' - -class ActiveRecord::Base - yaml_as "tag:ruby.yaml.org,2002:ActiveRecord" - - def self.yaml_new(klass, tag, val) - klass.find(val['attributes']['id']) - rescue ActiveRecord::RecordNotFound - nil - end - - def to_yaml_properties - ['@attributes'] - end -end - -module Delayed - module Backend - module ActiveRecord - # A job object that is persisted to the database. - # Contains the work object as a YAML field. - class Job < ::ActiveRecord::Base - include Delayed::Backend::Base - - attr_accessible :payload_object, :priority, :run_at - - set_table_name :delayed_jobs - - before_save :set_default_run_at - - if ::ActiveRecord::VERSION::MAJOR >= 3 - scope :ready_to_run, lambda {|worker_name, max_run_time| - 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]) - } - scope :by_priority, order('priority ASC, run_at ASC') - else - scope :ready_to_run, lambda {|worker_name, max_run_time| - {: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]} - } - scope :by_priority, :order => 'priority ASC, run_at ASC' - end - - def self.after_fork - ::ActiveRecord::Base.connection.reconnect! - end - - # When a worker is exiting, make sure we don't have any locked jobs. - def self.clear_locks!(worker_name) - update_all("locked_by = null, locked_at = null", ["locked_by = ?", worker_name]) - end - - # Find a few candidate jobs to run (in case some immediately get locked by others). - def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time) - scope = self.ready_to_run(worker_name, max_run_time) - scope = scope.scoped(:conditions => ['priority >= ?', Worker.min_priority]) if Worker.min_priority - scope = scope.scoped(:conditions => ['priority <= ?', Worker.max_priority]) if Worker.max_priority - - ::ActiveRecord::Base.silence do - scope.by_priority.all(:limit => limit) - end - end - - # Lock this job for this worker. - # Returns true if we have the lock, false otherwise. - def lock_exclusively!(max_run_time, worker) - now = self.class.db_time_now - affected_rows = if locked_by != worker - # We don't own this job so we will update the locked_by name and the locked_at - 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]) - else - # We already own this job, this may happen if the job queue crashes. - # Simply resume and update the locked_at - self.class.update_all(["locked_at = ?", now], ["id = ? and locked_by = ?", id, worker]) - end - if affected_rows == 1 - self.locked_at = now - self.locked_by = worker - return true - else - return false - end - end - - # Get the current time (GMT or local depending on DB) - # Note: This does not ping the DB to get the time, so all your clients - # must have syncronized clocks. - def self.db_time_now - if Time.zone - Time.zone.now - elsif ::ActiveRecord::Base.default_timezone == :utc - Time.now.utc - else - Time.now - end - end - - end - end - end -end diff --git a/vendor/plugins/delayed_job/lib/delayed/backend/base.rb b/vendor/plugins/delayed_job/lib/delayed/backend/base.rb index 88c68a6..3a85a2b 100644 --- a/vendor/plugins/delayed_job/lib/delayed/backend/base.rb +++ b/vendor/plugins/delayed_job/lib/delayed/backend/base.rb @@ -1,82 +1,157 @@ module Delayed module Backend - class DeserializationError < StandardError - end - module Base def self.included(base) base.extend ClassMethods end - + module ClassMethods # Add a job to the queue def enqueue(*args) - object = args.shift - unless object.respond_to?(:perform) + options = { + :priority => Delayed::Worker.default_priority, + :queue => Delayed::Worker.default_queue_name + }.merge!(args.extract_options!) + + options[:payload_object] ||= args.shift + + if args.size > 0 + warn "[DEPRECATION] Passing multiple arguments to `#enqueue` is deprecated. Pass a hash with :priority and :run_at." + options[:priority] = args.first || options[:priority] + options[:run_at] = args[1] + end + + unless options[:payload_object].respond_to?(:perform) raise ArgumentError, 'Cannot enqueue items which do not respond to perform' end - - priority = args.first || Delayed::Worker.default_priority - run_at = args[1] - self.create(:payload_object => object, :priority => priority.to_i, :run_at => run_at) + + if Delayed::Worker.delay_jobs + self.new(options).tap do |job| + Delayed::Worker.lifecycle.run_callbacks(:enqueue, job) do + job.hook(:enqueue) + job.save + end + end + else + Delayed::Job.new(:payload_object => options[:payload_object]).tap do |job| + job.invoke_job + end + end end - + + def reserve(worker, max_run_time = Worker.max_run_time) + # We get up to 5 jobs from the db. In case we cannot get exclusive access to a job we try the next. + # this leads to a more even distribution of jobs across the worker processes + find_available(worker.name, worker.read_ahead, max_run_time).detect do |job| + job.lock_exclusively!(max_run_time, worker.name) + end + end + + # Allow the backend to attempt recovery from reserve errors + def recover_from(error) + end + # Hook method that is called before a new worker is forked def before_fork end - + # Hook method that is called after a new worker is forked def after_fork end - + def work_off(num = 100) warn "[DEPRECATION] `Delayed::Job.work_off` is deprecated. Use `Delayed::Worker.new.work_off instead." Delayed::Worker.new.work_off(num) end end - - ParseObjectFromYaml = /\!ruby\/\w+\:([^\s]+)/ def failed? - failed_at + !!failed_at end alias_method :failed, :failed? + ParseObjectFromYaml = /\!ruby\/\w+\:([^\s]+)/ + def name - @name ||= begin - payload = payload_object - payload.respond_to?(:display_name) ? payload.display_name : payload.class.name - end + @name ||= payload_object.respond_to?(:display_name) ? + payload_object.display_name : + payload_object.class.name + rescue DeserializationError + ParseObjectFromYaml.match(handler)[1] end def payload_object=(object) + @payload_object = object self.handler = object.to_yaml end - + def payload_object - @payload_object ||= YAML.load(self.handler) - rescue TypeError, LoadError, NameError => e - raise DeserializationError, - "Job failed to load: #{e.message}. Try to manually require the required file. Handler: #{handler.inspect}" + if YAML.respond_to?(:unsafe_load) + #See https://github.com/dtao/safe_yaml + #When the method is there, we need to load our YAML like this... + @payload_object ||= YAML.load(self.handler, :safe => false) + else + @payload_object ||= YAML.load(self.handler) + end + rescue TypeError, LoadError, NameError, ArgumentError => e + raise DeserializationError, + "Job failed to load: #{e.message}. Handler: #{handler.inspect}" end - # Moved into its own method so that new_relic can trace it. def invoke_job - payload_object.perform + Delayed::Worker.lifecycle.run_callbacks(:invoke_job, self) do + begin + hook :before + payload_object.perform + hook :success + rescue Exception => e + hook :error, e + raise e + ensure + hook :after + end + end end - + # Unlock this job (note: not saved to DB) def unlock self.locked_at = nil self.locked_by = nil end - + + def hook(name, *args) + if payload_object.respond_to?(name) + method = payload_object.method(name) + method.arity == 0 ? method.call : method.call(self, *args) + end + rescue DeserializationError + # do nothing + end + + def reschedule_at + payload_object.respond_to?(:reschedule_at) ? + payload_object.reschedule_at(self.class.db_time_now, attempts) : + self.class.db_time_now + (attempts ** 4) + 5 + end + + def max_attempts + payload_object.max_attempts if payload_object.respond_to?(:max_attempts) + end + + def fail! + update_attributes(:failed_at => self.class.db_time_now) + end + protected def set_default_run_at self.run_at ||= self.class.db_time_now end - + + # Call during reload operation to clear out internal state + def reset + @payload_object = nil + end end end end diff --git a/vendor/plugins/delayed_job/lib/delayed/backend/couch_rest.rb b/vendor/plugins/delayed_job/lib/delayed/backend/couch_rest.rb deleted file mode 100644 index 8910c4a..0000000 --- a/vendor/plugins/delayed_job/lib/delayed/backend/couch_rest.rb +++ /dev/null @@ -1,109 +0,0 @@ -require 'couchrest' - -#extent couchrest to handle delayed_job serialization. -class CouchRest::ExtendedDocument - yaml_as "tag:ruby.yaml.org,2002:CouchRest" - - def reload - job = self.class.get self['_id'] - job.each {|k,v| self[k] = v} - end - def self.find(id) - get id - end - def self.yaml_new(klass, tag, val) - klass.get(val['_id']) - end - def ==(other) - if other.is_a? ::CouchRest::ExtendedDocument - self['_id'] == other['_id'] - else - super - end - end -end - -#couchrest adapter -module Delayed - module Backend - module CouchRest - class Job < ::CouchRest::ExtendedDocument - include Delayed::Backend::Base - use_database ::CouchRest::Server.new.database('delayed_job') - - property :handler - property :last_error - property :locked_by - property :priority, :default => 0 - property :attempts, :default => 0 - property :run_at, :cast_as => 'Time' - property :locked_at, :cast_as => 'Time' - property :failed_at, :cast_as => 'Time' - timestamps! - - set_callback :save, :before, :set_default_run_at - - view_by(:failed_at, :locked_by, :run_at, - :map => "function(doc){" + - " if(doc['couchrest-type'] == 'Delayed::Backend::CouchRest::Job') {" + - " emit([doc.failed_at || null, doc.locked_by || null, doc.run_at || null], null);}" + - " }") - view_by(:failed_at, :locked_at, :run_at, - :map => "function(doc){" + - " if(doc['couchrest-type'] == 'Delayed::Backend::CouchRest::Job') {" + - " emit([doc.failed_at || null, doc.locked_at || null, doc.run_at || null], null);}" + - " }") - - def self.db_time_now; Time.now; end - def self.find_available(worker_name, limit = 5, max_run_time = ::Delayed::Worker.max_run_time) - ready = ready_jobs - mine = my_jobs worker_name - expire = expired_jobs max_run_time - jobs = (ready + mine + expire)[0..limit-1].sort_by { |j| j.priority } - jobs = jobs.find_all { |j| j.priority >= Worker.min_priority } if Worker.min_priority - jobs = jobs.find_all { |j| j.priority <= Worker.max_priority } if Worker.max_priority - jobs - end - def self.clear_locks!(worker_name) - jobs = my_jobs worker_name - jobs.each { |j| j.locked_by, j.locked_at = nil, nil; } - database.bulk_save jobs - end - def self.delete_all - database.bulk_save all.each { |doc| doc['_deleted'] = true } - end - - def lock_exclusively!(max_run_time, worker = worker_name) - return false if locked_by_other?(worker) and not expired?(max_run_time) - case - when locked_by_me?(worker) - self.locked_at = self.class.db_time_now - when (unlocked? or (locked_by_other?(worker) and expired?(max_run_time))) - self.locked_at, self.locked_by = self.class.db_time_now, worker - end - save - rescue RestClient::Conflict - false - end - - private - def self.ready_jobs - options = {:startkey => [nil, nil], :endkey => [nil, nil, db_time_now]} - by_failed_at_and_locked_by_and_run_at options - end - def self.my_jobs(worker_name) - options = {:startkey => [nil, worker_name], :endkey => [nil, worker_name, {}]} - by_failed_at_and_locked_by_and_run_at options - end - def self.expired_jobs(max_run_time) - options = {:startkey => [nil,'0'], :endkey => [nil, db_time_now - max_run_time, db_time_now]} - by_failed_at_and_locked_at_and_run_at options - end - def unlocked?; locked_by.nil?; end - def expired?(time); locked_at < self.class.db_time_now - time; end - def locked_by_me?(worker); not locked_by.nil? and locked_by == worker; end - def locked_by_other?(worker); not locked_by.nil? and locked_by != worker; end - end - end - end -end diff --git a/vendor/plugins/delayed_job/lib/delayed/backend/data_mapper.rb b/vendor/plugins/delayed_job/lib/delayed/backend/data_mapper.rb deleted file mode 100644 index 8038863..0000000 --- a/vendor/plugins/delayed_job/lib/delayed/backend/data_mapper.rb +++ /dev/null @@ -1,121 +0,0 @@ -require 'dm-core' -require 'dm-observer' -require 'dm-aggregates' - -DataMapper::Resource.class_eval do - yaml_as "tag:ruby.yaml.org,2002:DataMapper" - - def self.yaml_new(klass, tag, val) - klass.find(val['id']) - end - - def to_yaml_properties - ['@id'] - end -end - -module Delayed - module Backend - module DataMapper - class Job - include ::DataMapper::Resource - include Delayed::Backend::Base - - storage_names[:default] = 'delayed_jobs' - - property :id, Serial - property :priority, Integer, :default => 0, :index => :run_at_priority - property :attempts, Integer, :default => 0 - property :handler, Text, :lazy => false - property :run_at, Time, :index => :run_at_priority - property :locked_at, Time, :index => true - property :locked_by, String - property :failed_at, Time - property :last_error, Text - - def self.db_time_now - Time.now - end - - def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time) - - simple_conditions = { :run_at.lte => db_time_now, :limit => limit, :failed_at => nil, :order => [:priority.asc, :run_at.asc] } - - # respect priorities - simple_conditions[:priority.gte] = Worker.min_priority if Worker.min_priority - simple_conditions[:priority.lte] = Worker.max_priority if Worker.max_priority - - # lockable - lockable = ( - # not locked or past the max time - ( all(:locked_at => nil ) | all(:locked_at.lt => db_time_now - max_run_time)) | - - # OR locked by our worker - all(:locked_by => worker_name)) - - # plus some other boring junk - (lockable).all( simple_conditions ) - end - - # When a worker is exiting, make sure we don't have any locked jobs. - def self.clear_locks!(worker_name) - all(:locked_by => worker_name).update(:locked_at => nil, :locked_by => nil) - end - - # Lock this job for this worker. - # Returns true if we have the lock, false otherwise. - def lock_exclusively!(max_run_time, worker = worker_name) - - now = self.class.db_time_now - overtime = now - max_run_time - - # FIXME - this is a bit gross - # DM doesn't give us the number of rows affected by a collection update - # so we have to circumvent some niceness in DM::Collection here - collection = locked_by != worker ? - (self.class.all(:id => id, :run_at.lte => now) & ( self.class.all(:locked_at => nil) | self.class.all(:locked_at.lt => overtime) ) ) : - self.class.all(:id => id, :locked_by => worker) - - attributes = collection.model.new(:locked_at => now, :locked_by => worker).dirty_attributes - affected_rows = self.repository.update(attributes, collection) - - if affected_rows == 1 - self.locked_at = now - self.locked_by = worker - return true - else - return false - end - end - - # these are common to the other backends, so we provide an implementation - def self.delete_all - Delayed::Job.auto_migrate! - end - - def self.find id - get id - end - - def update_attributes(attributes) - attributes.each do |k,v| - self[k] = v - end - self.save - end - - - end - - class JobObserver - include ::DataMapper::Observer - - observe Job - - before :save do - self.run_at ||= self.class.db_time_now - end - end - end - end -end diff --git a/vendor/plugins/delayed_job/lib/delayed/backend/mongo_mapper.rb b/vendor/plugins/delayed_job/lib/delayed/backend/mongo_mapper.rb deleted file mode 100644 index 2c2a803..0000000 --- a/vendor/plugins/delayed_job/lib/delayed/backend/mongo_mapper.rb +++ /dev/null @@ -1,106 +0,0 @@ -require 'mongo_mapper' - -MongoMapper::Document.class_eval do - yaml_as "tag:ruby.yaml.org,2002:MongoMapper" - - def self.yaml_new(klass, tag, val) - klass.find(val['_id']) - end - - def to_yaml_properties - ['@_id'] - end -end - -module Delayed - module Backend - module MongoMapper - class Job - include ::MongoMapper::Document - include Delayed::Backend::Base - set_collection_name 'delayed_jobs' - - key :priority, Integer, :default => 0 - key :attempts, Integer, :default => 0 - key :handler, String - key :run_at, Time - key :locked_at, Time - key :locked_by, String, :index => true - key :failed_at, Time - key :last_error, String - timestamps! - - before_save :set_default_run_at - - ensure_index [[:priority, 1], [:run_at, 1]] - - def self.before_fork - ::MongoMapper.connection.close - end - - def self.after_fork - ::MongoMapper.connect(RAILS_ENV) - end - - def self.db_time_now - Time.now.utc - end - - def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time) - right_now = db_time_now - - conditions = { - :run_at => {"$lte" => right_now}, - :limit => -limit, # In mongo, positive limits are 'soft' and negative are 'hard' - :failed_at => nil, - :sort => [['priority', 1], ['run_at', 1]] - } - - where = "this.locked_at == null || this.locked_at < #{make_date(right_now - max_run_time)}" - - (conditions[:priority] ||= {})['$gte'] = Worker.min_priority.to_i if Worker.min_priority - (conditions[:priority] ||= {})['$lte'] = Worker.max_priority.to_i if Worker.max_priority - - results = all(conditions.merge(:locked_by => worker_name)) - results += all(conditions.merge('$where' => where)) if results.size < limit - results - end - - # When a worker is exiting, make sure we don't have any locked jobs. - def self.clear_locks!(worker_name) - collection.update({:locked_by => worker_name}, {"$set" => {:locked_at => nil, :locked_by => nil}}, :multi => true) - end - - # Lock this job for this worker. - # Returns true if we have the lock, false otherwise. - def lock_exclusively!(max_run_time, worker = worker_name) - right_now = self.class.db_time_now - overtime = right_now - max_run_time.to_i - - query = "this.locked_at == null || this.locked_at < #{make_date(overtime)} || this.locked_by == #{worker.to_json}" - conditions = {:_id => id, :run_at => {"$lte" => right_now}, "$where" => query} - - collection.update(conditions, {"$set" => {:locked_at => right_now, :locked_by => worker}}) - affected_rows = collection.find({:_id => id, :locked_by => worker}).count - if affected_rows == 1 - self.locked_at = right_now - self.locked_by = worker - return true - else - return false - end - end - - private - - def self.make_date(date_or_seconds) - "new Date(#{date_or_seconds.to_f * 1000})" - end - - def make_date(date) - self.class.make_date(date) - end - end - end - end -end diff --git a/vendor/plugins/delayed_job/lib/delayed/backend/shared_spec.rb b/vendor/plugins/delayed_job/lib/delayed/backend/shared_spec.rb new file mode 100644 index 0000000..1cd847b --- /dev/null +++ b/vendor/plugins/delayed_job/lib/delayed/backend/shared_spec.rb @@ -0,0 +1,594 @@ +require File.expand_path('../../../../spec/sample_jobs', __FILE__) + +require 'active_support/core_ext' + +shared_examples_for "a delayed_job backend" do + let(:worker) { Delayed::Worker.new } + + def create_job(opts = {}) + described_class.create(opts.merge(:payload_object => SimpleJob.new)) + end + + before do + Delayed::Worker.max_priority = nil + Delayed::Worker.min_priority = nil + Delayed::Worker.default_priority = 99 + Delayed::Worker.delay_jobs = true + SimpleJob.runs = 0 + described_class.delete_all + end + + after do + Delayed::Worker.reset + end + + it "sets run_at automatically if not set" do + expect(described_class.create(:payload_object => ErrorJob.new ).run_at).not_to be_nil + end + + it "does not set run_at automatically if already set" do + later = described_class.db_time_now + 5.minutes + job = described_class.create(:payload_object => ErrorJob.new, :run_at => later) + expect(job.run_at).to be_within(1).of(later) + end + + describe "#reload" do + it "reloads the payload" do + job = described_class.enqueue :payload_object => SimpleJob.new + expect(job.payload_object.object_id).not_to eq(job.reload.payload_object.object_id) + end + end + + describe "enqueue" do + context "with a hash" do + it "raises ArgumentError when handler doesn't respond_to :perform" do + expect{described_class.enqueue(:payload_object => Object.new)}.to raise_error(ArgumentError) + end + + it "is able to set priority" do + job = described_class.enqueue :payload_object => SimpleJob.new, :priority => 5 + expect(job.priority).to eq(5) + end + + it "uses default priority" do + job = described_class.enqueue :payload_object => SimpleJob.new + expect(job.priority).to eq(99) + end + + it "is able to set run_at" do + later = described_class.db_time_now + 5.minutes + job = described_class.enqueue :payload_object => SimpleJob.new, :run_at => later + expect(job.run_at).to be_within(1).of(later) + end + + it "is able to set queue" do + job = described_class.enqueue :payload_object => SimpleJob.new, :queue => 'tracking' + expect(job.queue).to eq('tracking') + end + end + + context "with multiple arguments" do + it "raises ArgumentError when handler doesn't respond_to :perform" do + expect{described_class.enqueue(Object.new)}.to raise_error(ArgumentError) + end + + it "increases count after enqueuing items" do + described_class.enqueue SimpleJob.new + expect(described_class.count).to eq(1) + end + + it "is able to set priority [DEPRECATED]" do + silence_warnings do + job = described_class.enqueue SimpleJob.new, 5 + expect(job.priority).to eq(5) + end + end + + it "uses default priority when it is not set" do + @job = described_class.enqueue SimpleJob.new + expect(@job.priority).to eq(99) + end + + it "is able to set run_at [DEPRECATED]" do + silence_warnings do + later = described_class.db_time_now + 5.minutes + @job = described_class.enqueue SimpleJob.new, 5, later + expect(@job.run_at).to be_within(1).of(later) + end + end + + it "works with jobs in modules" do + M::ModuleJob.runs = 0 + job = described_class.enqueue M::ModuleJob.new + expect{job.invoke_job}.to change { M::ModuleJob.runs }.from(0).to(1) + end + end + + context "with delay_jobs = false" do + before(:each) do + Delayed::Worker.delay_jobs = false + end + + it "does not increase count after enqueuing items" do + described_class.enqueue SimpleJob.new + expect(described_class.count).to eq(0) + end + + it "invokes the enqueued job" do + job = SimpleJob.new + job.should_receive(:perform) + described_class.enqueue job + end + + it "returns a job, not the result of invocation" do + expect(described_class.enqueue(SimpleJob.new)).to be_instance_of(described_class) + end + end + end + + describe "callbacks" do + before(:each) do + CallbackJob.messages = [] + end + + %w(before success after).each do |callback| + it "calls #{callback} with job" do + job = described_class.enqueue(CallbackJob.new) + job.payload_object.should_receive(callback).with(job) + job.invoke_job + end + end + + it "calls before and after callbacks" do + job = described_class.enqueue(CallbackJob.new) + expect(CallbackJob.messages).to eq(["enqueue"]) + job.invoke_job + expect(CallbackJob.messages).to eq(["enqueue", "before", "perform", "success", "after"]) + end + + it "calls the after callback with an error" do + job = described_class.enqueue(CallbackJob.new) + job.payload_object.should_receive(:perform).and_raise(RuntimeError.new("fail")) + + expect{job.invoke_job}.to raise_error + expect(CallbackJob.messages).to eq(["enqueue", "before", "error: RuntimeError", "after"]) + end + + it "calls error when before raises an error" do + job = described_class.enqueue(CallbackJob.new) + job.payload_object.should_receive(:before).and_raise(RuntimeError.new("fail")) + expect{job.invoke_job}.to raise_error(RuntimeError) + expect(CallbackJob.messages).to eq(["enqueue", "error: RuntimeError", "after"]) + end + end + + describe "payload_object" do + it "raises a DeserializationError when the job class is totally unknown" do + job = described_class.new :handler => "--- !ruby/object:JobThatDoesNotExist {}" + expect{job.payload_object}.to raise_error(Delayed::DeserializationError) + end + + it "raises a DeserializationError when the job struct is totally unknown" do + job = described_class.new :handler => "--- !ruby/struct:StructThatDoesNotExist {}" + expect{job.payload_object}.to raise_error(Delayed::DeserializationError) + end + + it "raises a DeserializationError when the YAML.load raises argument error" do + job = described_class.new :handler => "--- !ruby/struct:GoingToRaiseArgError {}" + YAML.should_receive(:load).and_raise(ArgumentError) + expect{job.payload_object}.to raise_error(Delayed::DeserializationError) + end + end + + describe "reserve" do + before do + Delayed::Worker.max_run_time = 2.minutes + end + + after do + Time.zone = nil + end + + it "does not reserve failed jobs" do + create_job :attempts => 50, :failed_at => described_class.db_time_now + expect(described_class.reserve(worker)).to be_nil + end + + it "does not reserve jobs scheduled for the future" do + create_job :run_at => described_class.db_time_now + 1.minute + expect(described_class.reserve(worker)).to be_nil + end + + it "reserves jobs scheduled for the past" do + job = create_job :run_at => described_class.db_time_now - 1.minute + expect(described_class.reserve(worker)).to eq(job) + end + + it "reserves jobs scheduled for the past when time zones are involved" do + Time.zone = 'US/Eastern' + job = create_job :run_at => described_class.db_time_now - 1.minute + expect(described_class.reserve(worker)).to eq(job) + end + + it "does not reserve jobs locked by other workers" do + job = create_job + other_worker = Delayed::Worker.new + other_worker.name = 'other_worker' + expect(described_class.reserve(other_worker)).to eq(job) + expect(described_class.reserve(worker)).to be_nil + end + + it "reserves open jobs" do + job = create_job + expect(described_class.reserve(worker)).to eq(job) + end + + it "reserves expired jobs" do + job = create_job(:locked_by => 'some other worker', :locked_at => described_class.db_time_now - Delayed::Worker.max_run_time - 1.minute) + expect(described_class.reserve(worker)).to eq(job) + end + + it "reserves own jobs" do + job = create_job(:locked_by => worker.name, :locked_at => (described_class.db_time_now - 1.minutes)) + expect(described_class.reserve(worker)).to eq(job) + end + end + + context "#name" do + it "is the class name of the job that was enqueued" do + expect(described_class.create(:payload_object => ErrorJob.new ).name).to eq('ErrorJob') + end + + it "is the method that will be called if its a performable method object" do + job = described_class.new(:payload_object => NamedJob.new) + expect(job.name).to eq('named_job') + end + + it "is the instance method that will be called if its a performable method object" do + job = Story.create(:text => "...").delay.save + expect(job.name).to eq('Story#save') + end + + it "parses from handler on deserialization error" do + job = Story.create(:text => "...").delay.text + job.payload_object.object.destroy + expect(job.reload.name).to eq('Delayed::PerformableMethod') + end + end + + context "worker prioritization" do + after do + Delayed::Worker.max_priority = nil + Delayed::Worker.min_priority = nil + end + + it "fetches jobs ordered by priority" do + 10.times { described_class.enqueue SimpleJob.new, :priority => rand(10) } + jobs = [] + 10.times { jobs << described_class.reserve(worker) } + expect(jobs.size).to eq(10) + jobs.each_cons(2) do |a, b| + expect(a.priority).to be <= b.priority + end + end + + it "only finds jobs greater than or equal to min priority" do + min = 5 + Delayed::Worker.min_priority = min + [4,5,6].sort_by {|i| rand }.each {|i| create_job :priority => i } + 2.times do + job = described_class.reserve(worker) + expect(job.priority).to be >= min + job.destroy + end + expect(described_class.reserve(worker)).to be_nil + end + + it "only finds jobs less than or equal to max priority" do + max = 5 + Delayed::Worker.max_priority = max + [4,5,6].sort_by {|i| rand }.each {|i| create_job :priority => i } + 2.times do + job = described_class.reserve(worker) + expect(job.priority).to be <= max + job.destroy + end + expect(described_class.reserve(worker)).to be_nil + end + end + + context "clear_locks!" do + before do + @job = create_job(:locked_by => 'worker1', :locked_at => described_class.db_time_now) + end + + it "clears locks for the given worker" do + described_class.clear_locks!('worker1') + expect(described_class.reserve(worker)).to eq(@job) + end + + it "does not clear locks for other workers" do + described_class.clear_locks!('different_worker') + expect(described_class.reserve(worker)).not_to eq(@job) + end + end + + context "unlock" do + before do + @job = create_job(:locked_by => 'worker', :locked_at => described_class.db_time_now) + end + + it "clears locks" do + @job.unlock + expect(@job.locked_by).to be_nil + expect(@job.locked_at).to be_nil + end + end + + context "large handler" do + before do + text = "Lorem ipsum dolor sit amet. " * 1000 + @job = described_class.enqueue Delayed::PerformableMethod.new(text, :length, {}) + end + + it "has an id" do + expect(@job.id).not_to be_nil + end + end + + context "named queues" do + context "when worker has one queue set" do + before(:each) do + worker.queues = ['large'] + end + + it "only works off jobs which are from its queue" do + expect(SimpleJob.runs).to eq(0) + + create_job(:queue => "large") + create_job(:queue => "small") + worker.work_off + + expect(SimpleJob.runs).to eq(1) + end + end + + context "when worker has two queue set" do + before(:each) do + worker.queues = ['large', 'small'] + end + + it "only works off jobs which are from its queue" do + expect(SimpleJob.runs).to eq(0) + + create_job(:queue => "large") + create_job(:queue => "small") + create_job(:queue => "medium") + create_job + worker.work_off + + expect(SimpleJob.runs).to eq(2) + end + end + + context "when worker does not have queue set" do + before(:each) do + worker.queues = [] + end + + it "works off all jobs" do + expect(SimpleJob.runs).to eq(0) + + create_job(:queue => "one") + create_job(:queue => "two") + create_job + worker.work_off + + expect(SimpleJob.runs).to eq(3) + end + end + end + + context "max_attempts" do + before(:each) do + @job = described_class.enqueue SimpleJob.new + end + + it "is not defined" do + expect(@job.max_attempts).to be_nil + end + + it "uses the max_retries value on the payload when defined" do + @job.payload_object.stub(:max_attempts).and_return(99) + expect(@job.max_attempts).to eq(99) + end + end + + describe "yaml serialization" do + it "reloads changed attributes" do + story = Story.create(:text => 'hello') + job = story.delay.tell + story.update_attributes :text => 'goodbye' + expect(job.reload.payload_object.object.text).to eq('goodbye') + end + + it "raises error ArgumentError the record is not persisted" do + story = Story.new(:text => 'hello') + if story.respond_to?(:new_record?) + expect { + story.delay.tell + }.to raise_error(ArgumentError, "Jobs cannot be created for records before they've been persisted") + end + end + + it "raises deserialization error for destroyed records" do + story = Story.create(:text => 'hello') + job = story.delay.tell + story.destroy + expect { + job.reload.payload_object + }.to raise_error(Delayed::DeserializationError) + end + end + + describe "worker integration" do + before do + Delayed::Job.delete_all + SimpleJob.runs = 0 + end + + describe "running a job" do + it "fails after Worker.max_run_time" do + Delayed::Worker.max_run_time = 1.second + job = Delayed::Job.create :payload_object => LongRunningJob.new + worker.run(job) + expect(job.reload.last_error).to match(/expired/) + expect(job.reload.last_error).to match(/Delayed::Worker.max_run_time is only 1 second/) + expect(job.attempts).to eq(1) + end + + context "when the job raises a deserialization error" do + after do + Delayed::Worker.destroy_failed_jobs = true + end + + it "marks the job as failed" do + Delayed::Worker.destroy_failed_jobs = false + job = described_class.create! :handler => "--- !ruby/object:JobThatDoesNotExist {}" + worker.work_off + job.reload + expect(job).to be_failed + end + end + end + + describe "failed jobs" do + before do + @job = Delayed::Job.enqueue(ErrorJob.new, :run_at => described_class.db_time_now - 1) + end + + after do + # reset default + Delayed::Worker.destroy_failed_jobs = true + end + + it "records last_error when destroy_failed_jobs = false, max_attempts = 1" do + Delayed::Worker.destroy_failed_jobs = false + Delayed::Worker.max_attempts = 1 + worker.run(@job) + @job.reload + expect(@job.last_error).to match(/did not work/) + expect(@job.attempts).to eq(1) + expect(@job).to be_failed + end + + it "re-schedules jobs after failing" do + worker.work_off + @job.reload + expect(@job.last_error).to match(/did not work/) + expect(@job.last_error).to match(/sample_jobs.rb:\d+:in `perform'/) + expect(@job.attempts).to eq(1) + expect(@job.run_at).to be > Delayed::Job.db_time_now - 10.minutes + expect(@job.run_at).to be < Delayed::Job.db_time_now + 10.minutes + expect(@job.locked_by).to be_nil + expect(@job.locked_at).to be_nil + end + + it "re-schedules jobs with handler provided time if present" do + job = Delayed::Job.enqueue(CustomRescheduleJob.new(99.minutes)) + worker.run(job) + job.reload + + expect((Delayed::Job.db_time_now + 99.minutes - job.run_at).abs).to be < 1 + end + + it "does not fail when the triggered error doesn't have a message" do + error_with_nil_message = StandardError.new + error_with_nil_message.stub(:message).and_return nil + @job.stub(:invoke_job).and_raise error_with_nil_message + expect{worker.run(@job)}.not_to raise_error + end + end + + context "reschedule" do + before do + @job = Delayed::Job.create :payload_object => SimpleJob.new + end + + share_examples_for "any failure more than Worker.max_attempts times" do + context "when the job's payload has a #failure hook" do + before do + @job = Delayed::Job.create :payload_object => OnPermanentFailureJob.new + expect(@job.payload_object).to respond_to :failure + end + + it "runs that hook" do + @job.payload_object.should_receive :failure + worker.reschedule(@job) + end + end + + context "when the job's payload has no #failure hook" do + # It's a little tricky to test this in a straightforward way, + # because putting a should_not_receive expectation on + # @job.payload_object.failure makes that object + # incorrectly return true to + # payload_object.respond_to? :failure, which is what + # reschedule uses to decide whether to call failure. + # So instead, we just make sure that the payload_object as it + # already stands doesn't respond_to? failure, then + # shove it through the iterated reschedule loop and make sure we + # don't get a NoMethodError (caused by calling that nonexistent + # failure method). + + before do + expect(@job.payload_object).not_to respond_to(:failure) + end + + it "does not try to run that hook" do + expect { + Delayed::Worker.max_attempts.times { worker.reschedule(@job) } + }.not_to raise_exception + end + end + end + + context "and we want to destroy jobs" do + it_should_behave_like "any failure more than Worker.max_attempts times" + + it "is destroyed if it failed more than Worker.max_attempts times" do + @job.should_receive(:destroy) + Delayed::Worker.max_attempts.times { worker.reschedule(@job) } + end + + it "is not destroyed if failed fewer than Worker.max_attempts times" do + @job.should_not_receive(:destroy) + (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) } + end + end + + context "and we don't want to destroy jobs" do + before do + Delayed::Worker.destroy_failed_jobs = false + end + + after do + Delayed::Worker.destroy_failed_jobs = true + end + + it_should_behave_like "any failure more than Worker.max_attempts times" + + it "is failed if it failed more than Worker.max_attempts times" do + expect(@job.reload).not_to be_failed + Delayed::Worker.max_attempts.times { worker.reschedule(@job) } + expect(@job.reload).to be_failed + end + + it "is not failed if it failed fewer than Worker.max_attempts times" do + (Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) } + expect(@job.reload).not_to be_failed + end + end + end + end +end diff --git a/vendor/plugins/delayed_job/lib/delayed/command.rb b/vendor/plugins/delayed_job/lib/delayed/command.rb index b844ba7..ba7e466 100644 --- a/vendor/plugins/delayed_job/lib/delayed/command.rb +++ b/vendor/plugins/delayed_job/lib/delayed/command.rb @@ -1,21 +1,23 @@ -require 'rubygems' -require 'daemons' +begin + require 'daemons' +rescue LoadError + raise "You need to add gem 'daemons' to your Gemfile if you wish to use it." +end require 'optparse' module Delayed class Command attr_accessor :worker_count - + def initialize(args) - @files_to_reopen = [] @options = { :quiet => true, :pid_dir => "#{Rails.root}/tmp/pids" } - + @worker_count = 1 @monitor = false - + opts = OptionParser.new do |opts| opts.banner = "Usage: #{File.basename($0)} [options] start|stop|restart|run" @@ -44,22 +46,32 @@ module Delayed opts.on('-m', '--monitor', 'Start monitor process.') do @monitor = true end - - + opts.on('--sleep-delay N', "Amount of time to sleep when no jobs are found") do |n| + @options[:sleep_delay] = n.to_i + end + opts.on('--read-ahead N', "Number of jobs from the queue to consider") do |n| + @options[:read_ahead] = n + end + opts.on('-p', '--prefix NAME', "String to be prefixed to worker process names") do |prefix| + @options[:prefix] = prefix + end + opts.on('--queues=queues', "Specify which queue DJ must look up for jobs") do |queues| + @options[:queues] = queues.split(',') + end + opts.on('--queue=queue', "Specify which queue DJ must look up for jobs") do |queue| + @options[:queues] = queue.split(',') + end + 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 + @options[:exit_on_complete] = true + end end @args = opts.parse!(args) end - - def daemonize - Delayed::Worker.backend.before_fork - ObjectSpace.each_object(File) do |file| - @files_to_reopen << file unless file.closed? - end - + def daemonize dir = @options[:pid_dir] Dir.mkdir(dir) unless File.exists?(dir) - + if @worker_count > 1 && @options[:identifier] raise ArgumentError, 'Cannot specify both --number-of-workers and --identifier' elsif @worker_count == 1 && @options[:identifier] @@ -72,28 +84,21 @@ module Delayed end end end - + def run_process(process_name, dir) + Delayed::Worker.before_fork Daemons.run_proc(process_name, :dir => dir, :dir_mode => :normal, :monitor => @monitor, :ARGV => @args) do |*args| + $0 = File.join(@options[:prefix], process_name) if @options[:prefix] run process_name end end - + def run(worker_name = nil) Dir.chdir(Rails.root) - - # Re-open file handles - @files_to_reopen.each do |file| - begin - file.reopen file.path, "a+" - file.sync = true - rescue ::Exception - end - end - - Delayed::Worker.logger = Logger.new(File.join(Rails.root, 'log', 'delayed_job.log')) - Delayed::Worker.backend.after_fork - + + Delayed::Worker.after_fork + Delayed::Worker.logger ||= Logger.new(File.join(Rails.root, 'log', 'delayed_job.log')) + worker = Delayed::Worker.new(@options) worker.name_prefix = "#{worker_name} " worker.start @@ -102,6 +107,5 @@ module Delayed STDERR.puts e.message exit 1 end - end end diff --git a/vendor/plugins/delayed_job/lib/delayed/compatibility.rb b/vendor/plugins/delayed_job/lib/delayed/compatibility.rb new file mode 100644 index 0000000..72778f9 --- /dev/null +++ b/vendor/plugins/delayed_job/lib/delayed/compatibility.rb @@ -0,0 +1,27 @@ +require 'active_support/version' + +module Delayed + module Compatibility + if ActiveSupport::VERSION::MAJOR >= 4 + require 'active_support/proxy_object' + + def self.executable_prefix + 'bin' + end + + def self.proxy_object_class + ActiveSupport::ProxyObject + end + else + require 'active_support/basic_object' + + def self.executable_prefix + 'script' + end + + def self.proxy_object_class + ActiveSupport::BasicObject + end + end + end +end diff --git a/vendor/plugins/delayed_job/lib/delayed/deserialization_error.rb b/vendor/plugins/delayed_job/lib/delayed/deserialization_error.rb new file mode 100644 index 0000000..960d8f5 --- /dev/null +++ b/vendor/plugins/delayed_job/lib/delayed/deserialization_error.rb @@ -0,0 +1,4 @@ +module Delayed + class DeserializationError < StandardError + end +end diff --git a/vendor/plugins/delayed_job/lib/delayed/exceptions.rb b/vendor/plugins/delayed_job/lib/delayed/exceptions.rb new file mode 100644 index 0000000..e3fbf6e --- /dev/null +++ b/vendor/plugins/delayed_job/lib/delayed/exceptions.rb @@ -0,0 +1,9 @@ +require 'timeout' + +module Delayed + class WorkerTimeout < Timeout::Error + def message + "#{super} (Delayed::Worker.max_run_time is only #{Delayed::Worker.max_run_time.to_i} seconds)" + end + end +end diff --git a/vendor/plugins/delayed_job/lib/delayed/lifecycle.rb b/vendor/plugins/delayed_job/lib/delayed/lifecycle.rb new file mode 100644 index 0000000..507ba11 --- /dev/null +++ b/vendor/plugins/delayed_job/lib/delayed/lifecycle.rb @@ -0,0 +1,84 @@ +module Delayed + class InvalidCallback < Exception; end + + class Lifecycle + EVENTS = { + :enqueue => [:job], + :execute => [:worker], + :loop => [:worker], + :perform => [:worker, :job], + :error => [:worker, :job], + :failure => [:worker, :job], + :invoke_job => [:job] + } + + def initialize + @callbacks = EVENTS.keys.inject({}) { |hash, e| hash[e] = Callback.new; hash } + end + + def before(event, &block) + add(:before, event, &block) + end + + def after(event, &block) + add(:after, event, &block) + end + + def around(event, &block) + add(:around, event, &block) + end + + def run_callbacks(event, *args, &block) + missing_callback(event) unless @callbacks.has_key?(event) + + unless EVENTS[event].size == args.size + raise ArgumentError, "Callback #{event} expects #{EVENTS[event].size} parameter(s): #{EVENTS[event].join(', ')}" + end + + @callbacks[event].execute(*args, &block) + end + + private + + def add(type, event, &block) + missing_callback(event) unless @callbacks.has_key?(event) + + @callbacks[event].add(type, &block) + end + + def missing_callback(event) + raise InvalidCallback, "Unknown callback event: #{event}" + end + end + + class Callback + def initialize + @before = [] + @after = [] + + # Identity proc. Avoids special cases when there is no existing around chain. + @around = lambda { |*args, &block| block.call(*args) } + end + + def execute(*args, &block) + @before.each { |c| c.call(*args) } + result = @around.call(*args, &block) + @after.each { |c| c.call(*args) } + result + end + + def add(type, &callback) + case type + when :before + @before << callback + when :after + @after << callback + when :around + chain = @around # use a local variable so that the current chain is closed over in the following lambda + @around = lambda { |*a, &block| chain.call(*a) { |*b| callback.call(*b, &block) } } + else + raise InvalidCallback, "Invalid callback type: #{type}" + end + end + end +end diff --git a/vendor/plugins/delayed_job/lib/delayed/message_sending.rb b/vendor/plugins/delayed_job/lib/delayed/message_sending.rb index c9ec6cb..e8af95a 100644 --- a/vendor/plugins/delayed_job/lib/delayed/message_sending.rb +++ b/vendor/plugins/delayed_job/lib/delayed/message_sending.rb @@ -1,30 +1,24 @@ -require 'active_support/basic_object' +require 'active_support/core_ext/module/aliasing' module Delayed - class DelayProxy < ActiveSupport::BasicObject - def initialize(target, options) + class DelayProxy < Delayed::Compatibility.proxy_object_class + def initialize(payload_class, target, options) + @payload_class = payload_class @target = target @options = options end def method_missing(method, *args) - if (Rails.env == "test" or Rails.env == "cucumber" and !$DISABLE_DELAYED_JOB_TEST_ENV_RUN) - @target.send method, *args - return - end - Job.create({ - :payload_object => PerformableMethod.new(@target, method.to_sym, args), - :priority => ::Delayed::Worker.default_priority - }.merge(@options)) + Job.enqueue({:payload_object => @payload_class.new(@target, method.to_sym, args)}.merge(@options)) end end module MessageSending def delay(options = {}) - DelayProxy.new(self, options) + DelayProxy.new(PerformableMethod, self, options) end alias __delay__ delay - + def send_later(method, *args) warn "[DEPRECATION] `object.send_later(:method)` is deprecated. Use `object.delay.method" __delay__.__send__(method, *args) @@ -34,17 +28,26 @@ module Delayed warn "[DEPRECATION] `object.send_at(time, :method)` is deprecated. Use `object.delay(:run_at => time).method" __delay__(:run_at => time).__send__(method, *args) end - + module ClassMethods - def handle_asynchronously(method) - return if (Rails.env == "test" or Rails.env == "cucumber") + def handle_asynchronously(method, opts = {}) aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1 with_method, without_method = "#{aliased_method}_with_delay#{punctuation}", "#{aliased_method}_without_delay#{punctuation}" define_method(with_method) do |*args| - delay.__send__(without_method, *args) + curr_opts = opts.clone + curr_opts.each_key do |key| + if (val = curr_opts[key]).is_a?(Proc) + curr_opts[key] = if val.arity == 1 + val.call(self) + else + val.call + end + end + end + delay(curr_opts).__send__(without_method, *args) end alias_method_chain method, :delay end end - end + end end diff --git a/vendor/plugins/delayed_job/lib/delayed/performable_mailer.rb b/vendor/plugins/delayed_job/lib/delayed/performable_mailer.rb new file mode 100644 index 0000000..f3a898e --- /dev/null +++ b/vendor/plugins/delayed_job/lib/delayed/performable_mailer.rb @@ -0,0 +1,21 @@ +require 'mail' + +module Delayed + class PerformableMailer < PerformableMethod + def perform + object.send(method_name, *args).deliver + end + end + + module DelayMail + def delay(options = {}) + DelayProxy.new(PerformableMailer, self, options) + end + end +end + +Mail::Message.class_eval do + def delay(*args) + raise RuntimeError, "Use MyMailer.delay.mailer_action(args) to delay sending of emails." + end +end diff --git a/vendor/plugins/delayed_job/lib/delayed/performable_method.rb b/vendor/plugins/delayed_job/lib/delayed/performable_method.rb index bc59089..40aaf68 100644 --- a/vendor/plugins/delayed_job/lib/delayed/performable_method.rb +++ b/vendor/plugins/delayed_job/lib/delayed/performable_method.rb @@ -1,27 +1,37 @@ +require 'active_support/core_ext/module/delegation' + module Delayed - class PerformableMethod < Struct.new(:object, :method, :args) - def initialize(object, method, args) - raise NoMethodError, "undefined method `#{method}' for #{object.inspect}" unless object.respond_to?(method, true) + class PerformableMethod + attr_accessor :object, :method_name, :args + + delegate :method, :to => :object + + def initialize(object, method_name, args) + raise NoMethodError, "undefined method `#{method_name}' for #{object.inspect}" unless object.respond_to?(method_name, true) - self.object = object - self.args = args - self.method = method.to_sym + if object.respond_to?(:new_record?) && object.new_record? + raise(ArgumentError, 'Jobs cannot be created for records before they\'ve been persisted') + end + + self.object = object + self.args = args + self.method_name = method_name.to_sym end - + def display_name - "#{object.class}##{method}" + "#{object.class}##{method_name}" end - + def perform - object.send(method, *args) if object + object.send(method_name, *args) if object end - + def method_missing(symbol, *args) - object.respond_to?(symbol) ? object.send(symbol, *args) : super + object.send(symbol, *args) end - + def respond_to?(symbol, include_private=false) - object.respond_to?(symbol, include_private) || super - end + super || object.respond_to?(symbol, include_private) + end end end diff --git a/vendor/plugins/delayed_job/lib/delayed/plugin.rb b/vendor/plugins/delayed_job/lib/delayed/plugin.rb new file mode 100644 index 0000000..18f2c62 --- /dev/null +++ b/vendor/plugins/delayed_job/lib/delayed/plugin.rb @@ -0,0 +1,15 @@ +require 'active_support/core_ext/class/attribute' + +module Delayed + class Plugin + class_attribute :callback_block + + def self.callbacks(&block) + self.callback_block = block + end + + def initialize + self.class.callback_block.call(Delayed::Worker.lifecycle) if self.class.callback_block + end + end +end diff --git a/vendor/plugins/delayed_job/lib/delayed/plugins/clear_locks.rb b/vendor/plugins/delayed_job/lib/delayed/plugins/clear_locks.rb new file mode 100644 index 0000000..05224a0 --- /dev/null +++ b/vendor/plugins/delayed_job/lib/delayed/plugins/clear_locks.rb @@ -0,0 +1,15 @@ +module Delayed + module Plugins + class ClearLocks < Plugin + callbacks do |lifecycle| + lifecycle.around(:execute) do |worker, &block| + begin + block.call(worker) + ensure + Delayed::Job.clear_locks!(worker.name) + end + end + end + end + end +end diff --git a/vendor/plugins/delayed_job/lib/delayed/psych_ext.rb b/vendor/plugins/delayed_job/lib/delayed/psych_ext.rb new file mode 100644 index 0000000..827a685 --- /dev/null +++ b/vendor/plugins/delayed_job/lib/delayed/psych_ext.rb @@ -0,0 +1,146 @@ +if defined?(ActiveRecord) + ActiveRecord::Base.class_eval do + if instance_methods.include?(:encode_with) + def encode_with_override(coder) + encode_with_without_override(coder) + coder.tag = "!ruby/ActiveRecord:#{self.class.name}" + end + alias_method :encode_with_without_override, :encode_with + alias_method :encode_with, :encode_with_override + else + def encode_with(coder) + coder["attributes"] = attributes + coder.tag = "!ruby/ActiveRecord:#{self.class.name}" + end + end + end +end + +class Delayed::PerformableMethod + # serialize to YAML + def encode_with(coder) + coder.map = { + "object" => object, + "method_name" => method_name, + "args" => args + } + end +end + +module Psych + module Visitors + class YAMLTree + def visit_Class(klass) + @emitter.scalar klass.name, nil, '!ruby/class', false, false, Nodes::Scalar::SINGLE_QUOTED + end + end + + class ToRuby + def visit_Psych_Nodes_Scalar(o) + @st[o.anchor] = o.value if o.anchor + + if klass = Psych.load_tags[o.tag] + instance = klass.allocate + + if instance.respond_to?(:init_with) + coder = Psych::Coder.new(o.tag) + coder.scalar = o.value + instance.init_with coder + end + + return instance + end + + return o.value if o.quoted + return @ss.tokenize(o.value) unless o.tag + + case o.tag + when '!binary', 'tag:yaml.org,2002:binary' + o.value.unpack('m').first + when '!str', 'tag:yaml.org,2002:str' + o.value + when "!ruby/object:DateTime" + require 'date' + @ss.parse_time(o.value).to_datetime + when "!ruby/object:Complex" + Complex(o.value) + when "!ruby/object:Rational" + Rational(o.value) + when "!ruby/class", "!ruby/module" + resolve_class o.value + when "tag:yaml.org,2002:float", "!float" + Float(@ss.tokenize(o.value)) + when "!ruby/regexp" + o.value =~ /^\/(.*)\/([mixn]*)$/ + source = $1 + options = 0 + lang = nil + ($2 || '').split('').each do |option| + case option + when 'x' then options |= Regexp::EXTENDED + when 'i' then options |= Regexp::IGNORECASE + when 'm' then options |= Regexp::MULTILINE + when 'n' then options |= Regexp::NOENCODING + else lang = option + end + end + Regexp.new(*[source, options, lang].compact) + when "!ruby/range" + args = o.value.split(/([.]{2,3})/, 2).map { |s| + accept Nodes::Scalar.new(s) + } + args.push(args.delete_at(1) == '...') + Range.new(*args) + when /^!ruby\/sym(bol)?:?(.*)?$/ + o.value.to_sym + else + @ss.tokenize o.value + end + end + + def visit_Psych_Nodes_Mapping_with_class(object) + return revive(Psych.load_tags[object.tag], object) if Psych.load_tags[object.tag] + + case object.tag + when /^!ruby\/ActiveRecord:(.+)$/ + klass = resolve_class($1) + payload = Hash[*object.children.map { |c| accept c }] + id = payload["attributes"][klass.primary_key] + begin + klass.unscoped.find(id) + rescue ActiveRecord::RecordNotFound + raise Delayed::DeserializationError + end + when /^!ruby\/Mongoid:(.+)$/ + klass = resolve_class($1) + payload = Hash[*object.children.map { |c| accept c }] + begin + klass.find(payload["attributes"]["_id"]) + rescue Mongoid::Errors::DocumentNotFound + raise Delayed::DeserializationError + end + when /^!ruby\/DataMapper:(.+)$/ + klass = resolve_class($1) + payload = Hash[*object.children.map { |c| accept c }] + begin + primary_keys = klass.properties.select { |p| p.key? } + key_names = primary_keys.map { |p| p.name.to_s } + klass.get!(*key_names.map { |k| payload["attributes"][k] }) + rescue DataMapper::ObjectNotFoundError + raise Delayed::DeserializationError + end + else + visit_Psych_Nodes_Mapping_without_class(object) + end + end + alias_method_chain :visit_Psych_Nodes_Mapping, :class + + def resolve_class_with_constantize(klass_name) + klass_name.constantize + rescue + resolve_class_without_constantize(klass_name) + end + alias_method_chain :resolve_class, :constantize + end + end +end diff --git a/vendor/plugins/delayed_job/lib/delayed/railtie.rb b/vendor/plugins/delayed_job/lib/delayed/railtie.rb index c4161cd..19567c1 100644 --- a/vendor/plugins/delayed_job/lib/delayed/railtie.rb +++ b/vendor/plugins/delayed_job/lib/delayed/railtie.rb @@ -4,7 +4,9 @@ require 'rails' module Delayed class Railtie < Rails::Railtie initializer :after_initialize do - Delayed::Worker.guess_backend + ActiveSupport.on_load(:action_mailer) do + ActionMailer::Base.send(:extend, Delayed::DelayMail) + end end rake_tasks do diff --git a/vendor/plugins/delayed_job/lib/delayed/recipes.rb b/vendor/plugins/delayed_job/lib/delayed/recipes.rb index c710bb6..18f8a16 100644 --- a/vendor/plugins/delayed_job/lib/delayed/recipes.rb +++ b/vendor/plugins/delayed_job/lib/delayed/recipes.rb @@ -6,26 +6,49 @@ # after "deploy:stop", "delayed_job:stop" # after "deploy:start", "delayed_job:start" # after "deploy:restart", "delayed_job:restart" +# +# If you want to use command line options, for example to start multiple workers, +# define a Capistrano variable delayed_job_args: +# +# set :delayed_job_args, "-n 2" +# +# If you've got delayed_job workers running on a servers, you can also specify +# which servers have delayed_job running and should be restarted after deploy. +# +# set :delayed_job_server_role, :worker +# Capistrano::Configuration.instance.load do namespace :delayed_job do def rails_env fetch(:rails_env, false) ? "RAILS_ENV=#{fetch(:rails_env)}" : '' end - + + def args + fetch(:delayed_job_args, "") + end + + def roles + fetch(:delayed_job_server_role, :app) + end + + def delayed_job_command + fetch(:delayed_job_command, "script/delayed_job") + end + desc "Stop the delayed_job process" - task :stop, :roles => :app do - run "cd #{current_path};#{rails_env} script/delayed_job stop" + task :stop, :roles => lambda { roles } do + run "cd #{current_path};#{rails_env} #{delayed_job_command} stop" end desc "Start the delayed_job process" - task :start, :roles => :app do - run "cd #{current_path};#{rails_env} script/delayed_job start" + task :start, :roles => lambda { roles } do + run "cd #{current_path};#{rails_env} #{delayed_job_command} start #{args}" end desc "Restart the delayed_job process" - task :restart, :roles => :app do - run "cd #{current_path};#{rails_env} script/delayed_job restart" + task :restart, :roles => lambda { roles } do + run "cd #{current_path};#{rails_env} #{delayed_job_command} restart #{args}" end end -end \ No newline at end of file +end diff --git a/vendor/plugins/delayed_job/lib/delayed/serialization/active_record.rb b/vendor/plugins/delayed_job/lib/delayed/serialization/active_record.rb new file mode 100644 index 0000000..e9d0d6b --- /dev/null +++ b/vendor/plugins/delayed_job/lib/delayed/serialization/active_record.rb @@ -0,0 +1,15 @@ +if defined?(ActiveRecord) + class ActiveRecord::Base + yaml_as "tag:ruby.yaml.org,2002:ActiveRecord" + + def self.yaml_new(klass, tag, val) + klass.unscoped.find(val['attributes'][klass.primary_key]) + rescue ActiveRecord::RecordNotFound + raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass} , primary key: #{val['attributes'][klass.primary_key]} " + end + + def to_yaml_properties + ['@attributes'] + end + end +end diff --git a/vendor/plugins/delayed_job/lib/delayed/syck_ext.rb b/vendor/plugins/delayed_job/lib/delayed/syck_ext.rb new file mode 100644 index 0000000..54e529f --- /dev/null +++ b/vendor/plugins/delayed_job/lib/delayed/syck_ext.rb @@ -0,0 +1,34 @@ +class Module + yaml_as "tag:ruby.yaml.org,2002:module" + + def self.yaml_new(klass, tag, val) + val.constantize + end + + def to_yaml(options = {}) + YAML.quick_emit(nil, options) do |out| + out.scalar(taguri, name, :plain) + end + end + + def yaml_tag_read_class(name) + # Constantize the object so that ActiveSupport can attempt + # its auto loading magic. Will raise LoadError if not successful. + name.constantize + name + end +end + +class Class + yaml_as "tag:ruby.yaml.org,2002:class" + remove_method :to_yaml if respond_to?(:to_yaml) && method(:to_yaml).owner == Class # use Module's to_yaml +end + +class Struct + def self.yaml_tag_read_class(name) + # Constantize the object so that ActiveSupport can attempt + # its auto loading magic. Will raise LoadError if not successful. + name.constantize + "Struct::#{ name }" + end +end diff --git a/vendor/plugins/delayed_job/lib/delayed/tasks.rb b/vendor/plugins/delayed_job/lib/delayed/tasks.rb index b62fbd8..ce2d075 100644 --- a/vendor/plugins/delayed_job/lib/delayed/tasks.rb +++ b/vendor/plugins/delayed_job/lib/delayed/tasks.rb @@ -1,15 +1,38 @@ -# Re-definitions are appended to existing tasks -task :environment -task :merb_env - namespace :jobs do desc "Clear the delayed_job queue." - task :clear => [:merb_env, :environment] do + task :clear => :environment do Delayed::Job.delete_all end desc "Start a delayed_job worker." - task :work => [:merb_env, :environment] do - Delayed::Worker.new(:min_priority => ENV['MIN_PRIORITY'], :max_priority => ENV['MAX_PRIORITY']).start + task :work => :environment_options do + Delayed::Worker.new(@worker_options).start + end + + desc "Start a delayed_job worker and exit when all available jobs are complete." + task :workoff => :environment_options do + Delayed::Worker.new(@worker_options.merge({:exit_on_complete => true})).start end + + task :environment_options => :environment do + @worker_options = { + :min_priority => ENV['MIN_PRIORITY'], + :max_priority => ENV['MAX_PRIORITY'], + :queues => (ENV['QUEUES'] || ENV['QUEUE'] || '').split(','), + :quiet => false + } + end + + desc "Exit with error status if any jobs older than max_age seconds haven't been attempted yet." + task :check, [:max_age] => :environment do |_, args| + args.with_defaults(:max_age => 300) + + unprocessed_jobs = Delayed::Job.where('attempts = 0 AND created_at < ?', Time.now - args[:max_age].to_i).count + + if unprocessed_jobs > 0 + fail "#{unprocessed_jobs} jobs older than #{args[:max_age]} seconds have not been processed yet" + end + + end + end diff --git a/vendor/plugins/delayed_job/lib/delayed/worker.rb b/vendor/plugins/delayed_job/lib/delayed/worker.rb index aeb11c7..fbc365f 100644 --- a/vendor/plugins/delayed_job/lib/delayed/worker.rb +++ b/vendor/plugins/delayed_job/lib/delayed/worker.rb @@ -2,59 +2,122 @@ require 'timeout' require 'active_support/core_ext/numeric/time' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/kernel' +require 'active_support/core_ext/enumerable' +require 'logger' +require 'benchmark' module Delayed class Worker - cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time, :default_priority, :sleep_delay, :logger - self.sleep_delay = 5 - self.max_attempts = 25 - self.max_run_time = 4.hours - self.default_priority = 0 - + DEFAULT_LOG_LEVEL = Logger::INFO + DEFAULT_SLEEP_DELAY = 5 + DEFAULT_MAX_ATTEMPTS = 25 + DEFAULT_MAX_RUN_TIME = 4.hours + DEFAULT_DEFAULT_PRIORITY = 0 + DEFAULT_DELAY_JOBS = true + DEFAULT_QUEUES = [] + DEFAULT_READ_AHEAD = 5 + + cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time, + :default_priority, :sleep_delay, :logger, :delay_jobs, :queues, + :read_ahead, :plugins, :destroy_failed_jobs, :exit_on_complete + + # Named queue into which jobs are enqueued by default + cattr_accessor :default_queue_name + + cattr_reader :backend + + # name_prefix is ignored if name is set directly + attr_accessor :name_prefix + + def self.reset + self.sleep_delay = DEFAULT_SLEEP_DELAY + self.max_attempts = DEFAULT_MAX_ATTEMPTS + self.max_run_time = DEFAULT_MAX_RUN_TIME + self.default_priority = DEFAULT_DEFAULT_PRIORITY + self.delay_jobs = DEFAULT_DELAY_JOBS + self.queues = DEFAULT_QUEUES + self.read_ahead = DEFAULT_READ_AHEAD + end + + reset + + # Add or remove plugins in this list before the worker is instantiated + self.plugins = [Delayed::Plugins::ClearLocks] + # By default failed jobs are destroyed after too many attempts. If you want to keep them around # (perhaps to inspect the reason for the failure), set this to false. - cattr_accessor :destroy_failed_jobs self.destroy_failed_jobs = true - - self.logger = if defined?(Merb::Logger) - Merb.logger + + # By default, Signals INT and TERM set @exit, and the worker exits upon completion of the current job. + # If you would prefer to raise a SignalException and exit immediately you can use this. + # Be aware daemons uses TERM to stop and restart + # false - No exceptions will be raised + # :term - Will only raise an exception on TERM signals but INT will wait for the current job to finish + # true - Will raise an exception on TERM and INT + cattr_accessor :raise_signal_exceptions + self.raise_signal_exceptions = false + + self.logger = if defined?(Rails) + Rails.logger elsif defined?(RAILS_DEFAULT_LOGGER) RAILS_DEFAULT_LOGGER end - # name_prefix is ignored if name is set directly - attr_accessor :name_prefix - - cattr_reader :backend - def self.backend=(backend) if backend.is_a? Symbol + require "delayed/serialization/#{backend}" require "delayed/backend/#{backend}" backend = "Delayed::Backend::#{backend.to_s.classify}::Job".constantize end @@backend = backend silence_warnings { ::Delayed.const_set(:Job, backend) } end - + def self.guess_backend - self.backend ||= if defined?(ActiveRecord) - :active_record - elsif defined?(MongoMapper) - :mongo_mapper - else - logger.warn "Could not decide on a backend, defaulting to active_record" - :active_record + warn "[DEPRECATION] guess_backend is deprecated. Please remove it from your code." + end + + def self.before_fork + unless @files_to_reopen + @files_to_reopen = [] + ObjectSpace.each_object(File) do |file| + @files_to_reopen << file unless file.closed? + end end + + backend.before_fork + end + + def self.after_fork + # Re-open file handles + @files_to_reopen.each do |file| + begin + file.reopen file.path, "a+" + file.sync = true + rescue ::Exception + end + end + + backend.after_fork + end + + def self.lifecycle + @lifecycle ||= Delayed::Lifecycle.new end def initialize(options={}) - @quiet = options[:quiet] - self.class.min_priority = options[:min_priority] if options.has_key?(:min_priority) - self.class.max_priority = options[:max_priority] if options.has_key?(:max_priority) + @quiet = options.has_key?(:quiet) ? options[:quiet] : true + @failed_reserve_count = 0 + + [:min_priority, :max_priority, :sleep_delay, :read_ahead, :queues, :exit_on_complete].each do |option| + self.class.send("#{option}=", options[option]) if options.has_key?(option) + end + + self.plugins.each { |klass| klass.new } end # Every worker has a unique name which by default is the pid of the process. There are some - # advantages to overriding this with something which survives worker retarts: Workers can# + # advantages to overriding this with something which survives worker restarts: Workers can # safely resume working on tasks which are locked by themselves. The worker will assume that # it crashed before. def name @@ -69,35 +132,54 @@ module Delayed end def start - say "Starting job worker" + trap('TERM') do + say 'Exiting...' + stop + raise SignalException.new('TERM') if self.class.raise_signal_exceptions + end - trap('TERM') { say 'Exiting...'; $exit = true } - trap('INT') { say 'Exiting...'; $exit = true } + trap('INT') do + say 'Exiting...' + stop + raise SignalException.new('INT') if self.class.raise_signal_exceptions && self.class.raise_signal_exceptions != :term + end - loop do - result = nil + say "Starting job worker" - realtime = Benchmark.realtime do - result = work_off - end + self.class.lifecycle.run_callbacks(:execute, self) do + loop do + self.class.lifecycle.run_callbacks(:loop, self) do + @realtime = Benchmark.realtime do + @result = work_off + end + end - count = result.sum + count = @result.sum - break if $exit + if count.zero? + if self.class.exit_on_complete + say "No more jobs available. Exiting" + break + else + sleep(self.class.sleep_delay) unless stop? + end + else + say "#{count} jobs processed at %.4f j/s, %d failed" % [count / @realtime, @result.last] + end - if count.zero? - sleep(@@sleep_delay) - else - say "#{count} jobs processed at %.4f j/s, %d failed ..." % [count / realtime, result.last] + break if stop? end - - break if $exit end + end + + def stop + @exit = true + end - ensure - Delayed::Job.clear_locks!(name) + def stop? + !!@exit end - + # Do num jobs and return stats on success/failure. # Exit early if interrupted. def work_off(num = 100) @@ -112,80 +194,90 @@ module Delayed else break # leave if no work could be done end - break if $exit # leave if we're exiting + break if stop? # leave if we're exiting end return [success, failure] end - + def run(job) + job_say job, 'RUNNING' runtime = Benchmark.realtime do - Timeout.timeout(self.class.max_run_time.to_i) { job.invoke_job } + Timeout.timeout(self.class.max_run_time.to_i, WorkerTimeout) { job.invoke_job } job.destroy end - say "#{job.name} completed after %.4f" % runtime + job_say job, 'COMPLETED after %.4f' % runtime return true # did work - rescue Exception => e - handle_failed_job(job, e) + rescue DeserializationError => error + job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}" + failed(job) + rescue Exception => error + self.class.lifecycle.run_callbacks(:error, self, job){ handle_failed_job(job, error) } return false # work failed end - + # Reschedule the job in the future (when a job fails). # Uses an exponential scale depending on the number of failed attempts. def reschedule(job, time = nil) - if (job.attempts += 1) < self.class.max_attempts - time ||= Job.db_time_now + (job.attempts ** 4) + 5 + if (job.attempts += 1) < max_attempts(job) + time ||= job.reschedule_at job.run_at = time job.unlock job.save! else - say "PERMANENTLY removing #{job.name} because of #{job.attempts} consecutive failures.", Logger::INFO - - if job.payload_object.respond_to? :on_permanent_failure - say "Running on_permanent_failure hook" - failure_method = job.payload_object.method(:on_permanent_failure) - if failure_method.arity == 1 - failure_method.call(job) - else - failure_method.call - end - end + job_say job, "REMOVED permanently because of #{job.attempts} consecutive failures", Logger::ERROR + failed(job) + end + end - self.class.destroy_failed_jobs ? job.destroy : job.update_attributes(:failed_at => Delayed::Job.db_time_now) + def failed(job) + self.class.lifecycle.run_callbacks(:failure, self, job) do + job.hook(:failure) + self.class.destroy_failed_jobs ? job.destroy : job.fail! end end - def say(text, level = Logger::INFO) + def job_say(job, text, level = DEFAULT_LOG_LEVEL) + text = "Job #{job.name} (id=#{job.id}) #{text}" + say text, level + end + + def say(text, level = DEFAULT_LOG_LEVEL) text = "[Worker(#{name})] #{text}" puts text unless @quiet logger.add level, "#{Time.now.strftime('%FT%T%z')}: #{text}" if logger end + def max_attempts(job) + job.max_attempts || self.class.max_attempts + end + protected - + def handle_failed_job(job, error) - job.last_error = error.message + "\n" + error.backtrace.join("\n") - say "#{job.name} failed with #{error.class.name}: #{error.message} - #{job.attempts} failed attempts", Logger::ERROR + job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}" + job_say job, "FAILED (#{job.attempts} prior attempts) with #{error.class.name}: #{error.message}", Logger::ERROR reschedule(job) end - + # Run the next job we can get an exclusive lock on. # If no jobs are left we return nil def reserve_and_run_one_job + job = reserve_job + self.class.lifecycle.run_callbacks(:perform, self, job){ run(job) } if job + end - # We get up to 5 jobs from the db. In case we cannot get exclusive access to a job we try the next. - # this leads to a more even distribution of jobs across the worker processes - job = Delayed::Job.find_available(name, 5, self.class.max_run_time).detect do |job| - if job.lock_exclusively!(self.class.max_run_time, name) - say "acquired lock on #{job.name}" - true - else - say "failed to acquire exclusive lock for #{job.name}", Logger::WARN - false - end - end - - run(job) if job + def reserve_job + job = Delayed::Job.reserve(self) + @failed_reserve_count = 0 + job + rescue Exception => error + say "Error while reserving job: #{error}" + Delayed::Job.recover_from(error) + @failed_reserve_count += 1 + raise FatalBackendError if @failed_reserve_count >= 10 + nil end end + end diff --git a/vendor/plugins/delayed_job/lib/delayed/yaml_ext.rb b/vendor/plugins/delayed_job/lib/delayed/yaml_ext.rb index 2e937b3..4ef8272 100644 --- a/vendor/plugins/delayed_job/lib/delayed/yaml_ext.rb +++ b/vendor/plugins/delayed_job/lib/delayed/yaml_ext.rb @@ -2,34 +2,9 @@ # Classes, Modules and Structs require 'yaml' - -class Module - yaml_as "tag:ruby.yaml.org,2002:module" - - def self.yaml_new(klass, tag, val) - val.constantize - end - - def to_yaml( opts = {} ) - YAML::quick_emit( nil, opts ) { |out| - out.scalar(taguri, self.name, :plain) - } - end - - def yaml_tag_read_class(name) - # Constantize the object so that ActiveSupport can attempt - # its auto loading magic. Will raise LoadError if not successful. - name.constantize - name - end - -end - -class Struct - def self.yaml_tag_read_class(name) - # Constantize the object so that ActiveSupport can attempt - # its auto loading magic. Will raise LoadError if not successful. - name.constantize - "Struct::#{ name }" - end +if YAML.parser.class.name =~ /syck|yecht/i + require File.expand_path('../syck_ext', __FILE__) + require File.expand_path('../serialization/active_record', __FILE__) +else + require File.expand_path('../psych_ext', __FILE__) end diff --git a/vendor/plugins/delayed_job/lib/delayed_job.rb b/vendor/plugins/delayed_job/lib/delayed_job.rb index 4f3966e..cada396 100644 --- a/vendor/plugins/delayed_job/lib/delayed_job.rb +++ b/vendor/plugins/delayed_job/lib/delayed_job.rb @@ -1,15 +1,22 @@ require 'active_support' +require 'delayed/compatibility' +require 'delayed/exceptions' +require 'delayed/message_sending' +require 'delayed/performable_method' -require File.dirname(__FILE__) + '/delayed/message_sending' -require File.dirname(__FILE__) + '/delayed/performable_method' -require File.dirname(__FILE__) + '/delayed/yaml_ext' -require File.dirname(__FILE__) + '/delayed/backend/base' -require File.dirname(__FILE__) + '/delayed/worker' -require File.dirname(__FILE__) + '/delayed/railtie' if defined?(::Rails::Railtie) +if defined?(ActionMailer) + require 'action_mailer/version' + require 'delayed/performable_mailer' +end -Object.send(:include, Delayed::MessageSending) -Module.send(:include, Delayed::MessageSending::ClassMethods) +require 'delayed/yaml_ext' +require 'delayed/lifecycle' +require 'delayed/plugin' +require 'delayed/plugins/clear_locks' +require 'delayed/backend/base' +require 'delayed/worker' +require 'delayed/deserialization_error' +require 'delayed/railtie' if defined?(Rails::Railtie) -if defined?(Merb::Plugins) - Merb::Plugins.add_rakefiles File.dirname(__FILE__) / 'delayed' / 'tasks' -end +Object.send(:include, Delayed::MessageSending) +Module.send(:include, Delayed::MessageSending::ClassMethods) diff --git a/vendor/plugins/delayed_job/lib/generators/delayed_job/delayed_job_generator.rb b/vendor/plugins/delayed_job/lib/generators/delayed_job/delayed_job_generator.rb index 83fe6a2..94157e3 100644 --- a/vendor/plugins/delayed_job/lib/generators/delayed_job/delayed_job_generator.rb +++ b/vendor/plugins/delayed_job/lib/generators/delayed_job/delayed_job_generator.rb @@ -1,34 +1,12 @@ require 'rails/generators' -require 'rails/generators/migration' +require 'delayed/compatibility' class DelayedJobGenerator < Rails::Generators::Base - include Rails::Generators::Migration - - def self.source_root - @source_root ||= File.join(File.dirname(__FILE__), 'templates') - end + self.source_paths << File.join(File.dirname(__FILE__), 'templates') - # Implement the required interface for Rails::Generators::Migration. - # - def self.next_migration_number(dirname) #:nodoc: - next_migration_number = current_migration_number(dirname) + 1 - if ActiveRecord::Base.timestamped_migrations - [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max - else - "%.3d" % next_migration_number - end - end - - def create_script_file - template 'script', 'script/delayed_job' - chmod 'script/delayed_job', 0755 + def create_executable_file + template "script", "#{Delayed::Compatibility.executable_prefix}/delayed_job" + chmod "#{Delayed::Compatibility.executable_prefix}/delayed_job", 0755 end - - def create_migration_file - if defined?(ActiveRecord) - migration_template 'migration.rb', 'db/migrate/create_delayed_jobs.rb' - end - end - -end \ No newline at end of file +end diff --git a/vendor/plugins/delayed_job/lib/generators/delayed_job/templates/migration.rb b/vendor/plugins/delayed_job/lib/generators/delayed_job/templates/migration.rb deleted file mode 100644 index ac579df..0000000 --- a/vendor/plugins/delayed_job/lib/generators/delayed_job/templates/migration.rb +++ /dev/null @@ -1,21 +0,0 @@ -class CreateDelayedJobs < ActiveRecord::Migration - def self.up - create_table :delayed_jobs, :force => true do |table| - table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue - table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually. - table.text :handler # YAML-encoded string of the object that will do work - table.text :last_error # reason for last failure (See Note below) - table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. - table.datetime :locked_at # Set when a client is working on this object - table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) - table.string :locked_by # Who is working on this object (if locked) - table.timestamps - end - - add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority' - end - - def self.down - drop_table :delayed_jobs - end -end \ No newline at end of file diff --git a/vendor/plugins/delayed_job/lib/generators/delayed_job/templates/script b/vendor/plugins/delayed_job/lib/generators/delayed_job/templates/script new file mode 100644 index 0000000..edf1959 --- /dev/null +++ b/vendor/plugins/delayed_job/lib/generators/delayed_job/templates/script @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) +require 'delayed/command' +Delayed::Command.new(ARGV).daemonize diff --git a/vendor/plugins/delayed_job/rails/init.rb b/vendor/plugins/delayed_job/rails/init.rb deleted file mode 100644 index 8252f9a..0000000 --- a/vendor/plugins/delayed_job/rails/init.rb +++ /dev/null @@ -1 +0,0 @@ -require 'delayed_job' diff --git a/vendor/plugins/delayed_job/spec/autoloaded/clazz.rb b/vendor/plugins/delayed_job/spec/autoloaded/clazz.rb index 4fd8542..5c5756d 100644 --- a/vendor/plugins/delayed_job/spec/autoloaded/clazz.rb +++ b/vendor/plugins/delayed_job/spec/autoloaded/clazz.rb @@ -4,4 +4,4 @@ module Autoloaded def perform end end -end \ No newline at end of file +end diff --git a/vendor/plugins/delayed_job/spec/autoloaded/instance_clazz.rb b/vendor/plugins/delayed_job/spec/autoloaded/instance_clazz.rb new file mode 100644 index 0000000..c6e4d4a --- /dev/null +++ b/vendor/plugins/delayed_job/spec/autoloaded/instance_clazz.rb @@ -0,0 +1,6 @@ +module Autoloaded + class InstanceClazz + def perform + end + end +end diff --git a/vendor/plugins/delayed_job/spec/autoloaded/instance_struct.rb b/vendor/plugins/delayed_job/spec/autoloaded/instance_struct.rb new file mode 100644 index 0000000..3b8a8ff --- /dev/null +++ b/vendor/plugins/delayed_job/spec/autoloaded/instance_struct.rb @@ -0,0 +1,6 @@ +module Autoloaded + class InstanceStruct < ::Struct.new(nil) + def perform + end + end +end diff --git a/vendor/plugins/delayed_job/spec/autoloaded/struct.rb b/vendor/plugins/delayed_job/spec/autoloaded/struct.rb index 7610121..3fbaeb4 100644 --- a/vendor/plugins/delayed_job/spec/autoloaded/struct.rb +++ b/vendor/plugins/delayed_job/spec/autoloaded/struct.rb @@ -4,4 +4,4 @@ module Autoloaded def perform end end -end \ No newline at end of file +end diff --git a/vendor/plugins/delayed_job/spec/backend/active_record_job_spec.rb b/vendor/plugins/delayed_job/spec/backend/active_record_job_spec.rb deleted file mode 100644 index 209805a..0000000 --- a/vendor/plugins/delayed_job/spec/backend/active_record_job_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'spec_helper' -require 'backend/shared_backend_spec' -require 'delayed/backend/active_record' - -describe Delayed::Backend::ActiveRecord::Job do - before(:all) do - @backend = Delayed::Backend::ActiveRecord::Job - end - - before(:each) do - Delayed::Backend::ActiveRecord::Job.delete_all - SimpleJob.runs = 0 - end - - after do - Time.zone = nil - end - - it_should_behave_like 'a backend' - - context "db_time_now" do - it "should return time in current time zone if set" do - Time.zone = 'Eastern Time (US & Canada)' - %w(EST EDT).should include(Delayed::Job.db_time_now.zone) - end - - it "should return UTC time if that is the AR default" do - Time.zone = nil - ActiveRecord::Base.default_timezone = :utc - Delayed::Backend::ActiveRecord::Job.db_time_now.zone.should == 'UTC' - end - - it "should return local time if that is the AR default" do - Time.zone = 'Central Time (US & Canada)' - ActiveRecord::Base.default_timezone = :local - %w(CST CDT).should include(Delayed::Backend::ActiveRecord::Job.db_time_now.zone) - end - end - - describe "after_fork" do - it "should call reconnect on the connection" do - ActiveRecord::Base.connection.should_receive(:reconnect!) - Delayed::Backend::ActiveRecord::Job.after_fork - end - end -end diff --git a/vendor/plugins/delayed_job/spec/backend/couch_rest_job_spec.rb b/vendor/plugins/delayed_job/spec/backend/couch_rest_job_spec.rb deleted file mode 100644 index d0e40d9..0000000 --- a/vendor/plugins/delayed_job/spec/backend/couch_rest_job_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' -require 'backend/shared_backend_spec' -require 'delayed/backend/couch_rest' - -describe Delayed::Backend::CouchRest::Job do - before(:all) do - @backend = Delayed::Backend::CouchRest::Job - end - - before(:each) do - @backend.delete_all - end - - it_should_behave_like 'a backend' -end diff --git a/vendor/plugins/delayed_job/spec/backend/data_mapper_job_spec.rb b/vendor/plugins/delayed_job/spec/backend/data_mapper_job_spec.rb deleted file mode 100644 index c3ab493..0000000 --- a/vendor/plugins/delayed_job/spec/backend/data_mapper_job_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'spec_helper' -require 'backend/shared_backend_spec' -require 'delayed/backend/data_mapper' - -describe Delayed::Backend::DataMapper::Job do - before(:all) do - @backend = Delayed::Backend::DataMapper::Job - end - - before(:each) do - # reset database before each example is run - DataMapper.auto_migrate! - end - - it_should_behave_like 'a backend' -end diff --git a/vendor/plugins/delayed_job/spec/backend/mongo_mapper_job_spec.rb b/vendor/plugins/delayed_job/spec/backend/mongo_mapper_job_spec.rb deleted file mode 100644 index 676f256..0000000 --- a/vendor/plugins/delayed_job/spec/backend/mongo_mapper_job_spec.rb +++ /dev/null @@ -1,94 +0,0 @@ -require 'spec_helper' -require 'backend/shared_backend_spec' -require 'delayed/backend/mongo_mapper' - -describe Delayed::Backend::MongoMapper::Job do - before(:all) do - @backend = Delayed::Backend::MongoMapper::Job - end - - before(:each) do - MongoMapper.database.collections.each(&:remove) - end - - it_should_behave_like 'a backend' - - describe "indexes" do - it "should have combo index on priority and run_at" do - @backend.collection.index_information.detect { |index| index[0] == 'priority_1_run_at_1' }.should_not be_nil - end - - it "should have index on locked_by" do - @backend.collection.index_information.detect { |index| index[0] == 'locked_by_1' }.should_not be_nil - end - end - - describe "delayed method" do - class MongoStoryReader - def read(story) - "Epilog: #{story.tell}" - end - end - - class MongoStory - include ::MongoMapper::Document - key :text, String - - def tell - text - end - end - - it "should ignore not found errors because they are permanent" do - story = MongoStory.create :text => 'Once upon a time...' - job = story.delay.tell - story.destroy - lambda { job.invoke_job }.should_not raise_error - end - - it "should store the object as string" do - story = MongoStory.create :text => 'Once upon a time...' - job = story.delay.tell - - job.payload_object.class.should == Delayed::PerformableMethod - job.payload_object.object.should == story - job.payload_object.method.should == :tell - job.payload_object.args.should == [] - job.payload_object.perform.should == 'Once upon a time...' - end - - it "should store arguments as string" do - story = MongoStory.create :text => 'Once upon a time...' - job = MongoStoryReader.new.delay.read(story) - job.payload_object.class.should == Delayed::PerformableMethod - job.payload_object.method.should == :read - job.payload_object.args.should == [story] - job.payload_object.perform.should == 'Epilog: Once upon a time...' - end - end - - describe "before_fork" do - after do - MongoMapper.connection.connect_to_master - end - - it "should disconnect" do - lambda do - Delayed::Backend::MongoMapper::Job.before_fork - end.should change { !!MongoMapper.connection.connected? }.from(true).to(false) - end - end - - describe "after_fork" do - before do - MongoMapper.connection.close - end - - it "should call reconnect" do - lambda do - Delayed::Backend::MongoMapper::Job.after_fork - end.should change { !!MongoMapper.connection.connected? }.from(false).to(true) - end - end - -end diff --git a/vendor/plugins/delayed_job/spec/backend/shared_backend_spec.rb b/vendor/plugins/delayed_job/spec/backend/shared_backend_spec.rb deleted file mode 100644 index d037ce5..0000000 --- a/vendor/plugins/delayed_job/spec/backend/shared_backend_spec.rb +++ /dev/null @@ -1,279 +0,0 @@ -class NamedJob < Struct.new(:perform) - def display_name - 'named_job' - end -end - -shared_examples_for 'a backend' do - def create_job(opts = {}) - @backend.create(opts.merge(:payload_object => SimpleJob.new)) - end - - before do - Delayed::Worker.max_priority = nil - Delayed::Worker.min_priority = nil - Delayed::Worker.default_priority = 99 - SimpleJob.runs = 0 - end - - it "should set run_at automatically if not set" do - @backend.create(:payload_object => ErrorJob.new ).run_at.should_not be_nil - end - - it "should not set run_at automatically if already set" do - later = @backend.db_time_now + 5.minutes - @backend.create(:payload_object => ErrorJob.new, :run_at => later).run_at.should be_close(later, 1) - end - - it "should raise ArgumentError when handler doesn't respond_to :perform" do - lambda { @backend.enqueue(Object.new) }.should raise_error(ArgumentError) - end - - it "should increase count after enqueuing items" do - @backend.enqueue SimpleJob.new - @backend.count.should == 1 - end - - it "should be able to set priority when enqueuing items" do - @job = @backend.enqueue SimpleJob.new, 5 - @job.priority.should == 5 - end - - it "should use default priority when it is not set" do - @job = @backend.enqueue SimpleJob.new - @job.priority.should == 99 - end - - it "should be able to set run_at when enqueuing items" do - later = @backend.db_time_now + 5.minutes - @job = @backend.enqueue SimpleJob.new, 5, later - @job.run_at.should be_close(later, 1) - end - - it "should work with jobs in modules" do - M::ModuleJob.runs = 0 - job = @backend.enqueue M::ModuleJob.new - lambda { job.invoke_job }.should change { M::ModuleJob.runs }.from(0).to(1) - end - - describe "payload_object" do - it "should raise a DeserializationError when the job class is totally unknown" do - job = @backend.new :handler => "--- !ruby/object:JobThatDoesNotExist {}" - lambda { job.payload_object }.should raise_error(Delayed::Backend::DeserializationError) - end - - it "should raise a DeserializationError when the job struct is totally unknown" do - job = @backend.new :handler => "--- !ruby/struct:StructThatDoesNotExist {}" - lambda { job.payload_object }.should raise_error(Delayed::Backend::DeserializationError) - end - - it "should autoload classes that are unknown at runtime" do - job = @backend.new :handler => "--- !ruby/object:Autoloaded::Clazz {}" - lambda { job.payload_object }.should_not raise_error(Delayed::Backend::DeserializationError) - end - - it "should autoload structs that are unknown at runtime" do - job = @backend.new :handler => "--- !ruby/struct:Autoloaded::Struct {}" - lambda { job.payload_object }.should_not raise_error(Delayed::Backend::DeserializationError) - end - end - - describe "find_available" do - it "should not find failed jobs" do - @job = create_job :attempts => 50, :failed_at => @backend.db_time_now - @backend.find_available('worker', 5, 1.second).should_not include(@job) - end - - it "should not find jobs scheduled for the future" do - @job = create_job :run_at => (@backend.db_time_now + 1.minute) - @backend.find_available('worker', 5, 4.hours).should_not include(@job) - end - - it "should not find jobs locked by another worker" do - @job = create_job(:locked_by => 'other_worker', :locked_at => @backend.db_time_now - 1.minute) - @backend.find_available('worker', 5, 4.hours).should_not include(@job) - end - - it "should find open jobs" do - @job = create_job - @backend.find_available('worker', 5, 4.hours).should include(@job) - end - - it "should find expired jobs" do - @job = create_job(:locked_by => 'worker', :locked_at => @backend.db_time_now - 2.minutes) - @backend.find_available('worker', 5, 1.minute).should include(@job) - end - - it "should find own jobs" do - @job = create_job(:locked_by => 'worker', :locked_at => (@backend.db_time_now - 1.minutes)) - @backend.find_available('worker', 5, 4.hours).should include(@job) - end - - it "should find only the right amount of jobs" do - 10.times { create_job } - @backend.find_available('worker', 7, 4.hours).should have(7).jobs - end - end - - context "when another worker is already performing an task, it" do - - before :each do - @job = @backend.create :payload_object => SimpleJob.new, :locked_by => 'worker1', :locked_at => @backend.db_time_now - 5.minutes - end - - it "should not allow a second worker to get exclusive access" do - @job.lock_exclusively!(4.hours, 'worker2').should == false - end - - it "should allow a second worker to get exclusive access if the timeout has passed" do - @job.lock_exclusively!(1.minute, 'worker2').should == true - end - - it "should be able to get access to the task if it was started more then max_age ago" do - @job.locked_at = 5.hours.ago - @job.save - - @job.lock_exclusively! 4.hours, 'worker2' - @job.reload - @job.locked_by.should == 'worker2' - @job.locked_at.should > 1.minute.ago - end - - it "should not be found by another worker" do - @backend.find_available('worker2', 1, 6.minutes).length.should == 0 - end - - it "should be found by another worker if the time has expired" do - @backend.find_available('worker2', 1, 4.minutes).length.should == 1 - end - - it "should be able to get exclusive access again when the worker name is the same" do - @job.lock_exclusively!(5.minutes, 'worker1').should be_true - @job.lock_exclusively!(5.minutes, 'worker1').should be_true - @job.lock_exclusively!(5.minutes, 'worker1').should be_true - end - end - - context "when another worker has worked on a task since the job was found to be available, it" do - - before :each do - @job = @backend.create :payload_object => SimpleJob.new - @job_copy_for_worker_2 = @backend.find(@job.id) - end - - it "should not allow a second worker to get exclusive access if already successfully processed by worker1" do - @job.destroy - @job_copy_for_worker_2.lock_exclusively!(4.hours, 'worker2').should == false - end - - 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 - @job.update_attributes(:attempts => 1, :run_at => 1.day.from_now) - @job_copy_for_worker_2.lock_exclusively!(4.hours, 'worker2').should == false - end - end - - context "#name" do - it "should be the class name of the job that was enqueued" do - @backend.create(:payload_object => ErrorJob.new ).name.should == 'ErrorJob' - end - - it "should be the method that will be called if its a performable method object" do - job = @backend.new(:payload_object => NamedJob.new) - job.name.should == 'named_job' - end - - it "should be the instance method that will be called if its a performable method object" do - @job = Story.create(:text => "...").delay.save - @job.name.should == 'Story#save' - end - end - - context "worker prioritization" do - before(:each) do - Delayed::Worker.max_priority = nil - Delayed::Worker.min_priority = nil - end - - it "should fetch jobs ordered by priority" do - 10.times { @backend.enqueue SimpleJob.new, rand(10) } - jobs = @backend.find_available('worker', 10) - jobs.size.should == 10 - jobs.each_cons(2) do |a, b| - a.priority.should <= b.priority - end - end - - it "should only find jobs greater than or equal to min priority" do - min = 5 - Delayed::Worker.min_priority = min - 10.times {|i| @backend.enqueue SimpleJob.new, i } - jobs = @backend.find_available('worker', 10) - jobs.each {|job| job.priority.should >= min} - end - - it "should only find jobs less than or equal to max priority" do - max = 5 - Delayed::Worker.max_priority = max - 10.times {|i| @backend.enqueue SimpleJob.new, i } - jobs = @backend.find_available('worker', 10) - jobs.each {|job| job.priority.should <= max} - end - end - - context "clear_locks!" do - before do - @job = create_job(:locked_by => 'worker', :locked_at => @backend.db_time_now) - end - - it "should clear locks for the given worker" do - @backend.clear_locks!('worker') - @backend.find_available('worker2', 5, 1.minute).should include(@job) - end - - it "should not clear locks for other workers" do - @backend.clear_locks!('worker1') - @backend.find_available('worker1', 5, 1.minute).should_not include(@job) - end - end - - context "unlock" do - before do - @job = create_job(:locked_by => 'worker', :locked_at => @backend.db_time_now) - end - - it "should clear locks" do - @job.unlock - @job.locked_by.should be_nil - @job.locked_at.should be_nil - end - end - - context "large handler" do - before do - text = "Lorem ipsum dolor sit amet. " * 1000 - @job = @backend.enqueue Delayed::PerformableMethod.new(text, :length, {}) - end - - it "should have an id" do - @job.id.should_not be_nil - end - end - - describe "yaml serialization" do - it "should reload changed attributes" do - job = @backend.enqueue SimpleJob.new - yaml = job.to_yaml - job.priority = 99 - job.save - YAML.load(yaml).priority.should == 99 - end - - it "should ignore destroyed records" do - job = @backend.enqueue SimpleJob.new - yaml = job.to_yaml - job.destroy - lambda { YAML.load(yaml).should be_nil }.should_not raise_error - end - end - -end diff --git a/vendor/plugins/delayed_job/spec/delayed/backend/test.rb b/vendor/plugins/delayed_job/spec/delayed/backend/test.rb new file mode 100644 index 0000000..fcf1d10 --- /dev/null +++ b/vendor/plugins/delayed_job/spec/delayed/backend/test.rb @@ -0,0 +1,112 @@ +require 'ostruct' + +# An in-memory backend suitable only for testing. Tries to behave as if it were an ORM. +module Delayed + module Backend + module Test + class Job + attr_accessor :id + attr_accessor :priority + attr_accessor :attempts + attr_accessor :handler + attr_accessor :last_error + attr_accessor :run_at + attr_accessor :locked_at + attr_accessor :locked_by + attr_accessor :failed_at + attr_accessor :queue + + include Delayed::Backend::Base + + cattr_accessor :id + self.id = 0 + + def initialize(hash = {}) + self.attempts = 0 + self.priority = 0 + self.id = (self.class.id += 1) + hash.each{|k,v| send(:"#{k}=", v)} + end + + @jobs = [] + def self.all + @jobs + end + + def self.count + all.size + end + + def self.delete_all + all.clear + end + + def self.create(attrs = {}) + new(attrs).tap do |o| + o.save + end + end + + def self.create!(*args); create(*args); end + + def self.clear_locks!(worker_name) + all.select{|j| j.locked_by == worker_name}.each {|j| j.locked_by = nil; j.locked_at = nil} + end + + # Find a few candidate jobs to run (in case some immediately get locked by others). + def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time) + jobs = all.select do |j| + j.run_at <= db_time_now && + (j.locked_at.nil? || j.locked_at < db_time_now - max_run_time || j.locked_by == worker_name) && + !j.failed? + end + + jobs = jobs.select{|j| Worker.queues.include?(j.queue)} if Worker.queues.any? + jobs = jobs.select{|j| j.priority >= Worker.min_priority} if Worker.min_priority + jobs = jobs.select{|j| j.priority <= Worker.max_priority} if Worker.max_priority + jobs.sort_by{|j| [j.priority, j.run_at]}[0..limit-1] + end + + # Lock this job for this worker. + # Returns true if we have the lock, false otherwise. + def lock_exclusively!(max_run_time, worker) + now = self.class.db_time_now + if locked_by != worker + # We don't own this job so we will update the locked_by name and the locked_at + self.locked_at = now + self.locked_by = worker + end + + return true + end + + def self.db_time_now + Time.current + end + + def update_attributes(attrs = {}) + attrs.each{|k,v| send(:"#{k}=", v)} + save + end + + def destroy + self.class.all.delete(self) + end + + def save + self.run_at ||= Time.current + + self.class.all << self unless self.class.all.include?(self) + true + end + + def save!; save; end + + def reload + reset + self + end + end + end + end +end diff --git a/vendor/plugins/delayed_job/spec/delayed/serialization/test.rb b/vendor/plugins/delayed_job/spec/delayed/serialization/test.rb new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/vendor/plugins/delayed_job/spec/delayed/serialization/test.rb diff --git a/vendor/plugins/delayed_job/spec/helper.rb b/vendor/plugins/delayed_job/spec/helper.rb new file mode 100644 index 0000000..7cd1108 --- /dev/null +++ b/vendor/plugins/delayed_job/spec/helper.rb @@ -0,0 +1,61 @@ +require 'logger' +require 'rspec' + +require 'action_mailer' +require 'active_support/dependencies' +require 'active_record' + +require 'delayed_job' +require 'delayed/backend/shared_spec' + +require 'simplecov' +require 'coveralls' + +SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ + SimpleCov::Formatter::HTMLFormatter, + Coveralls::SimpleCov::Formatter +] +SimpleCov.start + +Delayed::Worker.logger = Logger.new('/tmp/dj.log') +ENV['RAILS_ENV'] = 'test' + +Delayed::Worker.backend = :test + +# Add this directory so the ActiveSupport autoloading works +ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__) + +# Add this to simulate Railtie initializer being executed +ActionMailer::Base.send(:extend, Delayed::DelayMail) + + +# Used to test interactions between DJ and an ORM +ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:' +ActiveRecord::Base.logger = Delayed::Worker.logger +ActiveRecord::Migration.verbose = false + +ActiveRecord::Schema.define do + create_table :stories, :primary_key => :story_id, :force => true do |table| + table.string :text + table.boolean :scoped, :default => true + end +end + +class Story < ActiveRecord::Base + self.primary_key = 'story_id' + def tell; text; end + def whatever(n, _); tell*n; end + default_scope { where(:scoped => true) } + + handle_asynchronously :whatever +end + +RSpec.configure do |config| + config.after(:each) do + Delayed::Worker.reset + end + + config.expect_with :rspec do |c| + c.syntax = :expect + end +end diff --git a/vendor/plugins/delayed_job/spec/lifecycle_spec.rb b/vendor/plugins/delayed_job/spec/lifecycle_spec.rb new file mode 100644 index 0000000..0b31c90 --- /dev/null +++ b/vendor/plugins/delayed_job/spec/lifecycle_spec.rb @@ -0,0 +1,67 @@ +require 'helper' + +describe Delayed::Lifecycle do + let(:lifecycle) { Delayed::Lifecycle.new } + let(:callback) { lambda {|*args|} } + let(:arguments) { [1] } + let(:behavior) { double(Object, :before! => nil, :after! => nil, :inside! => nil) } + let(:wrapped_block) { Proc.new { behavior.inside! } } + + describe "before callbacks" do + before(:each) do + lifecycle.before(:execute, &callback) + end + + it "executes before wrapped block" do + callback.should_receive(:call).with(*arguments).ordered + behavior.should_receive(:inside!).ordered + lifecycle.run_callbacks :execute, *arguments, &wrapped_block + end + end + + describe "after callbacks" do + before(:each) do + lifecycle.after(:execute, &callback) + end + + it "executes after wrapped block" do + behavior.should_receive(:inside!).ordered + callback.should_receive(:call).with(*arguments).ordered + lifecycle.run_callbacks :execute, *arguments, &wrapped_block + end + end + + describe "around callbacks" do + before(:each) do + lifecycle.around(:execute) do |*args, &block| + behavior.before! + block.call(*args) + behavior.after! + end + end + + it "wraps a block" do + behavior.should_receive(:before!).ordered + behavior.should_receive(:inside!).ordered + behavior.should_receive(:after!).ordered + lifecycle.run_callbacks :execute, *arguments, &wrapped_block + end + + it "executes multiple callbacks in order" do + behavior.should_receive(:one).ordered + behavior.should_receive(:two).ordered + behavior.should_receive(:three).ordered + + lifecycle.around(:execute) { |*args, &block| behavior.one; block.call(*args) } + lifecycle.around(:execute) { |*args, &block| behavior.two; block.call(*args) } + lifecycle.around(:execute) { |*args, &block| behavior.three; block.call(*args) } + + lifecycle.run_callbacks(:execute, *arguments, &wrapped_block) + end + end + + it "raises if callback is executed with wrong number of parameters" do + lifecycle.before(:execute, &callback) + expect { lifecycle.run_callbacks(:execute, 1,2,3) {} }.to raise_error(ArgumentError, /1 parameter/) + end +end diff --git a/vendor/plugins/delayed_job/spec/message_sending_spec.rb b/vendor/plugins/delayed_job/spec/message_sending_spec.rb index c2a9587..a7abb3c 100644 --- a/vendor/plugins/delayed_job/spec/message_sending_spec.rb +++ b/vendor/plugins/delayed_job/spec/message_sending_spec.rb @@ -1,51 +1,122 @@ -require 'spec_helper' +require 'helper' describe Delayed::MessageSending do describe "handle_asynchronously" do - class Story < ActiveRecord::Base - def tell!(arg) - end + class Story + def tell!(arg);end handle_asynchronously :tell! end - - it "should alias original method" do - Story.new.should respond_to(:tell_without_delay!) - Story.new.should respond_to(:tell_with_delay!) + + it "aliases original method" do + expect(Story.new).to respond_to(:tell_without_delay!) + expect(Story.new).to respond_to(:tell_with_delay!) end - - it "should create a PerformableMethod" do - story = Story.create! - lambda { + + it "creates a PerformableMethod" do + story = Story.create + expect { job = story.tell!(1) - job.payload_object.class.should == Delayed::PerformableMethod - job.payload_object.method.should == :tell_without_delay! - job.payload_object.args.should == [1] - }.should change { Delayed::Job.count } + expect(job.payload_object.class).to eq(Delayed::PerformableMethod) + expect(job.payload_object.method_name).to eq(:tell_without_delay!) + expect(job.payload_object.args).to eq([1]) + }.to change { Delayed::Job.count } + end + + describe "with options" do + class Fable + cattr_accessor :importance + def tell;end + handle_asynchronously :tell, :priority => Proc.new { self.importance } + end + + it "sets the priority based on the Fable importance" do + Fable.importance = 10 + job = Fable.new.tell + expect(job.priority).to eq(10) + + Fable.importance = 20 + job = Fable.new.tell + expect(job.priority).to eq(20) + end + + describe "using a proc with parameters" do + class Yarn + attr_accessor :importance + def spin + end + handle_asynchronously :spin, :priority => Proc.new {|y| y.importance } + end + + it "sets the priority based on the Fable importance" do + job = Yarn.new.tap {|y| y.importance = 10 }.spin + expect(job.priority).to eq(10) + + job = Yarn.new.tap {|y| y.importance = 20 }.spin + expect(job.priority).to eq(20) + end + end end end context "delay" do - it "should create a new PerformableMethod job" do - lambda { + class FairyTail + attr_accessor :happy_ending + def self.princesses;end + def tell + @happy_ending = true + end + end + + after do + Delayed::Worker.default_queue_name = nil + end + + it "creates a new PerformableMethod job" do + expect { job = "hello".delay.count('l') - job.payload_object.class.should == Delayed::PerformableMethod - job.payload_object.method.should == :count - job.payload_object.args.should == ['l'] - }.should change { Delayed::Job.count }.by(1) + expect(job.payload_object.class).to eq(Delayed::PerformableMethod) + expect(job.payload_object.method_name).to eq(:count) + expect(job.payload_object.args).to eq(['l']) + }.to change { Delayed::Job.count }.by(1) end - it "should set default priority" do + it "sets default priority" do Delayed::Worker.default_priority = 99 - job = Object.delay.to_s - job.priority.should == 99 - Delayed::Worker.default_priority = 0 + job = FairyTail.delay.to_s + expect(job.priority).to eq(99) end - it "should set job options" do + it "sets default queue name" do + Delayed::Worker.default_queue_name = 'abbazabba' + job = FairyTail.delay.to_s + expect(job.queue).to eq('abbazabba') + end + + it "sets job options" do run_at = Time.parse('2010-05-03 12:55 AM') - job = Object.delay(:priority => 20, :run_at => run_at).to_s - job.run_at.should == run_at - job.priority.should == 20 + job = FairyTail.delay(:priority => 20, :run_at => run_at).to_s + expect(job.run_at).to eq(run_at) + expect(job.priority).to eq(20) + end + + it "does not delay the job when delay_jobs is false" do + Delayed::Worker.delay_jobs = false + fairy_tail = FairyTail.new + expect { + expect { + fairy_tail.delay.tell + }.to change(fairy_tail, :happy_ending).from(nil).to(true) + }.not_to change { Delayed::Job.count } + end + + it "does delay the job when delay_jobs is true" do + Delayed::Worker.delay_jobs = true + fairy_tail = FairyTail.new + expect { + expect { + fairy_tail.delay.tell + }.not_to change(fairy_tail, :happy_ending) + }.to change { Delayed::Job.count }.by(1) end end end diff --git a/vendor/plugins/delayed_job/spec/performable_mailer_spec.rb b/vendor/plugins/delayed_job/spec/performable_mailer_spec.rb new file mode 100644 index 0000000..617ab84 --- /dev/null +++ b/vendor/plugins/delayed_job/spec/performable_mailer_spec.rb @@ -0,0 +1,44 @@ +require 'helper' + +require 'action_mailer' +class MyMailer < ActionMailer::Base + def signup(email) + mail :to => email, :subject => "Delaying Emails", :from => "delayedjob@example.com",:body => 'Delaying Emails Body' + end +end + +describe ActionMailer::Base do + describe "delay" do + it "enqueues a PerformableEmail job" do + expect { + job = MyMailer.delay.signup('john@example.com') + expect(job.payload_object.class).to eq(Delayed::PerformableMailer) + expect(job.payload_object.method_name).to eq(:signup) + expect(job.payload_object.args).to eq(['john@example.com']) + }.to change { Delayed::Job.count }.by(1) + end + end + + describe "delay on a mail object" do + it "raises an exception" do + expect { + MyMailer.signup('john@example.com').delay + }.to raise_error(RuntimeError) + end + end + + describe Delayed::PerformableMailer do + describe "perform" do + it "calls the method and #deliver on the mailer" do + email = double('email', :deliver => true) + mailer_class = double('MailerClass', :signup => email) + mailer = Delayed::PerformableMailer.new(mailer_class, :signup, ['john@example.com']) + + mailer_class.should_receive(:signup).with('john@example.com') + email.should_receive(:deliver) + mailer.perform + end + end + end + +end diff --git a/vendor/plugins/delayed_job/spec/performable_method_spec.rb b/vendor/plugins/delayed_job/spec/performable_method_spec.rb index 722b46d..a116860 100644 --- a/vendor/plugins/delayed_job/spec/performable_method_spec.rb +++ b/vendor/plugins/delayed_job/spec/performable_method_spec.rb @@ -1,48 +1,136 @@ -require 'spec_helper' +require 'helper' describe Delayed::PerformableMethod do describe "perform" do before do @method = Delayed::PerformableMethod.new("foo", :count, ['o']) end - + context "with the persisted record cannot be found" do before do @method.object = nil end - - it "should be a no-op if object is nil" do - lambda { @method.perform }.should_not raise_error + + it "does nothing if object is nil" do + expect{@method.perform}.not_to raise_error end end - - it "should call the method on the object" do + + it "calls the method on the object" do @method.object.should_receive(:count).with('o') @method.perform end - - it "should respond to on_permanent_failure when implemented and target object is called via object.delay.do_something" do - @method = Delayed::PerformableMethod.new(OnPermanentFailureJob.new, :perform, []) - @method.respond_to?(:on_permanent_failure).should be_true - @method.object.should_receive(:on_permanent_failure) - @method.on_permanent_failure - end end - it "should raise a NoMethodError if target method doesn't exist" do - lambda { + it "raises a NoMethodError if target method doesn't exist" do + expect { Delayed::PerformableMethod.new(Object, :method_that_does_not_exist, []) - }.should raise_error(NoMethodError) + }.to raise_error(NoMethodError) end - - it "should not raise NoMethodError if target method is private" do + + it "does not raise NoMethodError if target method is private" do clazz = Class.new do def private_method end private :private_method end - lambda { + expect { Delayed::PerformableMethod.new(clazz.new, :private_method, []) - }.should_not raise_error(NoMethodError) + }.not_to raise_error + end + + describe "hooks" do + %w(before after success).each do |hook| + it "delegates #{hook} hook to object" do + story = Story.create + job = story.delay.tell + + story.should_receive(hook).with(job) + job.invoke_job + end + end + + %w(before after success).each do |hook| + it "delegates #{hook} hook to object" do + story = Story.create + job = story.delay.tell + + story.should_receive(hook).with(job) + job.invoke_job + end + end + + it "delegates enqueue hook to object" do + story = Story.create + story.should_receive(:enqueue).with(an_instance_of(Delayed::Job)) + story.delay.tell + end + + it "delegates error hook to object" do + story = Story.create + story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError)) + story.should_receive(:tell).and_raise(RuntimeError) + expect { story.delay.tell.invoke_job }.to raise_error + end + + it "delegates error hook to object when delay_jobs = false" do + story = Story.create + story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError)) + story.should_receive(:tell).and_raise(RuntimeError) + expect { story.delay.tell.invoke_job }.to raise_error + end + + it "delegates failure hook to object" do + method = Delayed::PerformableMethod.new("object", :size, []) + method.object.should_receive(:failure) + method.failure + end + + context 'with delay_job == false' do + before do + Delayed::Worker.delay_jobs = false + end + + after do + Delayed::Worker.delay_jobs = true + end + + %w(before after success).each do |hook| + it "delegates #{hook} hook to object" do + story = Story.create + story.should_receive(hook).with(an_instance_of(Delayed::Job)) + story.delay.tell + end + end + + %w(before after success).each do |hook| + it "delegates #{hook} hook to object" do + story = Story.create + story.should_receive(hook).with(an_instance_of(Delayed::Job)) + story.delay.tell + end + end + + it "delegates error hook to object" do + story = Story.create + story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError)) + story.should_receive(:tell).and_raise(RuntimeError) + expect { story.delay.tell }.to raise_error + end + + it "delegates error hook to object when delay_jobs = false" do + story = Story.create + story.should_receive(:error).with(an_instance_of(Delayed::Job), an_instance_of(RuntimeError)) + story.should_receive(:tell).and_raise(RuntimeError) + expect { story.delay.tell }.to raise_error + end + + it "delegates failure hook to object when delay_jobs = false" do + Delayed::Worker.delay_jobs = false + method = Delayed::PerformableMethod.new("object", :size, []) + method.object.should_receive(:failure) + method.failure + end + end end end diff --git a/vendor/plugins/delayed_job/spec/sample_jobs.rb b/vendor/plugins/delayed_job/spec/sample_jobs.rb index a4f6df0..30a6183 100644 --- a/vendor/plugins/delayed_job/spec/sample_jobs.rb +++ b/vendor/plugins/delayed_job/spec/sample_jobs.rb @@ -1,3 +1,9 @@ +class NamedJob < Struct.new(:perform) + def display_name + 'named_job' + end +end + class SimpleJob cattr_accessor :runs; self.runs = 0 def perform; @@runs += 1; end @@ -6,20 +12,64 @@ end class ErrorJob cattr_accessor :runs; self.runs = 0 def perform; raise 'did not work'; end -end +end + +class CustomRescheduleJob < Struct.new(:offset) + cattr_accessor :runs; self.runs = 0 + def perform; raise 'did not work'; end + def reschedule_at(time, attempts); time + offset; end +end class LongRunningJob def perform; sleep 250; end end class OnPermanentFailureJob < SimpleJob - def on_permanent_failure - end + def failure; end + def max_attempts; 1; end end module M class ModuleJob cattr_accessor :runs; self.runs = 0 - def perform; @@runs += 1; end + def perform; @@runs += 1; end + end +end + +class CallbackJob + cattr_accessor :messages + + def enqueue(job) + self.class.messages << 'enqueue' + end + + def before(job) + self.class.messages << 'before' + end + + def perform + self.class.messages << 'perform' + end + + def after(job) + self.class.messages << 'after' + end + + def success(job) + self.class.messages << 'success' + end + + def error(job, error) + self.class.messages << "error: #{error.class}" + end + + def failure(job) + self.class.messages << 'failure' + end +end + +class EnqueueJobMod < SimpleJob + def enqueue(job) + job.run_at = 20.minutes.from_now end end diff --git a/vendor/plugins/delayed_job/spec/setup/active_record.rb b/vendor/plugins/delayed_job/spec/setup/active_record.rb deleted file mode 100644 index 56fd238..0000000 --- a/vendor/plugins/delayed_job/spec/setup/active_record.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'active_record' - -ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:') -ActiveRecord::Base.logger = Delayed::Worker.logger -ActiveRecord::Migration.verbose = false - -ActiveRecord::Schema.define do - create_table :delayed_jobs, :force => true do |table| - table.integer :priority, :default => 0 - table.integer :attempts, :default => 0 - table.text :handler - table.text :last_error - table.datetime :run_at - table.datetime :locked_at - table.datetime :failed_at - table.string :locked_by - table.timestamps - end - - add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority' - - create_table :stories, :force => true do |table| - table.string :text - end -end - -# Purely useful for test cases... -class Story < ActiveRecord::Base - def tell; text; end - def whatever(n, _); tell*n; end - - handle_asynchronously :whatever -end diff --git a/vendor/plugins/delayed_job/spec/setup/couch_rest.rb b/vendor/plugins/delayed_job/spec/setup/couch_rest.rb deleted file mode 100644 index 0dea714..0000000 --- a/vendor/plugins/delayed_job/spec/setup/couch_rest.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'couchrest' -require 'delayed/backend/couch_rest' - -Delayed::Backend::CouchRest::Job.use_database CouchRest::Server.new.database!('delayed_job_spec') - -# try to perform a query to check that we can connect -Delayed::Backend::CouchRest::Job.all \ No newline at end of file diff --git a/vendor/plugins/delayed_job/spec/setup/data_mapper.rb b/vendor/plugins/delayed_job/spec/setup/data_mapper.rb deleted file mode 100644 index 54ac508..0000000 --- a/vendor/plugins/delayed_job/spec/setup/data_mapper.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'dm-core' -require 'dm-validations' - -require 'delayed/backend/data_mapper' - -DataMapper.logger = Delayed::Worker.logger -DataMapper.setup(:default, "sqlite3::memory:") -DataMapper.auto_migrate! \ No newline at end of file diff --git a/vendor/plugins/delayed_job/spec/setup/mongo_mapper.rb b/vendor/plugins/delayed_job/spec/setup/mongo_mapper.rb deleted file mode 100644 index ec967c0..0000000 --- a/vendor/plugins/delayed_job/spec/setup/mongo_mapper.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'mongo_mapper' - -MongoMapper.config = { - RAILS_ENV => {'database' => 'delayed_job'} -} -MongoMapper.connect RAILS_ENV - -unless defined?(Story) - class Story - include ::MongoMapper::Document - def tell; text; end - def whatever(n, _); tell*n; end - def self.count; end - - handle_asynchronously :whatever - end -end diff --git a/vendor/plugins/delayed_job/spec/spec_helper.rb b/vendor/plugins/delayed_job/spec/spec_helper.rb deleted file mode 100644 index ce78fae..0000000 --- a/vendor/plugins/delayed_job/spec/spec_helper.rb +++ /dev/null @@ -1,31 +0,0 @@ -$:.unshift(File.dirname(__FILE__) + '/../lib') - -require 'rubygems' -require 'spec' -require 'logger' - -gem 'activerecord', ENV['RAILS_VERSION'] if ENV['RAILS_VERSION'] - -require 'delayed_job' -require 'sample_jobs' - -Delayed::Worker.logger = Logger.new('/tmp/dj.log') -RAILS_ENV = 'test' - -# determine the available backends -BACKENDS = [] -Dir.glob("#{File.dirname(__FILE__)}/setup/*.rb") do |backend| - begin - backend = File.basename(backend, '.rb') - require "setup/#{backend}" - require "backend/#{backend}_job_spec" - BACKENDS << backend.to_sym - rescue Exception - puts "Unable to load #{backend} backend: #{$!}" - end -end - -Delayed::Worker.backend = BACKENDS.first - -# Add this directory so the ActiveSupport autoloading works -ActiveSupport::Dependencies.load_paths << File.dirname(__FILE__) diff --git a/vendor/plugins/delayed_job/spec/test_backend_spec.rb b/vendor/plugins/delayed_job/spec/test_backend_spec.rb new file mode 100644 index 0000000..2840ef3 --- /dev/null +++ b/vendor/plugins/delayed_job/spec/test_backend_spec.rb @@ -0,0 +1,13 @@ +require 'helper' + +describe Delayed::Backend::Test::Job do + it_should_behave_like 'a delayed_job backend' + + describe "#reload" do + it "causes the payload object to be reloaded" do + job = "foo".delay.length + o = job.payload_object + expect(o.object_id).not_to eq(job.reload.payload_object.object_id) + end + end +end diff --git a/vendor/plugins/delayed_job/spec/worker_spec.rb b/vendor/plugins/delayed_job/spec/worker_spec.rb index 7b816aa..d57ad7a 100644 --- a/vendor/plugins/delayed_job/spec/worker_spec.rb +++ b/vendor/plugins/delayed_job/spec/worker_spec.rb @@ -1,214 +1,102 @@ -require 'spec_helper' +require 'helper' describe Delayed::Worker do - def job_create(opts = {}) - Delayed::Job.create(opts.merge(:payload_object => SimpleJob.new)) - end - describe "backend=" do before do @clazz = Class.new Delayed::Worker.backend = @clazz end - it "should set the Delayed::Job constant to the backend" do - Delayed::Job.should == @clazz + after do + Delayed::Worker.backend = :test end - - it "should set backend with a symbol" do - Delayed::Worker.backend = :active_record - Delayed::Worker.backend.should == Delayed::Backend::ActiveRecord::Job + + it "sets the Delayed::Job constant to the backend" do + expect(Delayed::Job).to eq(@clazz) + end + + it "sets backend with a symbol" do + Delayed::Worker.backend = :test + expect(Delayed::Worker.backend).to eq(Delayed::Backend::Test::Job) end end - - BACKENDS.each do |backend| - describe "with the #{backend} backend" do - before do - Delayed::Worker.backend = backend - Delayed::Job.delete_all - @worker = Delayed::Worker.new(:max_priority => nil, :min_priority => nil, :quiet => true) + describe "job_say" do + before do + @worker = Delayed::Worker.new + @job = double('job', :id => 123, :name => 'ExampleJob') + end - SimpleJob.runs = 0 - end - - describe "running a job" do - it "should fail after Worker.max_run_time" do - begin - old_max_run_time = Delayed::Worker.max_run_time - Delayed::Worker.max_run_time = 1.second - @job = Delayed::Job.create :payload_object => LongRunningJob.new - @worker.run(@job) - @job.reload.last_error.should =~ /expired/ - @job.attempts.should == 1 - ensure - Delayed::Worker.max_run_time = old_max_run_time - end - end - end - - context "worker prioritization" do - before(:each) do - @worker = Delayed::Worker.new(:max_priority => 5, :min_priority => -5, :quiet => true) - end + it "logs with job name and id" do + @worker.should_receive(:say). + with('Job ExampleJob (id=123) message', Delayed::Worker::DEFAULT_LOG_LEVEL) + @worker.job_say(@job, 'message') + end + end - it "should only work_off jobs that are >= min_priority" do - job_create(:priority => -10) - job_create(:priority => 0) - @worker.work_off + context "worker read-ahead" do + before do + @read_ahead = Delayed::Worker.read_ahead + end - SimpleJob.runs.should == 1 - end + after do + Delayed::Worker.read_ahead = @read_ahead + end - it "should only work_off jobs that are <= max_priority" do - job_create(:priority => 10) - job_create(:priority => 0) + it "reads five jobs" do + Delayed::Job.should_receive(:find_available).with(anything, 5, anything).and_return([]) + Delayed::Job.reserve(Delayed::Worker.new) + end - @worker.work_off + it "reads a configurable number of jobs" do + Delayed::Worker.read_ahead = 15 + Delayed::Job.should_receive(:find_available).with(anything, Delayed::Worker.read_ahead, anything).and_return([]) + Delayed::Job.reserve(Delayed::Worker.new) + end + end - SimpleJob.runs.should == 1 - end - end + context "worker exit on complete" do + before do + Delayed::Worker.exit_on_complete = true + end - context "while running with locked and expired jobs" do - before(:each) do - @worker.name = 'worker1' - end - - it "should not run jobs locked by another worker" do - job_create(:locked_by => 'other_worker', :locked_at => (Delayed::Job.db_time_now - 1.minutes)) - lambda { @worker.work_off }.should_not change { SimpleJob.runs } - end - - it "should run open jobs" do - job_create - lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1) - end - - it "should run expired jobs" do - expired_time = Delayed::Job.db_time_now - (1.minutes + Delayed::Worker.max_run_time) - job_create(:locked_by => 'other_worker', :locked_at => expired_time) - lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1) - end - - it "should run own jobs" do - job_create(:locked_by => @worker.name, :locked_at => (Delayed::Job.db_time_now - 1.minutes)) - lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1) - end - end - - describe "failed jobs" do - before do - # reset defaults - Delayed::Worker.destroy_failed_jobs = true - Delayed::Worker.max_attempts = 25 - - @job = Delayed::Job.enqueue ErrorJob.new - end - - it "should record last_error when destroy_failed_jobs = false, max_attempts = 1" do - Delayed::Worker.destroy_failed_jobs = false - Delayed::Worker.max_attempts = 1 - @worker.run(@job) - @job.reload - @job.last_error.should =~ /did not work/ - @job.last_error.should =~ /worker_spec.rb/ - @job.attempts.should == 1 - @job.failed_at.should_not be_nil - end - - it "should re-schedule jobs after failing" do - @worker.run(@job) - @job.reload - @job.last_error.should =~ /did not work/ - @job.last_error.should =~ /sample_jobs.rb:8:in `perform'/ - @job.attempts.should == 1 - @job.run_at.should > Delayed::Job.db_time_now - 10.minutes - @job.run_at.should < Delayed::Job.db_time_now + 10.minutes - end - end - - context "reschedule" do - before do - @job = Delayed::Job.create :payload_object => SimpleJob.new - end - - share_examples_for "any failure more than Worker.max_attempts times" do - context "when the job's payload has an #on_permanent_failure hook" do - before do - @job = Delayed::Job.create :payload_object => OnPermanentFailureJob.new - @job.payload_object.should respond_to :on_permanent_failure - end - - it "should run that hook" do - @job.payload_object.should_receive :on_permanent_failure - Delayed::Worker.max_attempts.times { @worker.reschedule(@job) } - end - end - - context "when the job's payload has no #on_permanent_failure hook" do - # It's a little tricky to test this in a straightforward way, - # because putting a should_not_receive expectation on - # @job.payload_object.on_permanent_failure makes that object - # incorrectly return true to - # payload_object.respond_to? :on_permanent_failure, which is what - # reschedule uses to decide whether to call on_permanent_failure. - # So instead, we just make sure that the payload_object as it - # already stands doesn't respond_to? on_permanent_failure, then - # shove it through the iterated reschedule loop and make sure we - # don't get a NoMethodError (caused by calling that nonexistent - # on_permanent_failure method). - - before do - @job.payload_object.should_not respond_to(:on_permanent_failure) - end - - it "should not try to run that hook" do - lambda do - Delayed::Worker.max_attempts.times { @worker.reschedule(@job) } - end.should_not raise_exception(NoMethodError) - end - end - end - - context "and we want to destroy jobs" do - before do - Delayed::Worker.destroy_failed_jobs = true - end - - it_should_behave_like "any failure more than Worker.max_attempts times" - - it "should be destroyed if it failed more than Worker.max_attempts times" do - @job.should_receive(:destroy) - Delayed::Worker.max_attempts.times { @worker.reschedule(@job) } - end - - it "should not be destroyed if failed fewer than Worker.max_attempts times" do - @job.should_not_receive(:destroy) - (Delayed::Worker.max_attempts - 1).times { @worker.reschedule(@job) } - end - end - - context "and we don't want to destroy jobs" do - before do - Delayed::Worker.destroy_failed_jobs = false - end - - it_should_behave_like "any failure more than Worker.max_attempts times" - - it "should be failed if it failed more than Worker.max_attempts times" do - @job.reload.failed_at.should == nil - Delayed::Worker.max_attempts.times { @worker.reschedule(@job) } - @job.reload.failed_at.should_not == nil - end - - it "should not be failed if it failed fewer than Worker.max_attempts times" do - (Delayed::Worker.max_attempts - 1).times { @worker.reschedule(@job) } - @job.reload.failed_at.should == nil - end - end + after do + Delayed::Worker.exit_on_complete = false + end + + it "exits the loop when no jobs are available" do + worker = Delayed::Worker.new + Timeout::timeout(2) do + worker.start end end end - + + context "worker job reservation" do + before do + Delayed::Worker.exit_on_complete = true + end + + after do + Delayed::Worker.exit_on_complete = false + end + + it "handles error during job reservation" do + Delayed::Job.should_receive(:reserve).and_raise(Exception) + Delayed::Worker.new.work_off + end + + it "gives up after 10 backend failures" do + Delayed::Job.stub(:reserve).and_raise(Exception) + worker = Delayed::Worker.new + 9.times { worker.work_off } + expect(lambda { worker.work_off }).to raise_exception + end + + it "allows the backend to attempt recovery from reservation errors" do + Delayed::Job.should_receive(:reserve).and_raise(Exception) + Delayed::Job.should_receive(:recover_from).with(instance_of(Exception)) + Delayed::Worker.new.work_off + end + end end diff --git a/vendor/plugins/delayed_job/spec/yaml_ext_spec.rb b/vendor/plugins/delayed_job/spec/yaml_ext_spec.rb new file mode 100644 index 0000000..d6edfa7 --- /dev/null +++ b/vendor/plugins/delayed_job/spec/yaml_ext_spec.rb @@ -0,0 +1,35 @@ +require 'helper' + +describe "YAML" do + it "autoloads classes" do + expect { + yaml = "--- !ruby/class Autoloaded::Clazz\n" + expect(YAML.load(yaml)).to eq(Autoloaded::Clazz) + }.not_to raise_error + end + + it "autoloads the class of a struct" do + expect { + yaml = "--- !ruby/class Autoloaded::Struct\n" + expect(YAML.load(yaml)).to eq(Autoloaded::Struct) + }.not_to raise_error + end + + it "autoloads the class for the instance of a struct" do + expect { + yaml = "--- !ruby/struct:Autoloaded::InstanceStruct {}" + expect(YAML.load(yaml).class).to eq(Autoloaded::InstanceStruct) + }.not_to raise_error + end + + it "autoloads the class for the instance" do + expect { + yaml = "--- !ruby/object:Autoloaded::InstanceClazz {}\n" + expect(YAML.load(yaml).class).to eq(Autoloaded::InstanceClazz) + }.not_to raise_error + end + + it "does not throw an uninitialized constant Syck::Syck when using YAML.load with poorly formed yaml" do + expect{ YAML.load(YAML.dump("foo: *bar"))}.not_to raise_error + end +end diff --git a/vendor/plugins/delayed_job/tasks/jobs.rake b/vendor/plugins/delayed_job/tasks/jobs.rake deleted file mode 100644 index f3993f7..0000000 --- a/vendor/plugins/delayed_job/tasks/jobs.rake +++ /dev/null @@ -1 +0,0 @@ -require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'delayed', 'tasks')) diff --git a/vendor/plugins/delayed_job_active_record/.rspec b/vendor/plugins/delayed_job_active_record/.rspec new file mode 100644 index 0000000..ba44b74 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/.rspec @@ -0,0 +1,2 @@ +--color +--fail-fast diff --git a/vendor/plugins/delayed_job_active_record/.travis.yml b/vendor/plugins/delayed_job_active_record/.travis.yml new file mode 100644 index 0000000..2550250 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/.travis.yml @@ -0,0 +1,28 @@ +language: ruby +before_script: + - mysql -e 'create database delayed_job_test;' + - psql -c 'create database delayed_job_test;' -U postgres +script: bundle exec rspec +gemfile: + - gemfiles/mysql/3-0.gemfile + - gemfiles/mysql/3-1.gemfile + - gemfiles/mysql/3-2.gemfile + - gemfiles/mysql/4-0.gemfile + - gemfiles/postgresql/3-0.gemfile + - gemfiles/postgresql/3-1.gemfile + - gemfiles/postgresql/3-2.gemfile + - gemfiles/postgresql/4-0.gemfile + - gemfiles/sqlite3/3-0.gemfile + - gemfiles/sqlite3/3-1.gemfile + - gemfiles/sqlite3/3-2.gemfile + - gemfiles/sqlite3/4-0.gemfile + - gemfiles/sqlite3/4-0-protected_attributes.gemfile +rvm: + - rbx-19mode + - jruby-19mode + - 1.9.3 + - 2.0.0 +matrix: + allow_failures: + - rvm: rbx-19mode + - rvm: jruby-19mode diff --git a/vendor/plugins/delayed_job_active_record/CONTRIBUTING.md b/vendor/plugins/delayed_job_active_record/CONTRIBUTING.md new file mode 100644 index 0000000..c154663 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/CONTRIBUTING.md @@ -0,0 +1,14 @@ +## How to contribute + +If you find what looks like a bug: + +* Search the [mailing list](http://groups.google.com/group/delayed_job) to see if anyone else had the same issue. +* Check the [GitHub issue tracker](http://github.com/collectiveidea/delayed_job_active_record/issues/) to see if anyone else has reported issue. +* If you don't see anything, create an issue with information on how to reproduce it. + +If you want to contribute an enhancement or a fix: + +* Fork the project on github. +* Make your changes with tests. +* Commit the changes without making changes to the Rakefile or any other files that aren't related to your enhancement or fix +* Send a pull request. diff --git a/vendor/plugins/delayed_job_active_record/Gemfile b/vendor/plugins/delayed_job_active_record/Gemfile new file mode 100644 index 0000000..7b288e0 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/Gemfile @@ -0,0 +1,28 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'activerecord-jdbcmysql-adapter' + gem 'jdbc-mysql' + + gem 'activerecord-jdbcpostgresql-adapter' + gem 'jdbc-postgres' + + gem 'activerecord-jdbcsqlite3-adapter' + gem 'jdbc-sqlite3' + end + + platforms :ruby, :mswin, :mingw do + gem 'mysql', '~> 2.8.1' + gem 'pg' + gem 'sqlite3' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false +end + +gemspec diff --git a/vendor/plugins/delayed_job_active_record/LICENSE.md b/vendor/plugins/delayed_job_active_record/LICENSE.md new file mode 100644 index 0000000..759ff02 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/LICENSE.md @@ -0,0 +1,20 @@ +Copyright (c) 2005 Tobias Lütke + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND +NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/plugins/delayed_job_active_record/README.md b/vendor/plugins/delayed_job_active_record/README.md new file mode 100644 index 0000000..a546a38 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/README.md @@ -0,0 +1,31 @@ +# DelayedJob ActiveRecord Backend + +[![Gem Version](https://badge.fury.io/rb/delayed_job_active_record.png)](https://rubygems.org/gems/delayed_job_active_record) +[![Build Status](https://travis-ci.org/collectiveidea/delayed_job_active_record.png)](https://travis-ci.org/collectiveidea/delayed_job_active_record) +[![Dependency Status](https://gemnasium.com/collectiveidea/delayed_job_active_record.png)](https://gemnasium.com/collectiveidea/delayed_job_active_record) +[![Code Climate](https://codeclimate.com/github/collectiveidea/delayed_job_active_record.png)](https://codeclimate.com/github/collectiveidea/delayed_job_active_record) +[![Coverage Status](https://coveralls.io/repos/collectiveidea/delayed_job_active_record/badge.png?branch=master)](https://coveralls.io/r/collectiveidea/delayed_job_active_record) + +## Installation + +Add the gem to your Gemfile: + + gem 'delayed_job_active_record' + +Run `bundle install`. + +If you're using Rails, run the generator to create the migration for the +delayed_job table. + + rails g delayed_job:active_record + rake db:migrate + +## Upgrading from 2.x to 3.0.0 + +If you're upgrading from Delayed Job 2.x, run the upgrade generator to create a +migration to add a column to your delayed_jobs table. + + rails g delayed_job:upgrade + rake db:migrate + +That's it. Use [delayed_job as normal](http://github.com/collectiveidea/delayed_job). diff --git a/vendor/plugins/delayed_job_active_record/Rakefile b/vendor/plugins/delayed_job_active_record/Rakefile new file mode 100644 index 0000000..0932015 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/Rakefile @@ -0,0 +1,35 @@ +# -*- encoding: utf-8 -*- +require "bundler/gem_helper" +Bundler::GemHelper.install_tasks + +require "rspec/core/rake_task" + +ADAPTERS = %w(mysql postgresql sqlite3) + +ADAPTERS.each do |adapter| + desc "Run RSpec code examples for #{adapter} adapter" + RSpec::Core::RakeTask.new(adapter => "#{adapter}:adapter") + + namespace adapter do + task :adapter do + ENV["ADAPTER"] = adapter + end + end +end + +task :coverage do + ENV["COVERAGE"] = "true" +end + +task :adapter do + ENV["ADAPTER"] = nil +end + +Rake::Task[:spec].enhance do + require "simplecov" + require "coveralls" + + Coveralls::SimpleCov::Formatter.new.format(SimpleCov.result) +end + +task default: ([:coverage] + ADAPTERS + [:adapter]) diff --git a/vendor/plugins/delayed_job_active_record/delayed_job_active_record.gemspec b/vendor/plugins/delayed_job_active_record/delayed_job_active_record.gemspec new file mode 100644 index 0000000..a480c5e --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/delayed_job_active_record.gemspec @@ -0,0 +1,19 @@ +# coding: utf-8 + +Gem::Specification.new do |spec| + spec.add_dependency 'activerecord', ['>= 3.0', '< 4.1'] + spec.add_dependency 'delayed_job', ['>= 3.0', '< 4.1'] + spec.authors = ["Brian Ryckbost", "Matt Griffin", "Erik Michaels-Ober"] + spec.description = 'ActiveRecord backend for Delayed::Job, originally authored by Tobias Lütke' + spec.email = ['bryckbost@gmail.com', 'matt@griffinonline.org', 'sferik@gmail.com'] + spec.files = %w(CONTRIBUTING.md LICENSE.md README.md Rakefile delayed_job_active_record.gemspec) + spec.files += Dir.glob("lib/**/*.rb") + spec.files += Dir.glob("spec/**/*") + spec.homepage = 'http://github.com/collectiveidea/delayed_job_active_record' + spec.licenses = ['MIT'] + spec.name = 'delayed_job_active_record' + spec.require_paths = ['lib'] + spec.summary = 'ActiveRecord backend for DelayedJob' + spec.test_files = Dir.glob("spec/**/*") + spec.version = '4.0.0' +end diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-0.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-0.gemfile new file mode 100644 index 0000000..74e564a --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-0.gemfile @@ -0,0 +1,23 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcmysql-adapter' + gem 'jdbc-mysql' + end + + platforms :ruby, :mswin, :mingw do + gem 'mysql', '~> 2.8.1' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false + + gem 'activerecord', "~> 3.0.0" +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-1.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-1.gemfile new file mode 100644 index 0000000..fc883ed --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-1.gemfile @@ -0,0 +1,23 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcmysql-adapter' + gem 'jdbc-mysql' + end + + platforms :ruby, :mswin, :mingw do + gem 'mysql', '~> 2.8.1' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false + + gem 'activerecord', "~> 3.1.0" +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-2.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-2.gemfile new file mode 100644 index 0000000..f77341c --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/mysql/3-2.gemfile @@ -0,0 +1,23 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcmysql-adapter' + gem 'jdbc-mysql' + end + + platforms :ruby, :mswin, :mingw do + gem 'mysql', '~> 2.8.1' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false + + gem 'activerecord', "~> 3.2.0" +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/mysql/4-0.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/mysql/4-0.gemfile new file mode 100644 index 0000000..9328916 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/mysql/4-0.gemfile @@ -0,0 +1,25 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcmysql-adapter' + gem 'jdbc-mysql' + end + + platforms :ruby, :mswin, :mingw do + gem 'mysql', '~> 2.9' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false + + gem 'activerecord', "~> 4.0.0.beta" + + gem 'delayed_job', "~> 4.0.0.beta" +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-0.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-0.gemfile new file mode 100644 index 0000000..3f39d4a --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-0.gemfile @@ -0,0 +1,23 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcpostgresql-adapter' + gem 'jdbc-postgres' + end + + platforms :ruby, :mswin, :mingw do + gem 'pg' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false + + gem 'activerecord', "~> 3.0.0" +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-1.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-1.gemfile new file mode 100644 index 0000000..cf2b1fc --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-1.gemfile @@ -0,0 +1,23 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcpostgresql-adapter' + gem 'jdbc-postgres' + end + + platforms :ruby, :mswin, :mingw do + gem 'pg' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false + + gem 'activerecord', "~> 3.1.0" +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-2.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-2.gemfile new file mode 100644 index 0000000..b5c9026 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/postgresql/3-2.gemfile @@ -0,0 +1,23 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcpostgresql-adapter' + gem 'jdbc-postgres' + end + + platforms :ruby, :mswin, :mingw do + gem 'pg' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false + + gem 'activerecord', "~> 3.2.0" +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/postgresql/4-0.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/postgresql/4-0.gemfile new file mode 100644 index 0000000..fd576bc --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/postgresql/4-0.gemfile @@ -0,0 +1,25 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcpostgresql-adapter' + gem 'jdbc-postgres' + end + + platforms :ruby, :mswin, :mingw do + gem 'pg' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false + + gem 'activerecord', "~> 4.0.0.beta" + + gem 'delayed_job', "~> 4.0.0.beta" +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-0.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-0.gemfile new file mode 100644 index 0000000..0688e17 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-0.gemfile @@ -0,0 +1,23 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'jruby-openssl' + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcsqlite3-adapter' + end + + platforms :ruby, :mswin, :mingw do + gem 'sqlite3' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false + + gem 'activerecord', "~> 3.0.0" +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-1.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-1.gemfile new file mode 100644 index 0000000..b693196 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-1.gemfile @@ -0,0 +1,23 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'jruby-openssl' + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcsqlite3-adapter' + end + + platforms :ruby, :mswin, :mingw do + gem 'sqlite3' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false + + gem 'activerecord', "~> 3.1.0" +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-2.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-2.gemfile new file mode 100644 index 0000000..2e2ca7a --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/3-2.gemfile @@ -0,0 +1,23 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'jruby-openssl' + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcsqlite3-adapter' + end + + platforms :ruby, :mswin, :mingw do + gem 'sqlite3' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false + + gem 'activerecord', "~> 3.2.0" +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/4-0-protected_attributes.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/4-0-protected_attributes.gemfile new file mode 100644 index 0000000..f25516e --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/4-0-protected_attributes.gemfile @@ -0,0 +1,26 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'jruby-openssl' + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcsqlite3-adapter' + end + + platforms :ruby, :mswin, :mingw do + gem 'sqlite3' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11', :require => false + gem 'simplecov', :require => false + + gem 'activerecord', "~> 4.0.0.beta" + gem 'protected_attributes' + + gem 'delayed_job', "~> 4.0.0.beta", :require => false +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/4-0.gemfile b/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/4-0.gemfile new file mode 100644 index 0000000..3b70d1b --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/gemfiles/sqlite3/4-0.gemfile @@ -0,0 +1,25 @@ +source 'https://rubygems.org' + +gem 'rake' + +group :test do + platforms :jruby do + gem 'jruby-openssl' + gem 'activerecord-jdbc-adapter' + gem 'activerecord-jdbcsqlite3-adapter' + end + + platforms :ruby, :mswin, :mingw do + gem 'sqlite3' + end + + gem 'coveralls', :require => false + gem 'rspec', '>= 2.11' + gem 'simplecov', :require => false + + gem 'activerecord', "~> 4.0.0.beta" + + gem 'delayed_job', "~> 4.0.0.beta" +end + +gemspec :path => "../../" diff --git a/vendor/plugins/delayed_job_active_record/lib/delayed/backend/active_record.rb b/vendor/plugins/delayed_job_active_record/lib/delayed/backend/active_record.rb new file mode 100644 index 0000000..747c2f1 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/lib/delayed/backend/active_record.rb @@ -0,0 +1,111 @@ +require 'active_record/version' +module Delayed + module Backend + module ActiveRecord + # A job object that is persisted to the database. + # Contains the work object as a YAML field. + class Job < ::ActiveRecord::Base + include Delayed::Backend::Base + + if ::ActiveRecord::VERSION::MAJOR < 4 || defined?(::ActiveRecord::MassAssignmentSecurity) + attr_accessible :priority, :run_at, :queue, :payload_object, + :failed_at, :locked_at, :locked_by, :handler + end + + scope :by_priority, lambda { order('priority ASC, run_at ASC') } + + before_save :set_default_run_at + + def self.set_delayed_job_table_name + delayed_job_table_name = "#{::ActiveRecord::Base.table_name_prefix}delayed_jobs" + self.table_name = delayed_job_table_name + end + + self.set_delayed_job_table_name + + def self.ready_to_run(worker_name, max_run_time) + 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) + end + + def self.before_fork + ::ActiveRecord::Base.clear_all_connections! + end + + def self.after_fork + ::ActiveRecord::Base.establish_connection + end + + # When a worker is exiting, make sure we don't have any locked jobs. + def self.clear_locks!(worker_name) + where(:locked_by => worker_name).update_all(:locked_by => nil, :locked_at => nil) + end + + def self.reserve(worker, max_run_time = Worker.max_run_time) + # scope to filter to records that are "ready to run" + ready_scope = self.ready_to_run(worker.name, max_run_time) + + # scope to filter to the single next eligible job + ready_scope = ready_scope.where('priority >= ?', Worker.min_priority) if Worker.min_priority + ready_scope = ready_scope.where('priority <= ?', Worker.max_priority) if Worker.max_priority + ready_scope = ready_scope.where(:queue => Worker.queues) if Worker.queues.any? + ready_scope = ready_scope.by_priority + + now = self.db_time_now + + # Optimizations for faster lookups on some common databases + case self.connection.adapter_name + when "PostgreSQL" + # Custom SQL required for PostgreSQL because postgres does not support UPDATE...LIMIT + # 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) + # Note: active_record would attempt to generate UPDATE...LIMIT like sql for postgres if we use a .limit() filter, but it would not use + # 'FOR UPDATE' and we would have many locking conflicts + quoted_table_name = self.connection.quote_table_name(self.table_name) + subquery_sql = ready_scope.limit(1).lock(true).select('id').to_sql + reserved = self.find_by_sql(["UPDATE #{quoted_table_name} SET locked_at = ?, locked_by = ? WHERE id IN (#{subquery_sql}) RETURNING *", now, worker.name]) + reserved[0] + when "MySQL", "Mysql2" + # This works on MySQL and possibly some other DBs that support UPDATE...LIMIT. It uses separate queries to lock and return the job + count = ready_scope.limit(1).update_all(:locked_at => now, :locked_by => worker.name) + return nil if count == 0 + self.where(:locked_at => now, :locked_by => worker.name, :failed_at => nil).first + when "MSSQL", "Teradata" + # The MSSQL driver doesn't generate a limit clause when update_all is called directly + subsubquery_sql = ready_scope.limit(1).to_sql + # select("id") doesn't generate a subquery, so force a subquery + subquery_sql = "SELECT id FROM (#{subsubquery_sql}) AS x" + quoted_table_name = self.connection.quote_table_name(self.table_name) + sql = ["UPDATE #{quoted_table_name} SET locked_at = ?, locked_by = ? WHERE id IN (#{subquery_sql})", now, worker.name] + count = self.connection.execute(sanitize_sql(sql)) + return nil if count == 0 + # MSSQL JDBC doesn't support OUTPUT INSERTED.* for returning a result set, so query locked row + self.where(:locked_at => now, :locked_by => worker.name, :failed_at => nil).first + else + # This is our old fashion, tried and true, but slower lookup + ready_scope.limit(worker.read_ahead).detect do |job| + count = ready_scope.where(:id => job.id).update_all(:locked_at => now, :locked_by => worker.name) + count == 1 && job.reload + end + end + end + + # Get the current time (GMT or local depending on DB) + # Note: This does not ping the DB to get the time, so all your clients + # must have syncronized clocks. + def self.db_time_now + if Time.zone + Time.zone.now + elsif ::ActiveRecord::Base.default_timezone == :utc + Time.now.utc + else + Time.now + end + end + + def reload(*args) + reset + super + end + end + end + end +end diff --git a/vendor/plugins/delayed_job_active_record/lib/delayed_job_active_record.rb b/vendor/plugins/delayed_job_active_record/lib/delayed_job_active_record.rb new file mode 100644 index 0000000..08b8723 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/lib/delayed_job_active_record.rb @@ -0,0 +1,5 @@ +require 'active_record' +require 'delayed_job' +require 'delayed/backend/active_record' + +Delayed::Worker.backend = :active_record diff --git a/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/active_record_generator.rb b/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/active_record_generator.rb new file mode 100644 index 0000000..762ac26 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/active_record_generator.rb @@ -0,0 +1,22 @@ +require 'generators/delayed_job/delayed_job_generator' +require 'generators/delayed_job/next_migration_version' +require 'rails/generators/migration' +require 'rails/generators/active_record' + +# Extend the DelayedJobGenerator so that it creates an AR migration +module DelayedJob + class ActiveRecordGenerator < ::DelayedJobGenerator + include Rails::Generators::Migration + extend NextMigrationVersion + + self.source_paths << File.join(File.dirname(__FILE__), 'templates') + + def create_migration_file + migration_template 'migration.rb', 'db/migrate/create_delayed_jobs.rb' + end + + def self.next_migration_number dirname + ActiveRecord::Generators::Base.next_migration_number dirname + end + end +end diff --git a/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/next_migration_version.rb b/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/next_migration_version.rb new file mode 100644 index 0000000..e48ed62 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/next_migration_version.rb @@ -0,0 +1,14 @@ +module DelayedJob + module NextMigrationVersion + # while methods have moved around this has been the implementation + # since ActiveRecord 3.0 + def next_migration_number(dirname) + next_migration_number = current_migration_number(dirname) + 1 + if ActiveRecord::Base.timestamped_migrations + [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max + else + "%.3d" % next_migration_number + end + end + end +end diff --git a/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/templates/migration.rb b/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/templates/migration.rb new file mode 100644 index 0000000..ec0dd93 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/templates/migration.rb @@ -0,0 +1,22 @@ +class CreateDelayedJobs < ActiveRecord::Migration + def self.up + create_table :delayed_jobs, :force => true do |table| + table.integer :priority, :default => 0, :null => false # Allows some jobs to jump to the front of the queue + table.integer :attempts, :default => 0, :null => false # Provides for retries, but still fail eventually. + table.text :handler, :null => false # YAML-encoded string of the object that will do work + table.text :last_error # reason for last failure (See Note below) + table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. + table.datetime :locked_at # Set when a client is working on this object + table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) + table.string :locked_by # Who is working on this object (if locked) + table.string :queue # The name of the queue this job is in + table.timestamps + end + + add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority' + end + + def self.down + drop_table :delayed_jobs + end +end diff --git a/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/templates/upgrade_migration.rb b/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/templates/upgrade_migration.rb new file mode 100644 index 0000000..072c8d4 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/templates/upgrade_migration.rb @@ -0,0 +1,9 @@ +class AddQueueToDelayedJobs < ActiveRecord::Migration + def self.up + add_column :delayed_jobs, :queue, :string + end + + def self.down + remove_column :delayed_jobs, :queue + end +end diff --git a/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/upgrade_generator.rb b/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/upgrade_generator.rb new file mode 100644 index 0000000..c62d4b8 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/lib/generators/delayed_job/upgrade_generator.rb @@ -0,0 +1,22 @@ +require 'generators/delayed_job/delayed_job_generator' +require 'generators/delayed_job/next_migration_version' +require 'rails/generators/migration' +require 'rails/generators/active_record' + +# Extend the DelayedJobGenerator so that it creates an AR migration +module DelayedJob + class UpgradeGenerator < ::DelayedJobGenerator + include Rails::Generators::Migration + extend NextMigrationVersion + + self.source_paths << File.join(File.dirname(__FILE__), 'templates') + + def create_migration_file + migration_template 'upgrade_migration.rb', 'db/migrate/add_queue_to_delayed_jobs.rb' + end + + def self.next_migration_number dirname + ActiveRecord::Generators::Base.next_migration_number dirname + end + end +end diff --git a/vendor/plugins/delayed_job_active_record/spec/database.yml b/vendor/plugins/delayed_job_active_record/spec/database.yml new file mode 100644 index 0000000..6f56eb4 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/spec/database.yml @@ -0,0 +1,14 @@ +mysql: + adapter: mysql + database: delayed_job_test + username: root + encoding: utf8 + +postgresql: + adapter: postgresql + database: delayed_job_test + username: postgres + +sqlite3: + adapter: sqlite3 + database: ":memory:" diff --git a/vendor/plugins/delayed_job_active_record/spec/delayed/backend/active_record_spec.rb b/vendor/plugins/delayed_job_active_record/spec/delayed/backend/active_record_spec.rb new file mode 100644 index 0000000..293fdc9 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/spec/delayed/backend/active_record_spec.rb @@ -0,0 +1,81 @@ +require 'helper' +require 'delayed/backend/active_record' + +describe Delayed::Backend::ActiveRecord::Job do + it_behaves_like 'a delayed_job backend' + + context "db_time_now" do + after do + Time.zone = nil + ActiveRecord::Base.default_timezone = :local + end + + it "returns time in current time zone if set" do + Time.zone = 'Eastern Time (US & Canada)' + expect(%(EST EDT)).to include(Delayed::Job.db_time_now.zone) + end + + it "returns UTC time if that is the AR default" do + Time.zone = nil + ActiveRecord::Base.default_timezone = :utc + expect(Delayed::Backend::ActiveRecord::Job.db_time_now.zone).to eq 'UTC' + end + + it "returns local time if that is the AR default" do + Time.zone = 'Central Time (US & Canada)' + ActiveRecord::Base.default_timezone = :local + expect(%w(CST CDT)).to include(Delayed::Backend::ActiveRecord::Job.db_time_now.zone) + end + end + + describe "after_fork" do + it "calls reconnect on the connection" do + ActiveRecord::Base.should_receive(:establish_connection) + Delayed::Backend::ActiveRecord::Job.after_fork + end + end + + describe "enqueue" do + it "allows enqueue hook to modify job at DB level" do + later = described_class.db_time_now + 20.minutes + job = Delayed::Backend::ActiveRecord::Job.enqueue :payload_object => EnqueueJobMod.new + expect(Delayed::Backend::ActiveRecord::Job.find(job.id).run_at).to be_within(1).of(later) + end + end + + if ::ActiveRecord::VERSION::MAJOR < 4 || defined?(::ActiveRecord::MassAssignmentSecurity) + context "ActiveRecord::Base.send(:attr_accessible, nil)" do + before do + Delayed::Backend::ActiveRecord::Job.send(:attr_accessible, nil) + end + + after do + Delayed::Backend::ActiveRecord::Job.send(:attr_accessible, *Delayed::Backend::ActiveRecord::Job.new.attributes.keys) + end + + it "is still accessible" do + job = Delayed::Backend::ActiveRecord::Job.enqueue :payload_object => EnqueueJobMod.new + expect(Delayed::Backend::ActiveRecord::Job.find(job.id).handler).to_not be_blank + end + end + end + + context "ActiveRecord::Base.table_name_prefix" do + it "when prefix is not set, use 'delayed_jobs' as table name" do + ::ActiveRecord::Base.table_name_prefix = nil + Delayed::Backend::ActiveRecord::Job.set_delayed_job_table_name + + expect(Delayed::Backend::ActiveRecord::Job.table_name).to eq 'delayed_jobs' + end + + it "when prefix is set, prepend it before default table name" do + ::ActiveRecord::Base.table_name_prefix = 'custom_' + Delayed::Backend::ActiveRecord::Job.set_delayed_job_table_name + + expect(Delayed::Backend::ActiveRecord::Job.table_name).to eq 'custom_delayed_jobs' + + ::ActiveRecord::Base.table_name_prefix = nil + Delayed::Backend::ActiveRecord::Job.set_delayed_job_table_name + end + end +end diff --git a/vendor/plugins/delayed_job_active_record/spec/delayed/serialization/active_record_spec.rb b/vendor/plugins/delayed_job_active_record/spec/delayed/serialization/active_record_spec.rb new file mode 100644 index 0000000..81e0ab8 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/spec/delayed/serialization/active_record_spec.rb @@ -0,0 +1,15 @@ +require 'helper' + +describe ActiveRecord do + it "loads classes with non-default primary key" do + expect { + YAML.load(Story.create.to_yaml) + }.not_to raise_error + end + + it "loads classes even if not in default scope" do + expect { + YAML.load(Story.create(:scoped => false).to_yaml) + }.not_to raise_error + end +end diff --git a/vendor/plugins/delayed_job_active_record/spec/helper.rb b/vendor/plugins/delayed_job_active_record/spec/helper.rb new file mode 100644 index 0000000..95894e9 --- /dev/null +++ b/vendor/plugins/delayed_job_active_record/spec/helper.rb @@ -0,0 +1,69 @@ +require 'simplecov' +require 'coveralls' + +SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ + SimpleCov::Formatter::HTMLFormatter, + Coveralls::SimpleCov::Formatter +] +SimpleCov.start + +require 'logger' +require 'rspec' + +begin + require 'protected_attributes' +rescue LoadError +end +require 'delayed_job_active_record' +require 'delayed/backend/shared_spec' + +Delayed::Worker.logger = Logger.new('/tmp/dj.log') +ENV['RAILS_ENV'] = 'test' + +db_adapter, gemfile = ENV["ADAPTER"], ENV["BUNDLE_GEMFILE"] +db_adapter ||= gemfile && gemfile[%r(gemfiles/(.*?)/)] && $1 +db_adapter ||= 'sqlite3' + +config = YAML.load(File.read('spec/database.yml')) +ActiveRecord::Base.establish_connection config[db_adapter] +ActiveRecord::Base.logger = Delayed::Worker.logger +ActiveRecord::Migration.verbose = false + +ActiveRecord::Schema.define do + create_table :delayed_jobs, :force => true do |table| + table.integer :priority, :default => 0 + table.integer :attempts, :default => 0 + table.text :handler + table.text :last_error + table.datetime :run_at + table.datetime :locked_at + table.datetime :failed_at + table.string :locked_by + table.string :queue + table.timestamps + end + + add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority' + + create_table :stories, :primary_key => :story_id, :force => true do |table| + table.string :text + table.boolean :scoped, :default => true + end +end + +# Purely useful for test cases... +class Story < ActiveRecord::Base + if ::ActiveRecord::VERSION::MAJOR < 4 && ActiveRecord::VERSION::MINOR < 2 + set_primary_key :story_id + else + self.primary_key = :story_id + end + def tell; text; end + def whatever(n, _); tell*n; end + default_scope { where(:scoped => true) } + + handle_asynchronously :whatever +end + +# Add this directory so the ActiveSupport autoloading works +ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__) -- libgit2 0.21.2