From b892d20c610af78a94ba6c9488092ef76845242a Mon Sep 17 00:00:00 2001 From: Potapov Sergey Date: Sun, 16 Feb 2014 14:40:23 +0200 Subject: Do not try to write timestamps if they are missing #8813 --- activerecord/CHANGELOG.md | 6 ++++++ activerecord/lib/active_record/timestamp.rb | 2 +- activerecord/test/cases/timestamp_test.rb | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index f1f9cf1ffd..460eeb5dfd 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Do not try to write timestamps when a table has no timestamps columns. + + Fixes #8813. + + *Sergey Potapov* + * Properly detect if a connection is still active before using it in multi-threaded environments. diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 7178bed560..271f58cbe0 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -48,7 +48,7 @@ module ActiveRecord current_time = current_time_from_proper_timezone all_timestamp_attributes.each do |column| - if respond_to?(column) && respond_to?("#{column}=") && self.send(column).nil? + if attributes.has_key?(column.to_s) && self.send(column).nil? write_attribute(column.to_s, current_time) end end diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index 717e0e1866..8195e8a08a 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -9,11 +9,26 @@ require 'models/task' class TimestampTest < ActiveRecord::TestCase fixtures :developers, :owners, :pets, :toys, :cars, :tasks + # Model with timestamp attribute accessors, but without timestamps columns. + class TimestampAttributePost < ActiveRecord::Base + attr_accessor :created_at, :updated_at + end + + def setup @developer = Developer.first @owner = Owner.first @developer.update_columns(updated_at: Time.now.prev_month) @previously_updated_at = @developer.updated_at + + @connection = ActiveRecord::Base.connection + @connection.create_table 'timestamp_attribute_posts', :force => true do |t| + # Must have no timestamps + end + end + + def teardown + @connection.execute 'drop table if exists timestamp_attribute_posts' end def test_saving_a_changed_record_updates_its_timestamp @@ -379,4 +394,11 @@ class TimestampTest < ActiveRecord::TestCase toy = Toy.first assert_equal [:created_at, :updated_at], toy.send(:all_timestamp_attributes_in_model) end + + def test_do_not_write_timestamps_on_save_if_they_are_not_attributes + post = TimestampAttributePost.new + assert_nothing_raised ActiveModel::MissingAttributeError do + post.run_callbacks :save + end + end end -- cgit v1.2.3 From a832bc387bd80760f7a362664115f2e78085838e Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 18 May 2014 11:44:28 +0200 Subject: Skeleton gem --- CHANGELOG.md | 1 + Gemfile | 3 +++ Gemfile.lock | 27 +++++++++++++++++++++++++++ MIT-LICENSE | 21 +++++++++++++++++++++ README.rdoc | 37 +++++++++++++++++++++++++++++++++++++ activejob.gemspec | 20 ++++++++++++++++++++ lib/active_job.rb | 33 +++++++++++++++++++++++++++++++++ lib/active_job/base.rb | 4 ++++ lib/active_job/gem_version.rb | 15 +++++++++++++++ lib/active_job/version.rb | 8 ++++++++ test/cases/queuing_test.rb | 7 +++++++ test/helper.rb | 8 ++++++++ 12 files changed, 184 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 MIT-LICENSE create mode 100644 README.rdoc create mode 100644 activejob.gemspec create mode 100644 lib/active_job.rb create mode 100644 lib/active_job/base.rb create mode 100644 lib/active_job/gem_version.rb create mode 100644 lib/active_job/version.rb create mode 100644 test/cases/queuing_test.rb create mode 100644 test/helper.rb diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..b04883413e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +* Started project. \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000000..cd8aa9e04c --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gemspec \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000000..d4b8ae3df8 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,27 @@ +PATH + remote: . + specs: + activejob (4.2.0.alpha) + activesupport (>= 4.1.0) + +GEM + remote: https://rubygems.org/ + specs: + activesupport (4.1.1) + i18n (~> 0.6, >= 0.6.9) + json (~> 1.7, >= 1.7.7) + minitest (~> 5.1) + thread_safe (~> 0.1) + tzinfo (~> 1.1) + i18n (0.6.9) + json (1.8.1) + minitest (5.3.4) + thread_safe (0.3.3) + tzinfo (1.1.0) + thread_safe (~> 0.1) + +PLATFORMS + ruby + +DEPENDENCIES + activejob! diff --git a/MIT-LICENSE b/MIT-LICENSE new file mode 100644 index 0000000000..8b1e97b776 --- /dev/null +++ b/MIT-LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2014 David Heinemeier Hansson + +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 PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL 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/README.rdoc b/README.rdoc new file mode 100644 index 0000000000..5137766226 --- /dev/null +++ b/README.rdoc @@ -0,0 +1,37 @@ += Active Job -- Make work happen later + +Active Job is a framework for declaring jobs and making them run on a variety +of queueing backends. These jobs can be everything from regularly scheduled +clean-ups, billing charges, or mailings. Anything that can be chopped up into +small units of work and run in parallel, really. + +It also serves as the backend for ActionMailer's #deliver_later functionality +that makes it easy to turn any mailing into a job for running later. That's +one of the most common jobs in a modern web application: Sending emails outside +of the request-response cycle, so the user doesn't have to wait on it. + + +== Under development as a gem, targeted for Rails inclusion + +Active Job is currently being developed in a separate repository until it's +ready to be merged in with Rails. The current plan is to have Active Job +be part of the Rails 4.2 release, but plans may change depending on when +this framework stabilizes and feels ready. + + +== Download and installation + +The latest version of Active Job can be installed with RubyGems: + + % [sudo] gem install activejob + +Source code can be downloaded as part of the Rails project on GitHub + +* https://github.com/rails/activejob + + +== License + +Active Job is released under the MIT license: + +* http://www.opensource.org/licenses/MIT diff --git a/activejob.gemspec b/activejob.gemspec new file mode 100644 index 0000000000..201f3efb31 --- /dev/null +++ b/activejob.gemspec @@ -0,0 +1,20 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'activejob' + s.version = '4.2.0.alpha' + s.summary = 'Job framework with pluggable queues (will be part of Rails).' + s.description = 'Declare job classes that can be run by a variety of queueing backends.' + + s.required_ruby_version = '>= 1.9.3' + + s.license = 'MIT' + + s.author = 'David Heinemeier Hansson' + s.email = 'david@loudthinking.com' + s.homepage = 'http://www.rubyonrails.org' + + s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.rdoc', 'lib/**/*'] + s.require_path = 'lib' + + s.add_dependency 'activesupport', '>= 4.1.0' +end diff --git a/lib/active_job.rb b/lib/active_job.rb new file mode 100644 index 0000000000..ba18cc093a --- /dev/null +++ b/lib/active_job.rb @@ -0,0 +1,33 @@ +#-- +# Copyright (c) 2014 David Heinemeier Hansson +# +# 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 PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL 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. +#++ + +require 'active_support' +require 'active_support/rails' + +require 'active_job/version' + +module ActiveJob + extend ActiveSupport::Autoload + + autoload :Base +end \ No newline at end of file diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb new file mode 100644 index 0000000000..f2c76c1811 --- /dev/null +++ b/lib/active_job/base.rb @@ -0,0 +1,4 @@ +module ActiveJob + class Base + end +end \ No newline at end of file diff --git a/lib/active_job/gem_version.rb b/lib/active_job/gem_version.rb new file mode 100644 index 0000000000..c166020b28 --- /dev/null +++ b/lib/active_job/gem_version.rb @@ -0,0 +1,15 @@ +module ActiveJob + # Returns the version of the currently loaded ActiveJob as a Gem::Version + def self.gem_version + Gem::Version.new VERSION::STRING + end + + module VERSION + MAJOR = 4 + MINOR = 2 + TINY = 0 + PRE = "alpha" + + STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") + end +end diff --git a/lib/active_job/version.rb b/lib/active_job/version.rb new file mode 100644 index 0000000000..0add9779d9 --- /dev/null +++ b/lib/active_job/version.rb @@ -0,0 +1,8 @@ +require_relative 'gem_version' + +module ActiveJob + # Returns the version of the currently loaded ActiveRecord as a Gem::Version + def self.version + gem_version + end +end diff --git a/test/cases/queuing_test.rb b/test/cases/queuing_test.rb new file mode 100644 index 0000000000..5f1b35fee5 --- /dev/null +++ b/test/cases/queuing_test.rb @@ -0,0 +1,7 @@ +require 'helper' + +class QueuingTest < ActiveSupport::TestCase + test 'the truth' do + assert true + end +end diff --git a/test/helper.rb b/test/helper.rb new file mode 100644 index 0000000000..4048ddbd3f --- /dev/null +++ b/test/helper.rb @@ -0,0 +1,8 @@ +require 'bundler' +Bundler.setup + +$LOAD_PATH << File.dirname(__FILE__ + "/../lib") + +require 'active_job' + +require 'active_support/testing/autorun' -- cgit v1.2.3 From 60d13d638ecc9083a897cffafe4230f134303c4f Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 18 May 2014 12:30:57 +0200 Subject: Run tests through Rake --- Rakefile | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Rakefile diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000000..7e9f53ef41 --- /dev/null +++ b/Rakefile @@ -0,0 +1,27 @@ +dir = File.dirname(__FILE__) + +require 'rake/testtask' + +task :default => :test + +Rake::TestTask.new do |t| + t.libs << "test" + t.test_files = Dir.glob("#{dir}/test/cases/**/*_test.rb").sort + t.warning = true + t.verbose = true +end + +require 'rubygems/package_task' + +spec = eval(File.read("#{dir}/activejob.gemspec")) + +Gem::PackageTask.new(spec) do |p| + p.gem_spec = spec +end + +desc "Release to rubygems" +task :release => :package do + require 'rake/gemcutter' + Rake::Gemcutter::Tasks.new(spec).define + Rake::Task['gem:push'].invoke +end -- cgit v1.2.3 From 08a2ba99e0bdecd0835e73ec8945dcaf7020f127 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 18 May 2014 12:32:22 +0200 Subject: Simplest job and inline queue --- lib/active_job/base.rb | 7 +++++++ lib/active_job/queue_adapters/inline_queue.rb | 11 +++++++++++ test/cases/queuing_test.rb | 16 ++++++++++++++-- test/jobs/hello_job.rb | 5 +++++ 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 lib/active_job/queue_adapters/inline_queue.rb create mode 100644 test/jobs/hello_job.rb diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index f2c76c1811..a404355026 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,4 +1,11 @@ +require 'active_job/queue_adapters/inline_queue' + module ActiveJob class Base + class << self + def enqueue(*args) + ActiveJob::QueueAdapters::InlineQueue.queue self, *args + end + end end end \ No newline at end of file diff --git a/lib/active_job/queue_adapters/inline_queue.rb b/lib/active_job/queue_adapters/inline_queue.rb new file mode 100644 index 0000000000..dd80c6a5a3 --- /dev/null +++ b/lib/active_job/queue_adapters/inline_queue.rb @@ -0,0 +1,11 @@ +module ActiveJob + module QueueAdapters + class InlineQueue + class << self + def queue(job, *args) + job.perform *args + end + end + end + end +end \ No newline at end of file diff --git a/test/cases/queuing_test.rb b/test/cases/queuing_test.rb index 5f1b35fee5..b6180a23dd 100644 --- a/test/cases/queuing_test.rb +++ b/test/cases/queuing_test.rb @@ -1,7 +1,19 @@ require 'helper' +require 'jobs/hello_job' + class QueuingTest < ActiveSupport::TestCase - test 'the truth' do - assert true + setup do + $BUFFER = [] + end + + test 'run queued job' do + HelloJob.enqueue + assert_equal "David says hello", $BUFFER.pop + end + + test 'run queued job with parameters' do + HelloJob.enqueue "Jamie" + assert_equal "Jamie says hello", $BUFFER.pop end end diff --git a/test/jobs/hello_job.rb b/test/jobs/hello_job.rb new file mode 100644 index 0000000000..d0e65f9674 --- /dev/null +++ b/test/jobs/hello_job.rb @@ -0,0 +1,5 @@ +class HelloJob < ActiveJob::Base + def self.perform(greeter = "David") + $BUFFER << "#{greeter} says hello" + end +end -- cgit v1.2.3 From 30973e35a0271ed0cfab524a017a0e77d34352b6 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 18 May 2014 13:10:34 +0200 Subject: Rename to InlineAdapter to match *Adapter form, even if the queue is embedded in there too --- lib/active_job/queue_adapters/inline_adapter.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 lib/active_job/queue_adapters/inline_adapter.rb diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb new file mode 100644 index 0000000000..ac28670a29 --- /dev/null +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -0,0 +1,11 @@ +module ActiveJob + module QueueAdapters + class InlineAdapter + class << self + def queue(job, *args) + job.perform(*args) + end + end + end + end +end \ No newline at end of file -- cgit v1.2.3 From eed52c8808fceb0004e488f77f0bc6904f7aaddf Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 18 May 2014 13:11:00 +0200 Subject: Add ResqueAdapter and provide test infrastructure for the now multiple adapters --- Gemfile | 5 ++- Gemfile.lock | 22 +++++++++++++ Rakefile | 41 ++++++++++++++++++++++--- lib/active_job/base.rb | 7 +++-- lib/active_job/queue_adapters/inline_queue.rb | 11 ------- lib/active_job/queue_adapters/resque_adapter.rb | 13 ++++++++ test/adapters/inline.rb | 0 test/adapters/resque.rb | 2 ++ test/helper.rb | 1 + test/jobs/hello_job.rb | 2 ++ 10 files changed, 85 insertions(+), 19 deletions(-) delete mode 100644 lib/active_job/queue_adapters/inline_queue.rb create mode 100644 lib/active_job/queue_adapters/resque_adapter.rb create mode 100644 test/adapters/inline.rb create mode 100644 test/adapters/resque.rb diff --git a/Gemfile b/Gemfile index cd8aa9e04c..aad6cc5441 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,6 @@ source 'https://rubygems.org' -gemspec \ No newline at end of file +gemspec + +gem 'resque' + diff --git a/Gemfile.lock b/Gemfile.lock index d4b8ae3df8..213e0b8c10 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,12 +16,34 @@ GEM i18n (0.6.9) json (1.8.1) minitest (5.3.4) + mono_logger (1.1.0) + multi_json (1.9.3) + rack (1.5.2) + rack-protection (1.5.2) + rack + redis (3.0.7) + redis-namespace (1.4.1) + redis (~> 3.0.4) + resque (1.24.1) + mono_logger (~> 1.0) + multi_json (~> 1.0) + redis-namespace (~> 1.2) + sinatra (>= 0.9.2) + vegas (~> 0.1.2) + sinatra (1.4.4) + rack (~> 1.4) + rack-protection (~> 1.4) + tilt (~> 1.3, >= 1.3.4) thread_safe (0.3.3) + tilt (1.4.1) tzinfo (1.1.0) thread_safe (~> 0.1) + vegas (0.1.11) + rack (>= 1.0.0) PLATFORMS ruby DEPENDENCIES activejob! + resque diff --git a/Rakefile b/Rakefile index 7e9f53ef41..2c94fbcace 100644 --- a/Rakefile +++ b/Rakefile @@ -2,13 +2,44 @@ dir = File.dirname(__FILE__) require 'rake/testtask' +def run_without_aborting(*tasks) + errors = [] + + tasks.each do |task| + begin + Rake::Task[task].invoke + rescue Exception + errors << task + end + end + + abort "Errors running #{errors.join(', ')}" if errors.any? +end + + + task :default => :test -Rake::TestTask.new do |t| - t.libs << "test" - t.test_files = Dir.glob("#{dir}/test/cases/**/*_test.rb").sort - t.warning = true - t.verbose = true +desc 'Run all adapter tests' +task :test do + tasks = %w(test_inline test_resque) + run_without_aborting(*tasks) +end + + +%w( inline resque ).each do |adapter| + Rake::TestTask.new("test_#{adapter}") do |t| + t.libs << 'test' + t.test_files = FileList['test/cases/**/*_test.rb'] + t.verbose = true + end + + namespace adapter do + task :test => "test_#{adapter}" + task(:env) { ENV['AJADAPTER'] = adapter } + end + + task "test_#{adapter}" => "#{adapter}:env" end require 'rubygems/package_task' diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index a404355026..e18c9d4309 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,10 +1,13 @@ -require 'active_job/queue_adapters/inline_queue' +require 'active_job/queue_adapters/inline_adapter' +require 'active_job/queue_adapters/resque_adapter' module ActiveJob class Base + cattr_accessor(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter } + class << self def enqueue(*args) - ActiveJob::QueueAdapters::InlineQueue.queue self, *args + queue_adapter.queue self, *args end end end diff --git a/lib/active_job/queue_adapters/inline_queue.rb b/lib/active_job/queue_adapters/inline_queue.rb deleted file mode 100644 index dd80c6a5a3..0000000000 --- a/lib/active_job/queue_adapters/inline_queue.rb +++ /dev/null @@ -1,11 +0,0 @@ -module ActiveJob - module QueueAdapters - class InlineQueue - class << self - def queue(job, *args) - job.perform *args - end - end - end - end -end \ No newline at end of file diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb new file mode 100644 index 0000000000..4155cd5bf9 --- /dev/null +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -0,0 +1,13 @@ +require 'resque' + +module ActiveJob + module QueueAdapters + class ResqueAdapter + class << self + def queue(job, *args) + Resque.enqueue(job, *args) + end + end + end + end +end \ No newline at end of file diff --git a/test/adapters/inline.rb b/test/adapters/inline.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/adapters/resque.rb b/test/adapters/resque.rb new file mode 100644 index 0000000000..7431d2d742 --- /dev/null +++ b/test/adapters/resque.rb @@ -0,0 +1,2 @@ +ActiveJob::Base.queue_adapter = ActiveJob::QueueAdapters::ResqueAdapter +Resque.inline = true diff --git a/test/helper.rb b/test/helper.rb index 4048ddbd3f..df0a2a53f8 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -4,5 +4,6 @@ Bundler.setup $LOAD_PATH << File.dirname(__FILE__ + "/../lib") require 'active_job' +require "adapters/#{ENV['AJADAPTER'] || 'inline'}" require 'active_support/testing/autorun' diff --git a/test/jobs/hello_job.rb b/test/jobs/hello_job.rb index d0e65f9674..469123fe0a 100644 --- a/test/jobs/hello_job.rb +++ b/test/jobs/hello_job.rb @@ -1,4 +1,6 @@ class HelloJob < ActiveJob::Base + @queue = :greetings + def self.perform(greeter = "David") $BUFFER << "#{greeter} says hello" end -- cgit v1.2.3 From b32fdd5731aa0a6b8ed77305f78aba17a730b8e8 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 18 May 2014 16:24:30 +0200 Subject: Queue naming with a base, which requires a JobWrapper to comply to Resques expectation of a class variable --- lib/active_job/base.rb | 10 +++++-- lib/active_job/job_wrappers/resque_wrapper.rb | 37 +++++++++++++++++++++++++ lib/active_job/queue_adapters/resque_adapter.rb | 3 +- test/cases/queue_naming_test.rb | 17 ++++++++++++ test/jobs/hello_job.rb | 2 -- 5 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 lib/active_job/job_wrappers/resque_wrapper.rb create mode 100644 test/cases/queue_naming_test.rb diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index e18c9d4309..341ea6158b 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -3,12 +3,18 @@ require 'active_job/queue_adapters/resque_adapter' module ActiveJob class Base - cattr_accessor(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter } - + cattr_accessor(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter } + cattr_accessor(:queue_base_name) { "active_jobs" } + cattr_accessor(:queue_name) { queue_base_name } + class << self def enqueue(*args) queue_adapter.queue self, *args end + + def queue_as(part_name) + self.queue_name = "#{queue_base_name}_#{part_name}" + end end end end \ No newline at end of file diff --git a/lib/active_job/job_wrappers/resque_wrapper.rb b/lib/active_job/job_wrappers/resque_wrapper.rb new file mode 100644 index 0000000000..c34ea99f3c --- /dev/null +++ b/lib/active_job/job_wrappers/resque_wrapper.rb @@ -0,0 +1,37 @@ +require 'resque' + +require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/array/access' +require 'active_support/core_ext/string/inflections' + + +module ActiveJob + module JobWrappers + class ResqueWrapper + class << self + def wrap(job, args) + [ new(job), *args.prepend(job) ] + end + + def perform(*args) + unwrapped_job = args.first.constantize + + if args.many? + unwrapped_job.perform *args.from(1) + else + unwrapped_job.perform + end + end + end + + + def initialize(job) + @queue = job.queue_name + end + + def to_s + self.class.to_s + end + end + end +end \ No newline at end of file diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index 4155cd5bf9..43a44d2a48 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -1,11 +1,12 @@ require 'resque' +require 'active_job/job_wrappers/resque_wrapper' module ActiveJob module QueueAdapters class ResqueAdapter class << self def queue(job, *args) - Resque.enqueue(job, *args) + Resque.enqueue *JobWrappers::ResqueWrapper.wrap(job, args) end end end diff --git a/test/cases/queue_naming_test.rb b/test/cases/queue_naming_test.rb new file mode 100644 index 0000000000..e171474686 --- /dev/null +++ b/test/cases/queue_naming_test.rb @@ -0,0 +1,17 @@ +require 'helper' +require 'jobs/hello_job' + +class QueueNamingTest < ActiveSupport::TestCase + test 'name derived from base' do + assert_equal "active_jobs", HelloJob.queue_name + end + + test 'name appended in job' do + begin + HelloJob.queue_as :greetings + assert_equal "active_jobs_greetings", HelloJob.queue_name + ensure + HelloJob.queue_name = HelloJob.queue_base_name + end + end +end diff --git a/test/jobs/hello_job.rb b/test/jobs/hello_job.rb index 469123fe0a..d0e65f9674 100644 --- a/test/jobs/hello_job.rb +++ b/test/jobs/hello_job.rb @@ -1,6 +1,4 @@ class HelloJob < ActiveJob::Base - @queue = :greetings - def self.perform(greeter = "David") $BUFFER << "#{greeter} says hello" end -- cgit v1.2.3 From 2ac1a021025ad4f5f85368b9a85dcf210e5797d6 Mon Sep 17 00:00:00 2001 From: Charlie Somerville Date: Mon, 19 May 2014 01:27:50 +1000 Subject: Clean up JobWrappers::ResqueWrapper.perform This is not only easier to read, but it'll also properly raise an ArgumentError rather than a NoMethodError when called with no arguments. It also allocates 4 fewer objects per call (8 down from 12), and is about 50% faster according to a quick benchmark. --- lib/active_job/job_wrappers/resque_wrapper.rb | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/active_job/job_wrappers/resque_wrapper.rb b/lib/active_job/job_wrappers/resque_wrapper.rb index c34ea99f3c..0db9cf144f 100644 --- a/lib/active_job/job_wrappers/resque_wrapper.rb +++ b/lib/active_job/job_wrappers/resque_wrapper.rb @@ -13,14 +13,8 @@ module ActiveJob [ new(job), *args.prepend(job) ] end - def perform(*args) - unwrapped_job = args.first.constantize - - if args.many? - unwrapped_job.perform *args.from(1) - else - unwrapped_job.perform - end + def perform(job_name, *args) + job_name.constantize.perform(*args) end end @@ -34,4 +28,4 @@ module ActiveJob end end end -end \ No newline at end of file +end -- cgit v1.2.3 From a712c07c7ff312aca9c72e9720baf59f779e0cc2 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Sun, 18 May 2014 19:02:02 +0000 Subject: Add Sidekiq adapter/wrapper fixes #3 --- Gemfile | 2 +- Rakefile | 4 ++-- lib/active_job/base.rb | 1 + lib/active_job/job_wrappers/sidekiq_wrapper.rb | 11 +++++++++++ lib/active_job/queue_adapters/sidekiq_adapter.rb | 14 ++++++++++++++ test/adapters/sidekiq.rb | 2 ++ 6 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 lib/active_job/job_wrappers/sidekiq_wrapper.rb create mode 100644 lib/active_job/queue_adapters/sidekiq_adapter.rb create mode 100644 test/adapters/sidekiq.rb diff --git a/Gemfile b/Gemfile index aad6cc5441..c0c7f2da83 100644 --- a/Gemfile +++ b/Gemfile @@ -3,4 +3,4 @@ source 'https://rubygems.org' gemspec gem 'resque' - +gem 'sidekiq' diff --git a/Rakefile b/Rakefile index 2c94fbcace..82522f4a89 100644 --- a/Rakefile +++ b/Rakefile @@ -22,12 +22,12 @@ task :default => :test desc 'Run all adapter tests' task :test do - tasks = %w(test_inline test_resque) + tasks = %w(test_inline test_resque test_sidekiq) run_without_aborting(*tasks) end -%w( inline resque ).each do |adapter| +%w( inline resque sidekiq ).each do |adapter| Rake::TestTask.new("test_#{adapter}") do |t| t.libs << 'test' t.test_files = FileList['test/cases/**/*_test.rb'] diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index 341ea6158b..d1fff27a3a 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,5 +1,6 @@ require 'active_job/queue_adapters/inline_adapter' require 'active_job/queue_adapters/resque_adapter' +require 'active_job/queue_adapters/sidekiq_adapter' module ActiveJob class Base diff --git a/lib/active_job/job_wrappers/sidekiq_wrapper.rb b/lib/active_job/job_wrappers/sidekiq_wrapper.rb new file mode 100644 index 0000000000..fb728ae0fd --- /dev/null +++ b/lib/active_job/job_wrappers/sidekiq_wrapper.rb @@ -0,0 +1,11 @@ +module ActiveJob + module JobWrappers + class SidekiqWrapper + include Sidekiq::Worker + + def perform(job_name, *args) + job_name.constantize.perform(*args) + end + end + end +end diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb new file mode 100644 index 0000000000..70c4377a23 --- /dev/null +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -0,0 +1,14 @@ +require 'sidekiq' +require 'active_job/job_wrappers/sidekiq_wrapper' + +module ActiveJob + module QueueAdapters + class SidekiqAdapter + class << self + def queue(job, *args) + JobWrappers::SidekiqWrapper.perform_async(job, *args) + end + end + end + end +end diff --git a/test/adapters/sidekiq.rb b/test/adapters/sidekiq.rb new file mode 100644 index 0000000000..74ce808d55 --- /dev/null +++ b/test/adapters/sidekiq.rb @@ -0,0 +1,2 @@ +require 'sidekiq/testing/inline' +ActiveJob::Base.queue_adapter = ActiveJob::QueueAdapters::SidekiqAdapter -- cgit v1.2.3 From 53f08f98686942a0335a1b5d5362dc442461bc2f Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Sun, 18 May 2014 19:11:03 +0000 Subject: Correct typo in version.rb --- lib/active_job/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_job/version.rb b/lib/active_job/version.rb index 0add9779d9..7e646fa3c4 100644 --- a/lib/active_job/version.rb +++ b/lib/active_job/version.rb @@ -1,7 +1,7 @@ require_relative 'gem_version' module ActiveJob - # Returns the version of the currently loaded ActiveRecord as a Gem::Version + # Returns the version of the currently loaded ActiveJob as a Gem::Version def self.version gem_version end -- cgit v1.2.3 From 68543de83612049e3254f7243b575039f102cd49 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Sun, 18 May 2014 19:20:06 +0000 Subject: Add Sucker Punch adapter/wrapper --- Gemfile | 1 + Rakefile | 4 ++-- lib/active_job/base.rb | 1 + lib/active_job/job_wrappers/sucker_punch_wrapper.rb | 11 +++++++++++ lib/active_job/queue_adapters/sucker_punch_adapter.rb | 14 ++++++++++++++ test/adapters/sucker_punch.rb | 2 ++ 6 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 lib/active_job/job_wrappers/sucker_punch_wrapper.rb create mode 100644 lib/active_job/queue_adapters/sucker_punch_adapter.rb create mode 100644 test/adapters/sucker_punch.rb diff --git a/Gemfile b/Gemfile index c0c7f2da83..e9b5ee9c8b 100644 --- a/Gemfile +++ b/Gemfile @@ -4,3 +4,4 @@ gemspec gem 'resque' gem 'sidekiq' +gem 'sucker_punch' \ No newline at end of file diff --git a/Rakefile b/Rakefile index 82522f4a89..7f38657160 100644 --- a/Rakefile +++ b/Rakefile @@ -22,12 +22,12 @@ task :default => :test desc 'Run all adapter tests' task :test do - tasks = %w(test_inline test_resque test_sidekiq) + tasks = %w(test_inline test_resque test_sidekiq test_sucker_punch) run_without_aborting(*tasks) end -%w( inline resque sidekiq ).each do |adapter| +%w( inline resque sidekiq sucker_punch).each do |adapter| Rake::TestTask.new("test_#{adapter}") do |t| t.libs << 'test' t.test_files = FileList['test/cases/**/*_test.rb'] diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index d1fff27a3a..6cce770466 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,6 +1,7 @@ require 'active_job/queue_adapters/inline_adapter' require 'active_job/queue_adapters/resque_adapter' require 'active_job/queue_adapters/sidekiq_adapter' +require 'active_job/queue_adapters/sucker_punch_adapter' module ActiveJob class Base diff --git a/lib/active_job/job_wrappers/sucker_punch_wrapper.rb b/lib/active_job/job_wrappers/sucker_punch_wrapper.rb new file mode 100644 index 0000000000..80648792ca --- /dev/null +++ b/lib/active_job/job_wrappers/sucker_punch_wrapper.rb @@ -0,0 +1,11 @@ +module ActiveJob + module JobWrappers + class SuckerPunchWrapper + include SuckerPunch::Job + + def perform(job_name, *args) + job_name.perform(*args) + end + end + end +end diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb new file mode 100644 index 0000000000..8191e5a1df --- /dev/null +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -0,0 +1,14 @@ +require 'sucker_punch' +require 'active_job/job_wrappers/sucker_punch_wrapper' + +module ActiveJob + module QueueAdapters + class SuckerPunchAdapter + class << self + def queue(job, *args) + JobWrappers::SuckerPunchWrapper.new.async.perform(job, *args) + end + end + end + end +end diff --git a/test/adapters/sucker_punch.rb b/test/adapters/sucker_punch.rb new file mode 100644 index 0000000000..a373552262 --- /dev/null +++ b/test/adapters/sucker_punch.rb @@ -0,0 +1,2 @@ +require 'sucker_punch/testing/inline' +ActiveJob::Base.queue_adapter = ActiveJob::QueueAdapters::SuckerPunchAdapter -- cgit v1.2.3 From e5feb8b764e8c4c377884fe6d3b736b415a923e1 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 18 May 2014 21:37:01 +0200 Subject: With dependencies for Sidekiq and Sucker Punch --- Gemfile.lock | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 213e0b8c10..51a3812ad9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -13,6 +13,9 @@ GEM minitest (~> 5.1) thread_safe (~> 0.1) tzinfo (~> 1.1) + celluloid (0.15.2) + timers (~> 1.1.0) + connection_pool (2.0.0) i18n (0.6.9) json (1.8.1) minitest (5.3.4) @@ -30,12 +33,21 @@ GEM redis-namespace (~> 1.2) sinatra (>= 0.9.2) vegas (~> 0.1.2) + sidekiq (3.0.2) + celluloid (>= 0.15.2) + connection_pool (>= 2.0.0) + json + redis (>= 3.0.6) + redis-namespace (>= 1.3.1) sinatra (1.4.4) rack (~> 1.4) rack-protection (~> 1.4) tilt (~> 1.3, >= 1.3.4) + sucker_punch (1.0.5) + celluloid (~> 0.15.2) thread_safe (0.3.3) tilt (1.4.1) + timers (1.1.0) tzinfo (1.1.0) thread_safe (~> 0.1) vegas (0.1.11) @@ -47,3 +59,5 @@ PLATFORMS DEPENDENCIES activejob! resque + sidekiq + sucker_punch -- cgit v1.2.3 From d3bc4499a26c58af81422a67f1fb21c405994756 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 18 May 2014 21:37:16 +0200 Subject: List adapters supported and wanted --- README.rdoc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.rdoc b/README.rdoc index 5137766226..8531988f33 100644 --- a/README.rdoc +++ b/README.rdoc @@ -11,6 +11,21 @@ one of the most common jobs in a modern web application: Sending emails outside of the request-response cycle, so the user doesn't have to wait on it. +== Supported queueing systems + +We currently have adapters for: + +* Resque 1.x +* Sidekiq +* Sucker Punch + +We would like to have adapters for: + +* Delayed Job +* beanstalkd +* rabbitmq + + == Under development as a gem, targeted for Rails inclusion Active Job is currently being developed in a separate repository until it's -- cgit v1.2.3 From 91461dc7a9f0aa879cfff503ea2fdb9a71e0277e Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Sun, 18 May 2014 20:59:12 +0000 Subject: Lazy-load adapters, fixes #6 --- lib/active_job/base.rb | 23 ++++++++++++++++---- lib/active_job/errors.rb | 12 +++++++++++ lib/active_job/job_wrappers/resque_wrapper.rb | 2 +- test/adapters/inline.rb | 1 + test/adapters/resque.rb | 2 +- test/adapters/sidekiq.rb | 2 +- test/adapters/sucker_punch.rb | 2 +- test/cases/adapter_test.rb | 31 +++++++++++++++++++++++++++ 8 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 lib/active_job/errors.rb create mode 100644 test/cases/adapter_test.rb diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index 6cce770466..f39dba6f5f 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,9 +1,9 @@ +require 'active_job/errors' require 'active_job/queue_adapters/inline_adapter' -require 'active_job/queue_adapters/resque_adapter' -require 'active_job/queue_adapters/sidekiq_adapter' -require 'active_job/queue_adapters/sucker_punch_adapter' +require 'active_support/core_ext/string/inflections' module ActiveJob + class Base cattr_accessor(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter } cattr_accessor(:queue_base_name) { "active_jobs" } @@ -13,10 +13,25 @@ module ActiveJob def enqueue(*args) queue_adapter.queue self, *args end - + def queue_as(part_name) self.queue_name = "#{queue_base_name}_#{part_name}" end + + def adapter=(adapter_name) + adapter_name = adapter_name.to_s + unless %w(inline resque sidekiq sucker_punch).include?(adapter_name) + fail ActiveJob::NotImplementedError + end + + begin + require_relative "queue_adapters/#{adapter_name}_adapter" + ActiveJob::Base.queue_adapter = "ActiveJob::QueueAdapters::#{adapter_name.camelize}Adapter".constantize + rescue + fail ActiveJob::Error.new("#{adapter_name} is missing") + end + end end + end end \ No newline at end of file diff --git a/lib/active_job/errors.rb b/lib/active_job/errors.rb new file mode 100644 index 0000000000..4fc3be6878 --- /dev/null +++ b/lib/active_job/errors.rb @@ -0,0 +1,12 @@ +module ActiveJob + + class NotImplementedError < ::NotImplementedError #:nodoc: + end + + class Error < ::StandardError #:nodoc: + def initialize(message = nil) + super(message) + end + end + +end \ No newline at end of file diff --git a/lib/active_job/job_wrappers/resque_wrapper.rb b/lib/active_job/job_wrappers/resque_wrapper.rb index 0db9cf144f..cbeee4fb1b 100644 --- a/lib/active_job/job_wrappers/resque_wrapper.rb +++ b/lib/active_job/job_wrappers/resque_wrapper.rb @@ -2,7 +2,7 @@ require 'resque' require 'active_support/core_ext/enumerable' require 'active_support/core_ext/array/access' -require 'active_support/core_ext/string/inflections' + module ActiveJob diff --git a/test/adapters/inline.rb b/test/adapters/inline.rb index e69de29bb2..131dd029b3 100644 --- a/test/adapters/inline.rb +++ b/test/adapters/inline.rb @@ -0,0 +1 @@ +ActiveJob::Base.adapter = :inline \ No newline at end of file diff --git a/test/adapters/resque.rb b/test/adapters/resque.rb index 7431d2d742..6e11f864c2 100644 --- a/test/adapters/resque.rb +++ b/test/adapters/resque.rb @@ -1,2 +1,2 @@ -ActiveJob::Base.queue_adapter = ActiveJob::QueueAdapters::ResqueAdapter +ActiveJob::Base.adapter = :resque Resque.inline = true diff --git a/test/adapters/sidekiq.rb b/test/adapters/sidekiq.rb index 74ce808d55..55153d0bc6 100644 --- a/test/adapters/sidekiq.rb +++ b/test/adapters/sidekiq.rb @@ -1,2 +1,2 @@ require 'sidekiq/testing/inline' -ActiveJob::Base.queue_adapter = ActiveJob::QueueAdapters::SidekiqAdapter +ActiveJob::Base.adapter = :sidekiq diff --git a/test/adapters/sucker_punch.rb b/test/adapters/sucker_punch.rb index a373552262..8ce9a902b6 100644 --- a/test/adapters/sucker_punch.rb +++ b/test/adapters/sucker_punch.rb @@ -1,2 +1,2 @@ require 'sucker_punch/testing/inline' -ActiveJob::Base.queue_adapter = ActiveJob::QueueAdapters::SuckerPunchAdapter +ActiveJob::Base.adapter = :sucker_punch diff --git a/test/cases/adapter_test.rb b/test/cases/adapter_test.rb new file mode 100644 index 0000000000..eac92fb27d --- /dev/null +++ b/test/cases/adapter_test.rb @@ -0,0 +1,31 @@ +require 'helper' + +class AdapterTest < ActiveSupport::TestCase + def setup + @old_adapter = ActiveJob::Base.queue_adapter + end + + test 'should load inline adapter' do + ActiveJob::Base.adapter = :inline + assert_equal ActiveJob::QueueAdapters::InlineAdapter, ActiveJob::Base.queue_adapter + end + + test 'should load resque adapter' do + ActiveJob::Base.adapter = :resque + assert_equal ActiveJob::QueueAdapters::ResqueAdapter, ActiveJob::Base.queue_adapter + end + + test 'should load sidekiq adapter' do + ActiveJob::Base.adapter = :sidekiq + assert_equal ActiveJob::QueueAdapters::SidekiqAdapter, ActiveJob::Base.queue_adapter + end + + test 'should load sucker punch adapter' do + ActiveJob::Base.adapter = :sucker_punch + assert_equal ActiveJob::QueueAdapters::SuckerPunchAdapter, ActiveJob::Base.queue_adapter + end + + def teardown + ActiveJob::Base.queue_adapter = @old_adapter + end +end -- cgit v1.2.3 From fd1e61adfc40275fe23fdcbc7796f7a72bf12fa3 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 09:47:55 +0200 Subject: Dont need the explicit error handling -- if the require fails, it will raise exactly the error we want to communicate anyway. Also use the load path, so we can allow plugins, rather than requre_relative --- lib/active_job/base.rb | 16 ++-------------- lib/active_job/errors.rb | 12 ------------ 2 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 lib/active_job/errors.rb diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index f39dba6f5f..86008d3c94 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,9 +1,7 @@ -require 'active_job/errors' require 'active_job/queue_adapters/inline_adapter' require 'active_support/core_ext/string/inflections' module ActiveJob - class Base cattr_accessor(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter } cattr_accessor(:queue_base_name) { "active_jobs" } @@ -19,19 +17,9 @@ module ActiveJob end def adapter=(adapter_name) - adapter_name = adapter_name.to_s - unless %w(inline resque sidekiq sucker_punch).include?(adapter_name) - fail ActiveJob::NotImplementedError - end - - begin - require_relative "queue_adapters/#{adapter_name}_adapter" - ActiveJob::Base.queue_adapter = "ActiveJob::QueueAdapters::#{adapter_name.camelize}Adapter".constantize - rescue - fail ActiveJob::Error.new("#{adapter_name} is missing") - end + require "active_job/queue_adapters/#{adapter_name}_adapter" + ActiveJob::Base.queue_adapter = "ActiveJob::QueueAdapters::#{adapter_name.to_s.camelize}Adapter".constantize end end - end end \ No newline at end of file diff --git a/lib/active_job/errors.rb b/lib/active_job/errors.rb deleted file mode 100644 index 4fc3be6878..0000000000 --- a/lib/active_job/errors.rb +++ /dev/null @@ -1,12 +0,0 @@ -module ActiveJob - - class NotImplementedError < ::NotImplementedError #:nodoc: - end - - class Error < ::StandardError #:nodoc: - def initialize(message = nil) - super(message) - end - end - -end \ No newline at end of file -- cgit v1.2.3 From d3ff144f89a1d42118f84f075fa3cc9b94fb190e Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Mon, 19 May 2014 00:47:35 +0300 Subject: Implemented delayed job --- Gemfile | 3 +- Gemfile.lock | 3 + Rakefile | 4 +- .../queue_adapters/delayed_job_adapter.rb | 13 +++ test/adapters/delayed_job.rb | 6 ++ test/cases/adapter_test.rb | 5 + test/helper.rb | 2 +- test/support/delayed_job/delayed/backend/test.rb | 113 +++++++++++++++++++++ .../delayed_job/delayed/serialization/test.rb | 0 9 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 lib/active_job/queue_adapters/delayed_job_adapter.rb create mode 100644 test/adapters/delayed_job.rb create mode 100644 test/support/delayed_job/delayed/backend/test.rb create mode 100644 test/support/delayed_job/delayed/serialization/test.rb diff --git a/Gemfile b/Gemfile index e9b5ee9c8b..f8c97a8cab 100644 --- a/Gemfile +++ b/Gemfile @@ -4,4 +4,5 @@ gemspec gem 'resque' gem 'sidekiq' -gem 'sucker_punch' \ No newline at end of file +gem 'sucker_punch' +gem 'delayed_job' diff --git a/Gemfile.lock b/Gemfile.lock index 51a3812ad9..7005cc4d46 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,6 +16,8 @@ GEM celluloid (0.15.2) timers (~> 1.1.0) connection_pool (2.0.0) + delayed_job (4.0.1) + activesupport (>= 3.0, < 4.2) i18n (0.6.9) json (1.8.1) minitest (5.3.4) @@ -58,6 +60,7 @@ PLATFORMS DEPENDENCIES activejob! + delayed_job resque sidekiq sucker_punch diff --git a/Rakefile b/Rakefile index 7f38657160..d0d837602a 100644 --- a/Rakefile +++ b/Rakefile @@ -22,12 +22,12 @@ task :default => :test desc 'Run all adapter tests' task :test do - tasks = %w(test_inline test_resque test_sidekiq test_sucker_punch) + tasks = %w(test_inline test_resque test_sidekiq test_sucker_punch test_delayed_job) run_without_aborting(*tasks) end -%w( inline resque sidekiq sucker_punch).each do |adapter| +%w( inline resque sidekiq sucker_punch delayed_job).each do |adapter| Rake::TestTask.new("test_#{adapter}") do |t| t.libs << 'test' t.test_files = FileList['test/cases/**/*_test.rb'] diff --git a/lib/active_job/queue_adapters/delayed_job_adapter.rb b/lib/active_job/queue_adapters/delayed_job_adapter.rb new file mode 100644 index 0000000000..33229dece4 --- /dev/null +++ b/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -0,0 +1,13 @@ +require 'delayed_job' + +module ActiveJob + module QueueAdapters + class DelayedJobAdapter + class << self + def queue(job, *args) + job.delay(queue: job.queue_name).perform(*args) + end + end + end + end +end diff --git a/test/adapters/delayed_job.rb b/test/adapters/delayed_job.rb new file mode 100644 index 0000000000..2055a6b6e6 --- /dev/null +++ b/test/adapters/delayed_job.rb @@ -0,0 +1,6 @@ +require 'delayed_job' +$LOAD_PATH << File.dirname(__FILE__) + "/../support/delayed_job" + +Delayed::Worker.delay_jobs = false +Delayed::Worker.backend = :test +ActiveJob::Base.adapter = :delayed_job diff --git a/test/cases/adapter_test.rb b/test/cases/adapter_test.rb index eac92fb27d..c51130bc15 100644 --- a/test/cases/adapter_test.rb +++ b/test/cases/adapter_test.rb @@ -25,6 +25,11 @@ class AdapterTest < ActiveSupport::TestCase assert_equal ActiveJob::QueueAdapters::SuckerPunchAdapter, ActiveJob::Base.queue_adapter end + test 'should load delayed_job adapter' do + ActiveJob::Base.adapter = :delayed_job + assert_equal ActiveJob::QueueAdapters::DelayedJobAdapter, ActiveJob::Base.queue_adapter + end + def teardown ActiveJob::Base.queue_adapter = @old_adapter end diff --git a/test/helper.rb b/test/helper.rb index df0a2a53f8..fc3e2642df 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -1,7 +1,7 @@ require 'bundler' Bundler.setup -$LOAD_PATH << File.dirname(__FILE__ + "/../lib") +$LOAD_PATH << File.dirname(__FILE__) + "/../lib" require 'active_job' require "adapters/#{ENV['AJADAPTER'] || 'inline'}" diff --git a/test/support/delayed_job/delayed/backend/test.rb b/test/support/delayed_job/delayed/backend/test.rb new file mode 100644 index 0000000000..b50ed36fc2 --- /dev/null +++ b/test/support/delayed_job/delayed/backend/test.rb @@ -0,0 +1,113 @@ +#copied from https://github.com/collectiveidea/delayed_job/blob/master/spec/delayed/backend/test.rb +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/test/support/delayed_job/delayed/serialization/test.rb b/test/support/delayed_job/delayed/serialization/test.rb new file mode 100644 index 0000000000..e69de29bb2 -- cgit v1.2.3 From 6e18a7469fb43714c8a37e6cf6ff94280cd12465 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Mon, 19 May 2014 11:47:12 +0300 Subject: Modified readme --- README.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 8531988f33..c83a5b2986 100644 --- a/README.rdoc +++ b/README.rdoc @@ -18,10 +18,10 @@ We currently have adapters for: * Resque 1.x * Sidekiq * Sucker Punch +* Delayed Job We would like to have adapters for: -* Delayed Job * beanstalkd * rabbitmq -- cgit v1.2.3 From c334bea36228319b3f624443f303447e48e29f3d Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 10:55:57 +0200 Subject: Extract QueueAdapter module for setting and looking up adapters --- lib/active_job/base.rb | 9 +++------ lib/active_job/queue_adapter.rb | 24 ++++++++++++++++++++++++ test/adapters/inline.rb | 2 +- test/adapters/resque.rb | 2 +- test/adapters/sidekiq.rb | 2 +- test/adapters/sucker_punch.rb | 2 +- test/cases/adapter_test.rb | 8 ++++---- 7 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 lib/active_job/queue_adapter.rb diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index 86008d3c94..d3b0fcffa8 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,9 +1,11 @@ +require 'active_job/queue_adapter' require 'active_job/queue_adapters/inline_adapter' require 'active_support/core_ext/string/inflections' module ActiveJob class Base - cattr_accessor(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter } + extend QueueAdapter + cattr_accessor(:queue_base_name) { "active_jobs" } cattr_accessor(:queue_name) { queue_base_name } @@ -15,11 +17,6 @@ module ActiveJob def queue_as(part_name) self.queue_name = "#{queue_base_name}_#{part_name}" end - - def adapter=(adapter_name) - require "active_job/queue_adapters/#{adapter_name}_adapter" - ActiveJob::Base.queue_adapter = "ActiveJob::QueueAdapters::#{adapter_name.to_s.camelize}Adapter".constantize - end end end end \ No newline at end of file diff --git a/lib/active_job/queue_adapter.rb b/lib/active_job/queue_adapter.rb new file mode 100644 index 0000000000..a1987a7dc3 --- /dev/null +++ b/lib/active_job/queue_adapter.rb @@ -0,0 +1,24 @@ +require 'active_job/queue_adapters/inline_adapter' +require 'active_support/core_ext/string/inflections' + +module ActiveJob + module QueueAdapter + mattr_reader(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter } + + def queue_adapter=(name_or_adapter) + if name_or_adapter.is_a?(Symbol) || name_or_adapter.is_a?(String) + adapter = load_adapter(name_or_adapter) + else + adapter = name_or_adapter + end + + @@queue_adapter = adapter + end + + private + def load_adapter(name) + require "active_job/queue_adapters/#{name}_adapter" + "ActiveJob::QueueAdapters::#{name.to_s.camelize}Adapter".constantize + end + end +end \ No newline at end of file diff --git a/test/adapters/inline.rb b/test/adapters/inline.rb index 131dd029b3..e0092552c4 100644 --- a/test/adapters/inline.rb +++ b/test/adapters/inline.rb @@ -1 +1 @@ -ActiveJob::Base.adapter = :inline \ No newline at end of file +ActiveJob::Base.queue_adapter = :inline \ No newline at end of file diff --git a/test/adapters/resque.rb b/test/adapters/resque.rb index 6e11f864c2..af7080358d 100644 --- a/test/adapters/resque.rb +++ b/test/adapters/resque.rb @@ -1,2 +1,2 @@ -ActiveJob::Base.adapter = :resque +ActiveJob::Base.queue_adapter = :resque Resque.inline = true diff --git a/test/adapters/sidekiq.rb b/test/adapters/sidekiq.rb index 55153d0bc6..cd9d2034de 100644 --- a/test/adapters/sidekiq.rb +++ b/test/adapters/sidekiq.rb @@ -1,2 +1,2 @@ require 'sidekiq/testing/inline' -ActiveJob::Base.adapter = :sidekiq +ActiveJob::Base.queue_adapter = :sidekiq diff --git a/test/adapters/sucker_punch.rb b/test/adapters/sucker_punch.rb index 8ce9a902b6..d2d1712946 100644 --- a/test/adapters/sucker_punch.rb +++ b/test/adapters/sucker_punch.rb @@ -1,2 +1,2 @@ require 'sucker_punch/testing/inline' -ActiveJob::Base.adapter = :sucker_punch +ActiveJob::Base.queue_adapter = :sucker_punch diff --git a/test/cases/adapter_test.rb b/test/cases/adapter_test.rb index eac92fb27d..f0ff2dfed2 100644 --- a/test/cases/adapter_test.rb +++ b/test/cases/adapter_test.rb @@ -6,22 +6,22 @@ class AdapterTest < ActiveSupport::TestCase end test 'should load inline adapter' do - ActiveJob::Base.adapter = :inline + ActiveJob::Base.queue_adapter = :inline assert_equal ActiveJob::QueueAdapters::InlineAdapter, ActiveJob::Base.queue_adapter end test 'should load resque adapter' do - ActiveJob::Base.adapter = :resque + ActiveJob::Base.queue_adapter = :resque assert_equal ActiveJob::QueueAdapters::ResqueAdapter, ActiveJob::Base.queue_adapter end test 'should load sidekiq adapter' do - ActiveJob::Base.adapter = :sidekiq + ActiveJob::Base.queue_adapter = :sidekiq assert_equal ActiveJob::QueueAdapters::SidekiqAdapter, ActiveJob::Base.queue_adapter end test 'should load sucker punch adapter' do - ActiveJob::Base.adapter = :sucker_punch + ActiveJob::Base.queue_adapter = :sucker_punch assert_equal ActiveJob::QueueAdapters::SuckerPunchAdapter, ActiveJob::Base.queue_adapter end -- cgit v1.2.3 From 935f53bd5ad2d71cfa18a91514184eea3fb83690 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 11:00:00 +0200 Subject: Fix for the new adapter setter --- test/adapters/delayed_job.rb | 2 +- test/cases/adapter_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/adapters/delayed_job.rb b/test/adapters/delayed_job.rb index 2055a6b6e6..f334437a9e 100644 --- a/test/adapters/delayed_job.rb +++ b/test/adapters/delayed_job.rb @@ -3,4 +3,4 @@ $LOAD_PATH << File.dirname(__FILE__) + "/../support/delayed_job" Delayed::Worker.delay_jobs = false Delayed::Worker.backend = :test -ActiveJob::Base.adapter = :delayed_job +ActiveJob::Base.queue_adapter = :delayed_job diff --git a/test/cases/adapter_test.rb b/test/cases/adapter_test.rb index cdc601b06a..05efdc33e9 100644 --- a/test/cases/adapter_test.rb +++ b/test/cases/adapter_test.rb @@ -26,7 +26,7 @@ class AdapterTest < ActiveSupport::TestCase end test 'should load delayed_job adapter' do - ActiveJob::Base.adapter = :delayed_job + ActiveJob::Base.queue_adapter = :delayed_job assert_equal ActiveJob::QueueAdapters::DelayedJobAdapter, ActiveJob::Base.queue_adapter end -- cgit v1.2.3 From 60d76c5651ce9cf62caa1e7e80f56b84ec2b6abe Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 11:04:23 +0200 Subject: Extract QueueName into its own module --- lib/active_job/base.rb | 17 ++++------------- lib/active_job/queue_name.rb | 10 ++++++++++ 2 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 lib/active_job/queue_name.rb diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index d3b0fcffa8..fe3d1677dd 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,22 +1,13 @@ require 'active_job/queue_adapter' -require 'active_job/queue_adapters/inline_adapter' -require 'active_support/core_ext/string/inflections' +require 'active_job/queue_name' module ActiveJob class Base extend QueueAdapter + extend QueueName - cattr_accessor(:queue_base_name) { "active_jobs" } - cattr_accessor(:queue_name) { queue_base_name } - - class << self - def enqueue(*args) - queue_adapter.queue self, *args - end - - def queue_as(part_name) - self.queue_name = "#{queue_base_name}_#{part_name}" - end + def self.enqueue(*args) + queue_adapter.queue self, *args end end end \ No newline at end of file diff --git a/lib/active_job/queue_name.rb b/lib/active_job/queue_name.rb new file mode 100644 index 0000000000..a606b67370 --- /dev/null +++ b/lib/active_job/queue_name.rb @@ -0,0 +1,10 @@ +module ActiveJob + module QueueName + mattr_accessor(:queue_base_name) { "active_jobs" } + mattr_accessor(:queue_name) { queue_base_name } + + def queue_as(part_name) + self.queue_name = "#{queue_base_name}_#{part_name}" + end + end +end \ No newline at end of file -- cgit v1.2.3 From 65bf5f101e1105411afe030887fbe203d08dadbd Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 11:05:34 +0200 Subject: Spacing --- test/adapters/delayed_job.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/adapters/delayed_job.rb b/test/adapters/delayed_job.rb index f334437a9e..c5d9fd37ab 100644 --- a/test/adapters/delayed_job.rb +++ b/test/adapters/delayed_job.rb @@ -2,5 +2,6 @@ require 'delayed_job' $LOAD_PATH << File.dirname(__FILE__) + "/../support/delayed_job" Delayed::Worker.delay_jobs = false -Delayed::Worker.backend = :test +Delayed::Worker.backend = :test + ActiveJob::Base.queue_adapter = :delayed_job -- cgit v1.2.3 From 211ce71400449c7e65cbef8ba9546418a20b3c8f Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 12:06:09 +0200 Subject: Add GlobalID support for serialization --- Gemfile | 1 + Gemfile.lock | 13 +++++++++ README.rdoc | 29 ++++++++++++++++++++ lib/active_job/base.rb | 6 ++--- lib/active_job/enqueuing.rb | 9 +++++++ lib/active_job/job_wrappers/delayed_job_wrapper.rb | 9 +++++++ lib/active_job/job_wrappers/resque_wrapper.rb | 4 +-- lib/active_job/job_wrappers/sidekiq_wrapper.rb | 4 ++- .../job_wrappers/sucker_punch_wrapper.rb | 2 +- lib/active_job/parameters.rb | 15 +++++++++++ .../queue_adapters/delayed_job_adapter.rb | 3 ++- lib/active_job/queue_adapters/inline_adapter.rb | 2 +- test/cases/job_serialization_test.rb | 15 +++++++++++ test/cases/parameters_test.rb | 31 ++++++++++++++++++++++ test/jobs/gid_job.rb | 6 +++++ test/models/person.rb | 19 +++++++++++++ 16 files changed, 157 insertions(+), 11 deletions(-) create mode 100644 lib/active_job/enqueuing.rb create mode 100644 lib/active_job/job_wrappers/delayed_job_wrapper.rb create mode 100644 lib/active_job/parameters.rb create mode 100644 test/cases/job_serialization_test.rb create mode 100644 test/cases/parameters_test.rb create mode 100644 test/jobs/gid_job.rb create mode 100644 test/models/person.rb diff --git a/Gemfile b/Gemfile index f8c97a8cab..92b0ed1765 100644 --- a/Gemfile +++ b/Gemfile @@ -6,3 +6,4 @@ gem 'resque' gem 'sidekiq' gem 'sucker_punch' gem 'delayed_job' +gem 'activemodel-globalid', github: 'rails/activemodel-globalid' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 7005cc4d46..e8e8e0eb18 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,11 @@ +GIT + remote: git://github.com/rails/activemodel-globalid.git + revision: c9d4cfa80e9234d178617338b22728e7c08498d1 + specs: + activemodel-globalid (0.1.0) + activemodel (>= 4.1.0) + activesupport (>= 4.1.0) + PATH remote: . specs: @@ -7,12 +15,16 @@ PATH GEM remote: https://rubygems.org/ specs: + activemodel (4.1.1) + activesupport (= 4.1.1) + builder (~> 3.1) activesupport (4.1.1) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.1) tzinfo (~> 1.1) + builder (3.2.2) celluloid (0.15.2) timers (~> 1.1.0) connection_pool (2.0.0) @@ -60,6 +72,7 @@ PLATFORMS DEPENDENCIES activejob! + activemodel-globalid! delayed_job resque sidekiq diff --git a/README.rdoc b/README.rdoc index c83a5b2986..84d40809f7 100644 --- a/README.rdoc +++ b/README.rdoc @@ -11,6 +11,35 @@ one of the most common jobs in a modern web application: Sending emails outside of the request-response cycle, so the user doesn't have to wait on it. +== GlobalID support + +Active Job supports GlobalID serialization for parameters. This makes it possible +to pass live Active Record objects to your job instead of class/id pairs, which +you then have to manually deserialize. Before, jobs would look like this: + +```ruby +class TrashableCleanupJob + def self.perfom(trashable_class, trashable_id, depth) + trashable = trashable_class.constantize.find(trashable_id) + trashable.cleanup(depth) + end +end +``` + +Now you can simply do: + +```ruby +class TrashableCleanupJob + def self.perfom(trashable, depth) + trashable.cleanup(depth) + end +end +``` + +This works with any class that mixes in ActiveModel::GlobalIdentification, which +by default has been mixed into Active Record classes. + + == Supported queueing systems We currently have adapters for: diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index fe3d1677dd..77b929d4af 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,13 +1,11 @@ require 'active_job/queue_adapter' require 'active_job/queue_name' +require 'active_job/enqueuing' module ActiveJob class Base extend QueueAdapter extend QueueName - - def self.enqueue(*args) - queue_adapter.queue self, *args - end + extend Enqueuing end end \ No newline at end of file diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb new file mode 100644 index 0000000000..94a9dbf8ab --- /dev/null +++ b/lib/active_job/enqueuing.rb @@ -0,0 +1,9 @@ +require 'active_job/parameters' + +module ActiveJob + module Enqueuing + def enqueue(*args) + queue_adapter.queue self, *Parameters.serialize(args) + end + end +end \ No newline at end of file diff --git a/lib/active_job/job_wrappers/delayed_job_wrapper.rb b/lib/active_job/job_wrappers/delayed_job_wrapper.rb new file mode 100644 index 0000000000..0868d7b570 --- /dev/null +++ b/lib/active_job/job_wrappers/delayed_job_wrapper.rb @@ -0,0 +1,9 @@ +module ActiveJob + module JobWrappers + class DelayedJobWrapper + def perform(job, *args) + job.perform(*ActiveJob::Parameters.deserialize(args)) + end + end + end +end diff --git a/lib/active_job/job_wrappers/resque_wrapper.rb b/lib/active_job/job_wrappers/resque_wrapper.rb index cbeee4fb1b..68d9b252ba 100644 --- a/lib/active_job/job_wrappers/resque_wrapper.rb +++ b/lib/active_job/job_wrappers/resque_wrapper.rb @@ -3,8 +3,6 @@ require 'resque' require 'active_support/core_ext/enumerable' require 'active_support/core_ext/array/access' - - module ActiveJob module JobWrappers class ResqueWrapper @@ -14,7 +12,7 @@ module ActiveJob end def perform(job_name, *args) - job_name.constantize.perform(*args) + job_name.constantize.perform(*ActiveJob::Parameters.deserialize(args)) end end diff --git a/lib/active_job/job_wrappers/sidekiq_wrapper.rb b/lib/active_job/job_wrappers/sidekiq_wrapper.rb index fb728ae0fd..e1332b0210 100644 --- a/lib/active_job/job_wrappers/sidekiq_wrapper.rb +++ b/lib/active_job/job_wrappers/sidekiq_wrapper.rb @@ -1,10 +1,12 @@ +require 'active_job/parameters' + module ActiveJob module JobWrappers class SidekiqWrapper include Sidekiq::Worker def perform(job_name, *args) - job_name.constantize.perform(*args) + job_name.constantize.perform(*ActiveJob::Parameters.deserialize(args)) end end end diff --git a/lib/active_job/job_wrappers/sucker_punch_wrapper.rb b/lib/active_job/job_wrappers/sucker_punch_wrapper.rb index 80648792ca..7e9960b44f 100644 --- a/lib/active_job/job_wrappers/sucker_punch_wrapper.rb +++ b/lib/active_job/job_wrappers/sucker_punch_wrapper.rb @@ -4,7 +4,7 @@ module ActiveJob include SuckerPunch::Job def perform(job_name, *args) - job_name.perform(*args) + job_name.perform(*ActiveJob::Parameters.deserialize(args)) end end end diff --git a/lib/active_job/parameters.rb b/lib/active_job/parameters.rb new file mode 100644 index 0000000000..a4841abd1e --- /dev/null +++ b/lib/active_job/parameters.rb @@ -0,0 +1,15 @@ +require 'active_model/global_locator' +require 'active_support/core_ext/object/try' + +module ActiveJob + class Parameters + def self.serialize(params) + params.collect { |param| param.try(:global_id) || param } + end + + def self.deserialize(params) + params.collect { |param| ActiveModel::GlobalLocator.locate(param) || param } + end + end +end + \ No newline at end of file diff --git a/lib/active_job/queue_adapters/delayed_job_adapter.rb b/lib/active_job/queue_adapters/delayed_job_adapter.rb index 33229dece4..4730d97a9a 100644 --- a/lib/active_job/queue_adapters/delayed_job_adapter.rb +++ b/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -1,11 +1,12 @@ require 'delayed_job' +require 'active_job/job_wrappers/delayed_job_wrapper' module ActiveJob module QueueAdapters class DelayedJobAdapter class << self def queue(job, *args) - job.delay(queue: job.queue_name).perform(*args) + JobWrappers::DelayedJobWrapper.new.delay(queue: job.queue_name).perform(job, *args) end end end diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index ac28670a29..0a1526dce7 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -3,7 +3,7 @@ module ActiveJob class InlineAdapter class << self def queue(job, *args) - job.perform(*args) + job.perform(*ActiveJob::Parameters.deserialize(args)) end end end diff --git a/test/cases/job_serialization_test.rb b/test/cases/job_serialization_test.rb new file mode 100644 index 0000000000..b1e24db22e --- /dev/null +++ b/test/cases/job_serialization_test.rb @@ -0,0 +1,15 @@ +require 'helper' +require 'jobs/gid_job' +require 'models/person' + +class JobSerializationTest < ActiveSupport::TestCase + setup do + $BUFFER = [] + @person = Person.find(5) + end + + test 'serialize job with gid' do + GidJob.enqueue @person + assert_equal "Person with ID: 5", $BUFFER.pop + end +end diff --git a/test/cases/parameters_test.rb b/test/cases/parameters_test.rb new file mode 100644 index 0000000000..eafa5a052b --- /dev/null +++ b/test/cases/parameters_test.rb @@ -0,0 +1,31 @@ +require 'helper' +require 'active_job/parameters' +require 'models/person' + +class ParameterSerializationTest < ActiveSupport::TestCase + test 'should make no change to regular values' do + assert_equal [ 1, "something" ], ActiveJob::Parameters.serialize([ 1, "something" ]) + end + + test 'should serialize records with global id' do + assert_equal [ Person.find(5).gid ], ActiveJob::Parameters.serialize([ Person.find(5) ]) + end + + test 'should serialize values and records together' do + assert_equal [ 3, Person.find(5).gid ], ActiveJob::Parameters.serialize([ 3, Person.find(5) ]) + end +end + +class ParameterDeserializationTest < ActiveSupport::TestCase + test 'should make no change to regular values' do + assert_equal [ 1, "something" ], ActiveJob::Parameters.deserialize([ 1, "something" ]) + end + + test 'should deserialize records with global id' do + assert_equal [ Person.find(5) ], ActiveJob::Parameters.deserialize([ Person.find(5).gid ]) + end + + test 'should serialize values and records together' do + assert_equal [ 3, Person.find(5) ], ActiveJob::Parameters.deserialize([ 3, Person.find(5).gid ]) + end +end diff --git a/test/jobs/gid_job.rb b/test/jobs/gid_job.rb new file mode 100644 index 0000000000..c1bfbb2655 --- /dev/null +++ b/test/jobs/gid_job.rb @@ -0,0 +1,6 @@ +class GidJob < ActiveJob::Base + def self.perform(person) + $BUFFER << "Person with ID: #{person.id}" + end +end + \ No newline at end of file diff --git a/test/models/person.rb b/test/models/person.rb new file mode 100644 index 0000000000..6e91e8b9a2 --- /dev/null +++ b/test/models/person.rb @@ -0,0 +1,19 @@ +require 'active_model/global_identification' + +class Person + include ActiveModel::GlobalIdentification + + attr_reader :id + + def self.find(id) + new(id) + end + + def initialize(id) + @id = id + end + + def ==(other_person) + other_person.is_a?(Person) && id = other_person.id + end +end \ No newline at end of file -- cgit v1.2.3 From f71147ecb097884b281b2027131aefd315a8e033 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 12:06:24 +0200 Subject: Use markdown instead of rdoc --- README.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.rdoc | 81 ------------------------------------------------------------- 2 files changed, 81 insertions(+), 81 deletions(-) create mode 100644 README.md delete mode 100644 README.rdoc diff --git a/README.md b/README.md new file mode 100644 index 0000000000..84d40809f7 --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ += Active Job -- Make work happen later + +Active Job is a framework for declaring jobs and making them run on a variety +of queueing backends. These jobs can be everything from regularly scheduled +clean-ups, billing charges, or mailings. Anything that can be chopped up into +small units of work and run in parallel, really. + +It also serves as the backend for ActionMailer's #deliver_later functionality +that makes it easy to turn any mailing into a job for running later. That's +one of the most common jobs in a modern web application: Sending emails outside +of the request-response cycle, so the user doesn't have to wait on it. + + +== GlobalID support + +Active Job supports GlobalID serialization for parameters. This makes it possible +to pass live Active Record objects to your job instead of class/id pairs, which +you then have to manually deserialize. Before, jobs would look like this: + +```ruby +class TrashableCleanupJob + def self.perfom(trashable_class, trashable_id, depth) + trashable = trashable_class.constantize.find(trashable_id) + trashable.cleanup(depth) + end +end +``` + +Now you can simply do: + +```ruby +class TrashableCleanupJob + def self.perfom(trashable, depth) + trashable.cleanup(depth) + end +end +``` + +This works with any class that mixes in ActiveModel::GlobalIdentification, which +by default has been mixed into Active Record classes. + + +== Supported queueing systems + +We currently have adapters for: + +* Resque 1.x +* Sidekiq +* Sucker Punch +* Delayed Job + +We would like to have adapters for: + +* beanstalkd +* rabbitmq + + +== Under development as a gem, targeted for Rails inclusion + +Active Job is currently being developed in a separate repository until it's +ready to be merged in with Rails. The current plan is to have Active Job +be part of the Rails 4.2 release, but plans may change depending on when +this framework stabilizes and feels ready. + + +== Download and installation + +The latest version of Active Job can be installed with RubyGems: + + % [sudo] gem install activejob + +Source code can be downloaded as part of the Rails project on GitHub + +* https://github.com/rails/activejob + + +== License + +Active Job is released under the MIT license: + +* http://www.opensource.org/licenses/MIT diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index 84d40809f7..0000000000 --- a/README.rdoc +++ /dev/null @@ -1,81 +0,0 @@ -= Active Job -- Make work happen later - -Active Job is a framework for declaring jobs and making them run on a variety -of queueing backends. These jobs can be everything from regularly scheduled -clean-ups, billing charges, or mailings. Anything that can be chopped up into -small units of work and run in parallel, really. - -It also serves as the backend for ActionMailer's #deliver_later functionality -that makes it easy to turn any mailing into a job for running later. That's -one of the most common jobs in a modern web application: Sending emails outside -of the request-response cycle, so the user doesn't have to wait on it. - - -== GlobalID support - -Active Job supports GlobalID serialization for parameters. This makes it possible -to pass live Active Record objects to your job instead of class/id pairs, which -you then have to manually deserialize. Before, jobs would look like this: - -```ruby -class TrashableCleanupJob - def self.perfom(trashable_class, trashable_id, depth) - trashable = trashable_class.constantize.find(trashable_id) - trashable.cleanup(depth) - end -end -``` - -Now you can simply do: - -```ruby -class TrashableCleanupJob - def self.perfom(trashable, depth) - trashable.cleanup(depth) - end -end -``` - -This works with any class that mixes in ActiveModel::GlobalIdentification, which -by default has been mixed into Active Record classes. - - -== Supported queueing systems - -We currently have adapters for: - -* Resque 1.x -* Sidekiq -* Sucker Punch -* Delayed Job - -We would like to have adapters for: - -* beanstalkd -* rabbitmq - - -== Under development as a gem, targeted for Rails inclusion - -Active Job is currently being developed in a separate repository until it's -ready to be merged in with Rails. The current plan is to have Active Job -be part of the Rails 4.2 release, but plans may change depending on when -this framework stabilizes and feels ready. - - -== Download and installation - -The latest version of Active Job can be installed with RubyGems: - - % [sudo] gem install activejob - -Source code can be downloaded as part of the Rails project on GitHub - -* https://github.com/rails/activejob - - -== License - -Active Job is released under the MIT license: - -* http://www.opensource.org/licenses/MIT -- cgit v1.2.3 From c14cc48ed741a8d96f2aeff58da8f92eeaceabd4 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 12:08:03 +0200 Subject: Update README.md --- README.md | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 84d40809f7..53cd1e86b6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -= Active Job -- Make work happen later +# Active Job -- Make work happen later Active Job is a framework for declaring jobs and making them run on a variety of queueing backends. These jobs can be everything from regularly scheduled @@ -11,7 +11,7 @@ one of the most common jobs in a modern web application: Sending emails outside of the request-response cycle, so the user doesn't have to wait on it. -== GlobalID support +## GlobalID support Active Job supports GlobalID serialization for parameters. This makes it possible to pass live Active Record objects to your job instead of class/id pairs, which @@ -40,7 +40,7 @@ This works with any class that mixes in ActiveModel::GlobalIdentification, which by default has been mixed into Active Record classes. -== Supported queueing systems +## Supported queueing systems We currently have adapters for: @@ -55,7 +55,7 @@ We would like to have adapters for: * rabbitmq -== Under development as a gem, targeted for Rails inclusion +## Under development as a gem, targeted for Rails inclusion Active Job is currently being developed in a separate repository until it's ready to be merged in with Rails. The current plan is to have Active Job @@ -63,18 +63,7 @@ be part of the Rails 4.2 release, but plans may change depending on when this framework stabilizes and feels ready. -== Download and installation - -The latest version of Active Job can be installed with RubyGems: - - % [sudo] gem install activejob - -Source code can be downloaded as part of the Rails project on GitHub - -* https://github.com/rails/activejob - - -== License +## License Active Job is released under the MIT license: -- cgit v1.2.3 From 501cc60ff2528ba75c0bf0715918516864546539 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 12:34:27 +0200 Subject: Inline the job wrappers --- lib/active_job/job_wrappers/delayed_job_wrapper.rb | 9 ------- lib/active_job/job_wrappers/resque_wrapper.rb | 29 ---------------------- lib/active_job/job_wrappers/sidekiq_wrapper.rb | 13 ---------- .../job_wrappers/sucker_punch_wrapper.rb | 11 -------- .../queue_adapters/delayed_job_adapter.rb | 9 +++++-- lib/active_job/queue_adapters/resque_adapter.rb | 25 +++++++++++++++++-- lib/active_job/queue_adapters/sidekiq_adapter.rb | 11 ++++++-- .../queue_adapters/sucker_punch_adapter.rb | 11 ++++++-- 8 files changed, 48 insertions(+), 70 deletions(-) delete mode 100644 lib/active_job/job_wrappers/delayed_job_wrapper.rb delete mode 100644 lib/active_job/job_wrappers/resque_wrapper.rb delete mode 100644 lib/active_job/job_wrappers/sidekiq_wrapper.rb delete mode 100644 lib/active_job/job_wrappers/sucker_punch_wrapper.rb diff --git a/lib/active_job/job_wrappers/delayed_job_wrapper.rb b/lib/active_job/job_wrappers/delayed_job_wrapper.rb deleted file mode 100644 index 0868d7b570..0000000000 --- a/lib/active_job/job_wrappers/delayed_job_wrapper.rb +++ /dev/null @@ -1,9 +0,0 @@ -module ActiveJob - module JobWrappers - class DelayedJobWrapper - def perform(job, *args) - job.perform(*ActiveJob::Parameters.deserialize(args)) - end - end - end -end diff --git a/lib/active_job/job_wrappers/resque_wrapper.rb b/lib/active_job/job_wrappers/resque_wrapper.rb deleted file mode 100644 index 68d9b252ba..0000000000 --- a/lib/active_job/job_wrappers/resque_wrapper.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'resque' - -require 'active_support/core_ext/enumerable' -require 'active_support/core_ext/array/access' - -module ActiveJob - module JobWrappers - class ResqueWrapper - class << self - def wrap(job, args) - [ new(job), *args.prepend(job) ] - end - - def perform(job_name, *args) - job_name.constantize.perform(*ActiveJob::Parameters.deserialize(args)) - end - end - - - def initialize(job) - @queue = job.queue_name - end - - def to_s - self.class.to_s - end - end - end -end diff --git a/lib/active_job/job_wrappers/sidekiq_wrapper.rb b/lib/active_job/job_wrappers/sidekiq_wrapper.rb deleted file mode 100644 index e1332b0210..0000000000 --- a/lib/active_job/job_wrappers/sidekiq_wrapper.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'active_job/parameters' - -module ActiveJob - module JobWrappers - class SidekiqWrapper - include Sidekiq::Worker - - def perform(job_name, *args) - job_name.constantize.perform(*ActiveJob::Parameters.deserialize(args)) - end - end - end -end diff --git a/lib/active_job/job_wrappers/sucker_punch_wrapper.rb b/lib/active_job/job_wrappers/sucker_punch_wrapper.rb deleted file mode 100644 index 7e9960b44f..0000000000 --- a/lib/active_job/job_wrappers/sucker_punch_wrapper.rb +++ /dev/null @@ -1,11 +0,0 @@ -module ActiveJob - module JobWrappers - class SuckerPunchWrapper - include SuckerPunch::Job - - def perform(job_name, *args) - job_name.perform(*ActiveJob::Parameters.deserialize(args)) - end - end - end -end diff --git a/lib/active_job/queue_adapters/delayed_job_adapter.rb b/lib/active_job/queue_adapters/delayed_job_adapter.rb index 4730d97a9a..1db15f1804 100644 --- a/lib/active_job/queue_adapters/delayed_job_adapter.rb +++ b/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -1,12 +1,17 @@ require 'delayed_job' -require 'active_job/job_wrappers/delayed_job_wrapper' module ActiveJob module QueueAdapters class DelayedJobAdapter class << self def queue(job, *args) - JobWrappers::DelayedJobWrapper.new.delay(queue: job.queue_name).perform(job, *args) + JobWrapper.new.delay(queue: job.queue_name).perform(job, *args) + end + end + + class JobWrapper + def perform(job, *args) + job.perform(*ActiveJob::Parameters.deserialize(args)) end end end diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index 43a44d2a48..1d1f9ffe83 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -1,12 +1,33 @@ require 'resque' -require 'active_job/job_wrappers/resque_wrapper' +require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/array/access' module ActiveJob module QueueAdapters class ResqueAdapter class << self def queue(job, *args) - Resque.enqueue *JobWrappers::ResqueWrapper.wrap(job, args) + Resque.enqueue *JobWrapper.wrap(job, args) + end + end + + class JobWrapper + class << self + def wrap(job, args) + [ new(job), *args.prepend(job) ] + end + + def perform(job_name, *args) + job_name.constantize.perform(*ActiveJob::Parameters.deserialize(args)) + end + end + + def initialize(job) + @queue = job.queue_name + end + + def to_s + self.class.to_s end end end diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index 70c4377a23..90af35525a 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -1,12 +1,19 @@ require 'sidekiq' -require 'active_job/job_wrappers/sidekiq_wrapper' module ActiveJob module QueueAdapters class SidekiqAdapter class << self def queue(job, *args) - JobWrappers::SidekiqWrapper.perform_async(job, *args) + JobWrapper.perform_async(job, *args) + end + end + + class JobWrapper + include Sidekiq::Worker + + def perform(job_name, *args) + job_name.constantize.perform(*ActiveJob::Parameters.deserialize(args)) end end end diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index 8191e5a1df..d721a0dcd5 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -1,12 +1,19 @@ require 'sucker_punch' -require 'active_job/job_wrappers/sucker_punch_wrapper' module ActiveJob module QueueAdapters class SuckerPunchAdapter class << self def queue(job, *args) - JobWrappers::SuckerPunchWrapper.new.async.perform(job, *args) + JobWrapper.new.async.perform(job, *args) + end + end + + class JobWrapper + include SuckerPunch::Job + + def perform(job_name, *args) + job_name.perform(*ActiveJob::Parameters.deserialize(args)) end end end -- cgit v1.2.3 From 37b13cdd1f59d17d6c44e3ff301c738cd13b4511 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 12:36:01 +0200 Subject: No need to qualify the Parameters class with the namespace --- lib/active_job/queue_adapters/delayed_job_adapter.rb | 2 +- lib/active_job/queue_adapters/inline_adapter.rb | 2 +- lib/active_job/queue_adapters/resque_adapter.rb | 2 +- lib/active_job/queue_adapters/sidekiq_adapter.rb | 2 +- lib/active_job/queue_adapters/sucker_punch_adapter.rb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/active_job/queue_adapters/delayed_job_adapter.rb b/lib/active_job/queue_adapters/delayed_job_adapter.rb index 1db15f1804..de53f66da1 100644 --- a/lib/active_job/queue_adapters/delayed_job_adapter.rb +++ b/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -11,7 +11,7 @@ module ActiveJob class JobWrapper def perform(job, *args) - job.perform(*ActiveJob::Parameters.deserialize(args)) + job.perform *Parameters.deserialize(args) end end end diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index 0a1526dce7..36fc3c4951 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -3,7 +3,7 @@ module ActiveJob class InlineAdapter class << self def queue(job, *args) - job.perform(*ActiveJob::Parameters.deserialize(args)) + job.perform *Parameters.deserialize(args) end end end diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index 1d1f9ffe83..c1e4000283 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -18,7 +18,7 @@ module ActiveJob end def perform(job_name, *args) - job_name.constantize.perform(*ActiveJob::Parameters.deserialize(args)) + job_name.constantize.perform *Parameters.deserialize(args) end end diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index 90af35525a..b02b3592da 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -13,7 +13,7 @@ module ActiveJob include Sidekiq::Worker def perform(job_name, *args) - job_name.constantize.perform(*ActiveJob::Parameters.deserialize(args)) + job_name.constantize.perform *Parameters.deserialize(args) end end end diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index d721a0dcd5..b575927cfe 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -13,7 +13,7 @@ module ActiveJob include SuckerPunch::Job def perform(job_name, *args) - job_name.perform(*ActiveJob::Parameters.deserialize(args)) + job_name.perform *Parameters.deserialize(args) end end end -- cgit v1.2.3 From f3a9ad70570d52fa01d847d86f34ec2119d4631b Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 12:39:14 +0200 Subject: Move activemodel-globalid dependency to gemspec --- Gemfile | 1 - Gemfile.lock | 13 ++++--------- activejob.gemspec | 1 + 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index 92b0ed1765..f8c97a8cab 100644 --- a/Gemfile +++ b/Gemfile @@ -6,4 +6,3 @@ gem 'resque' gem 'sidekiq' gem 'sucker_punch' gem 'delayed_job' -gem 'activemodel-globalid', github: 'rails/activemodel-globalid' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index e8e8e0eb18..1d86563133 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,15 +1,8 @@ -GIT - remote: git://github.com/rails/activemodel-globalid.git - revision: c9d4cfa80e9234d178617338b22728e7c08498d1 - specs: - activemodel-globalid (0.1.0) - activemodel (>= 4.1.0) - activesupport (>= 4.1.0) - PATH remote: . specs: activejob (4.2.0.alpha) + activemodel-globalid activesupport (>= 4.1.0) GEM @@ -18,6 +11,9 @@ GEM activemodel (4.1.1) activesupport (= 4.1.1) builder (~> 3.1) + activemodel-globalid (0.1.0) + activemodel (>= 4.1.0) + activesupport (>= 4.1.0) activesupport (4.1.1) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) @@ -72,7 +68,6 @@ PLATFORMS DEPENDENCIES activejob! - activemodel-globalid! delayed_job resque sidekiq diff --git a/activejob.gemspec b/activejob.gemspec index 201f3efb31..23fd25c6c8 100644 --- a/activejob.gemspec +++ b/activejob.gemspec @@ -17,4 +17,5 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.add_dependency 'activesupport', '>= 4.1.0' + s.add_dependency 'activemodel-globalid' end -- cgit v1.2.3 From afb3d4f9e5820cb8f5cd4502e1d0525044ac6ef2 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 12:46:27 +0200 Subject: Move to instance method and document usage --- README.md | 25 ++++++++++++++++++++-- .../queue_adapters/delayed_job_adapter.rb | 2 +- lib/active_job/queue_adapters/inline_adapter.rb | 2 +- lib/active_job/queue_adapters/resque_adapter.rb | 2 +- lib/active_job/queue_adapters/sidekiq_adapter.rb | 2 +- .../queue_adapters/sucker_punch_adapter.rb | 2 +- test/jobs/gid_job.rb | 2 +- test/jobs/hello_job.rb | 2 +- 8 files changed, 30 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 53cd1e86b6..dbd31d91ce 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,27 @@ one of the most common jobs in a modern web application: Sending emails outside of the request-response cycle, so the user doesn't have to wait on it. +## Usage + +Declare a job like so: + +```ruby +class MyJob < ActiveJob::Base + def perform(record) + record.do_work + end +end +``` + +Enqueue a job like so: + +```ruby +MyJob.enqueue record +``` + +That's it! + + ## GlobalID support Active Job supports GlobalID serialization for parameters. This makes it possible @@ -19,7 +40,7 @@ you then have to manually deserialize. Before, jobs would look like this: ```ruby class TrashableCleanupJob - def self.perfom(trashable_class, trashable_id, depth) + def perfom(trashable_class, trashable_id, depth) trashable = trashable_class.constantize.find(trashable_id) trashable.cleanup(depth) end @@ -30,7 +51,7 @@ Now you can simply do: ```ruby class TrashableCleanupJob - def self.perfom(trashable, depth) + def perfom(trashable, depth) trashable.cleanup(depth) end end diff --git a/lib/active_job/queue_adapters/delayed_job_adapter.rb b/lib/active_job/queue_adapters/delayed_job_adapter.rb index de53f66da1..14072e2801 100644 --- a/lib/active_job/queue_adapters/delayed_job_adapter.rb +++ b/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -11,7 +11,7 @@ module ActiveJob class JobWrapper def perform(job, *args) - job.perform *Parameters.deserialize(args) + job.new.perform *Parameters.deserialize(args) end end end diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index 36fc3c4951..cffa55af82 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -3,7 +3,7 @@ module ActiveJob class InlineAdapter class << self def queue(job, *args) - job.perform *Parameters.deserialize(args) + job.new.perform *Parameters.deserialize(args) end end end diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index c1e4000283..bb5f3df8d6 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -18,7 +18,7 @@ module ActiveJob end def perform(job_name, *args) - job_name.constantize.perform *Parameters.deserialize(args) + job_name.constantize.new.perform *Parameters.deserialize(args) end end diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index b02b3592da..957905f9d3 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -13,7 +13,7 @@ module ActiveJob include Sidekiq::Worker def perform(job_name, *args) - job_name.constantize.perform *Parameters.deserialize(args) + job_name.constantize.new.perform *Parameters.deserialize(args) end end end diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index b575927cfe..182b5ce018 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -13,7 +13,7 @@ module ActiveJob include SuckerPunch::Job def perform(job_name, *args) - job_name.perform *Parameters.deserialize(args) + job_name.new.perform *Parameters.deserialize(args) end end end diff --git a/test/jobs/gid_job.rb b/test/jobs/gid_job.rb index c1bfbb2655..c69e38d3cc 100644 --- a/test/jobs/gid_job.rb +++ b/test/jobs/gid_job.rb @@ -1,5 +1,5 @@ class GidJob < ActiveJob::Base - def self.perform(person) + def perform(person) $BUFFER << "Person with ID: #{person.id}" end end diff --git a/test/jobs/hello_job.rb b/test/jobs/hello_job.rb index d0e65f9674..25441dd0c8 100644 --- a/test/jobs/hello_job.rb +++ b/test/jobs/hello_job.rb @@ -1,5 +1,5 @@ class HelloJob < ActiveJob::Base - def self.perform(greeter = "David") + def perform(greeter = "David") $BUFFER << "#{greeter} says hello" end end -- cgit v1.2.3 From bc9bd5765b8282b3004b3db2789dd42229137fa7 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Mon, 19 May 2014 10:53:25 +0000 Subject: Use case/when --- lib/active_job/queue_adapter.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/active_job/queue_adapter.rb b/lib/active_job/queue_adapter.rb index a1987a7dc3..2033f6fe56 100644 --- a/lib/active_job/queue_adapter.rb +++ b/lib/active_job/queue_adapter.rb @@ -6,12 +6,13 @@ module ActiveJob mattr_reader(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter } def queue_adapter=(name_or_adapter) - if name_or_adapter.is_a?(Symbol) || name_or_adapter.is_a?(String) + case name_or_adapter + when Symbol, String adapter = load_adapter(name_or_adapter) else adapter = name_or_adapter end - + @@queue_adapter = adapter end -- cgit v1.2.3 From c47d6b499078b13cc7a75f520757b78c1a52d852 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 13:01:33 +0200 Subject: Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dbd31d91ce..1b9e20c0d0 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ of queueing backends. These jobs can be everything from regularly scheduled clean-ups, billing charges, or mailings. Anything that can be chopped up into small units of work and run in parallel, really. -It also serves as the backend for ActionMailer's #deliver_later functionality +It also serves as the backend for [ActionMailer's #deliver_later functionality][https://github.com/rails/activejob/issues/13] that makes it easy to turn any mailing into a job for running later. That's one of the most common jobs in a modern web application: Sending emails outside of the request-response cycle, so the user doesn't have to wait on it. @@ -34,7 +34,7 @@ That's it! ## GlobalID support -Active Job supports GlobalID serialization for parameters. This makes it possible +Active Job supports [GlobalID serialization][https://github.com/rails/activemodel-globalid/] for parameters. This makes it possible to pass live Active Record objects to your job instead of class/id pairs, which you then have to manually deserialize. Before, jobs would look like this: -- cgit v1.2.3 From f85ad8bdcd3796620d8b03a2dfdf034c6ed43498 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 13:02:32 +0200 Subject: Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1b9e20c0d0..039fd95ebb 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ of queueing backends. These jobs can be everything from regularly scheduled clean-ups, billing charges, or mailings. Anything that can be chopped up into small units of work and run in parallel, really. -It also serves as the backend for [ActionMailer's #deliver_later functionality][https://github.com/rails/activejob/issues/13] +It also serves as the backend for [ActionMailer's #deliver_later functionality](https://github.com/rails/activejob/issues/13) that makes it easy to turn any mailing into a job for running later. That's one of the most common jobs in a modern web application: Sending emails outside of the request-response cycle, so the user doesn't have to wait on it. @@ -34,7 +34,7 @@ That's it! ## GlobalID support -Active Job supports [GlobalID serialization][https://github.com/rails/activemodel-globalid/] for parameters. This makes it possible +Active Job supports [GlobalID serialization](https://github.com/rails/activemodel-globalid/) for parameters. This makes it possible to pass live Active Record objects to your job instead of class/id pairs, which you then have to manually deserialize. Before, jobs would look like this: -- cgit v1.2.3 From ca8b2fd8996d75f8dd3d612f7ea35279e29cb9a9 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Mon, 19 May 2014 11:02:37 +0000 Subject: Setting the adapter load the required gem. --- test/adapters/delayed_job.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/adapters/delayed_job.rb b/test/adapters/delayed_job.rb index c5d9fd37ab..afd9c9deb7 100644 --- a/test/adapters/delayed_job.rb +++ b/test/adapters/delayed_job.rb @@ -1,7 +1,7 @@ -require 'delayed_job' +ActiveJob::Base.queue_adapter = :delayed_job + $LOAD_PATH << File.dirname(__FILE__) + "/../support/delayed_job" Delayed::Worker.delay_jobs = false Delayed::Worker.backend = :test -ActiveJob::Base.queue_adapter = :delayed_job -- cgit v1.2.3 From c69dda49880df94c7243e9dc5fcf43a223769f43 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 13:13:30 +0200 Subject: Add justification --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index dbd31d91ce..4c442e08c4 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,13 @@ that makes it easy to turn any mailing into a job for running later. That's one of the most common jobs in a modern web application: Sending emails outside of the request-response cycle, so the user doesn't have to wait on it. +The main point is to ensure that all Rails apps will have a job infrastructure +in place, even if it's in the form of an "immediate runner". We can then have +framework features and other gems build on top of that, without having to worry +about API differences between Delayed Job and Resque. Picking your queuing +backend becomes more of an operational concern, then. And you'll be able to +switch between them without having to rewrite your jobs. + ## Usage -- cgit v1.2.3 From 3688c6c638e0cc528fbe06d1fc393c0272cad3d9 Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Mon, 19 May 2014 04:14:20 -0700 Subject: Pull in rake For those that don't have it globally... --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index f8c97a8cab..9f74b017f0 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,7 @@ source 'https://rubygems.org' gemspec +gem 'rake' gem 'resque' gem 'sidekiq' gem 'sucker_punch' -- cgit v1.2.3 From 575a837de1ba4bc2d0ff41c9b5b6d10f011f4c7a Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Mon, 19 May 2014 04:18:28 -0700 Subject: Whitelist legal job parameter types --- lib/active_job/parameters.rb | 10 +++++++--- test/cases/parameters_test.rb | 15 +++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/active_job/parameters.rb b/lib/active_job/parameters.rb index a4841abd1e..75de5bcae7 100644 --- a/lib/active_job/parameters.rb +++ b/lib/active_job/parameters.rb @@ -3,13 +3,17 @@ require 'active_support/core_ext/object/try' module ActiveJob class Parameters + TYPE_WHITELIST = [NilClass, Fixnum, Float, String, TrueClass, FalseClass, Hash, Array] + def self.serialize(params) - params.collect { |param| param.try(:global_id) || param } + params.collect do |param| + raise "Unsupported parameter type: #{param.class.name}" unless param.respond_to?(:global_id) || TYPE_WHITELIST.include?(param.class) + param.try(:global_id) || param + end end - + def self.deserialize(params) params.collect { |param| ActiveModel::GlobalLocator.locate(param) || param } end end end - \ No newline at end of file diff --git a/test/cases/parameters_test.rb b/test/cases/parameters_test.rb index eafa5a052b..3fbdf8adee 100644 --- a/test/cases/parameters_test.rb +++ b/test/cases/parameters_test.rb @@ -6,11 +6,18 @@ class ParameterSerializationTest < ActiveSupport::TestCase test 'should make no change to regular values' do assert_equal [ 1, "something" ], ActiveJob::Parameters.serialize([ 1, "something" ]) end - + + test 'should not allow complex objects' do + err = assert_raises RuntimeError do + ActiveJob::Parameters.serialize([ 1, self ]) + end + assert_equal "Unsupported parameter type: #{self.class.name}", err.message + end + test 'should serialize records with global id' do assert_equal [ Person.find(5).gid ], ActiveJob::Parameters.serialize([ Person.find(5) ]) end - + test 'should serialize values and records together' do assert_equal [ 3, Person.find(5).gid ], ActiveJob::Parameters.serialize([ 3, Person.find(5) ]) end @@ -20,11 +27,11 @@ class ParameterDeserializationTest < ActiveSupport::TestCase test 'should make no change to regular values' do assert_equal [ 1, "something" ], ActiveJob::Parameters.deserialize([ 1, "something" ]) end - + test 'should deserialize records with global id' do assert_equal [ Person.find(5) ], ActiveJob::Parameters.deserialize([ Person.find(5).gid ]) end - + test 'should serialize values and records together' do assert_equal [ 3, Person.find(5) ], ActiveJob::Parameters.deserialize([ 3, Person.find(5).gid ]) end -- cgit v1.2.3 From 704b17bd0a11c3691b74ca30def7a20ed0a0b5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 19 May 2014 08:18:42 -0300 Subject: Use bundle gem tasks --- Rakefile | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/Rakefile b/Rakefile index d0d837602a..c602ee53da 100644 --- a/Rakefile +++ b/Rakefile @@ -1,4 +1,4 @@ -dir = File.dirname(__FILE__) +require 'bundler/gem_tasks' require 'rake/testtask' @@ -41,18 +41,3 @@ end task "test_#{adapter}" => "#{adapter}:env" end - -require 'rubygems/package_task' - -spec = eval(File.read("#{dir}/activejob.gemspec")) - -Gem::PackageTask.new(spec) do |p| - p.gem_spec = spec -end - -desc "Release to rubygems" -task :release => :package do - require 'rake/gemcutter' - Rake::Gemcutter::Tasks.new(spec).define - Rake::Task['gem:push'].invoke -end -- cgit v1.2.3 From 744640304f7e88ad13b0676015c39d4654fc20ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 19 May 2014 08:19:09 -0300 Subject: Update Gemfile.lock --- Gemfile.lock | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 1d86563133..849ccf6634 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -34,6 +34,7 @@ GEM rack (1.5.2) rack-protection (1.5.2) rack + rake (10.3.2) redis (3.0.7) redis-namespace (1.4.1) redis (~> 3.0.4) @@ -69,6 +70,7 @@ PLATFORMS DEPENDENCIES activejob! delayed_job + rake resque sidekiq sucker_punch -- cgit v1.2.3 From 430228ea56341d8bb0bb9e4503fffea66c1b8251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 19 May 2014 08:19:20 -0300 Subject: :scissors: --- Rakefile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Rakefile b/Rakefile index c602ee53da..5242926c7f 100644 --- a/Rakefile +++ b/Rakefile @@ -16,8 +16,6 @@ def run_without_aborting(*tasks) abort "Errors running #{errors.join(', ')}" if errors.any? end - - task :default => :test desc 'Run all adapter tests' @@ -26,8 +24,7 @@ task :test do run_without_aborting(*tasks) end - -%w( inline resque sidekiq sucker_punch delayed_job).each do |adapter| +%w(inline resque sidekiq sucker_punch delayed_job).each do |adapter| Rake::TestTask.new("test_#{adapter}") do |t| t.libs << 'test' t.test_files = FileList['test/cases/**/*_test.rb'] -- cgit v1.2.3 From edc892680162ff6d41ba80c553c8f25539722912 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 19 May 2014 13:23:35 +0200 Subject: Fix typos in readme. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 08e24844fe..ee6a07bf34 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ you then have to manually deserialize. Before, jobs would look like this: ```ruby class TrashableCleanupJob - def perfom(trashable_class, trashable_id, depth) + def perform(trashable_class, trashable_id, depth) trashable = trashable_class.constantize.find(trashable_id) trashable.cleanup(depth) end @@ -58,7 +58,7 @@ Now you can simply do: ```ruby class TrashableCleanupJob - def perfom(trashable, depth) + def perform(trashable, depth) trashable.cleanup(depth) end end -- cgit v1.2.3 From 62aa8acb13afb367f49a21b25c6ac416f5a7e09f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 19 May 2014 13:36:08 +0200 Subject: Have Sidekiq adapter take queue_name into account. --- lib/active_job/queue_adapters/sidekiq_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index 957905f9d3..e712762785 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -5,7 +5,7 @@ module ActiveJob class SidekiqAdapter class << self def queue(job, *args) - JobWrapper.perform_async(job, *args) + JobWrapper.client_push('class' => JobWrapper, 'queue' => job.queue_name, 'args' => [job, *args]) end end -- cgit v1.2.3 From e2405662f6fdb7ba3534c84b19cde01e598551de Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 19 May 2014 13:44:07 +0200 Subject: Styling --- lib/active_job/queue_adapters/sidekiq_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index e712762785..c8fac32963 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -5,7 +5,7 @@ module ActiveJob class SidekiqAdapter class << self def queue(job, *args) - JobWrapper.client_push('class' => JobWrapper, 'queue' => job.queue_name, 'args' => [job, *args]) + JobWrapper.client_push class: JobWrapper, queue: job.queue_name, args: [ job, *args ] end end -- cgit v1.2.3 From c086dfbb99cbd49dadd51cc6b301125806684d42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 19 May 2014 08:51:51 -0300 Subject: Make sure Bignum can be serialized --- lib/active_job/parameters.rb | 2 +- test/cases/parameters_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/active_job/parameters.rb b/lib/active_job/parameters.rb index 75de5bcae7..5f814f846d 100644 --- a/lib/active_job/parameters.rb +++ b/lib/active_job/parameters.rb @@ -3,7 +3,7 @@ require 'active_support/core_ext/object/try' module ActiveJob class Parameters - TYPE_WHITELIST = [NilClass, Fixnum, Float, String, TrueClass, FalseClass, Hash, Array] + TYPE_WHITELIST = [NilClass, Fixnum, Float, String, TrueClass, FalseClass, Hash, Array, Bignum] def self.serialize(params) params.collect do |param| diff --git a/test/cases/parameters_test.rb b/test/cases/parameters_test.rb index 3fbdf8adee..fb0b920c6e 100644 --- a/test/cases/parameters_test.rb +++ b/test/cases/parameters_test.rb @@ -8,6 +8,16 @@ class ParameterSerializationTest < ActiveSupport::TestCase end test 'should not allow complex objects' do + assert_equal [ nil ], ActiveJob::Parameters.serialize([ nil ]) + assert_equal [ 1 ], ActiveJob::Parameters.serialize([ 1 ]) + assert_equal [ 1.0 ], ActiveJob::Parameters.serialize([ 1.0 ]) + assert_equal [ 'a' ], ActiveJob::Parameters.serialize([ 'a' ]) + assert_equal [ true ], ActiveJob::Parameters.serialize([ true ]) + assert_equal [ false ], ActiveJob::Parameters.serialize([ false ]) + assert_equal [ { a: 1 } ], ActiveJob::Parameters.serialize([ { a: 1 } ]) + assert_equal [ [ 1 ] ], ActiveJob::Parameters.serialize([ [ 1 ] ]) + assert_equal [ 1_000_000_000_000_000_000_000 ], ActiveJob::Parameters.serialize([ 1_000_000_000_000_000_000_000 ]) + err = assert_raises RuntimeError do ActiveJob::Parameters.serialize([ 1, self ]) end -- cgit v1.2.3 From 68793bff197d47be9f5742c78eff0b547281c83e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 19 May 2014 14:00:14 +0200 Subject: Refactor Resque adapter to be more consistent with others --- lib/active_job/queue_adapters/resque_adapter.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index bb5f3df8d6..6686f10593 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -7,16 +7,12 @@ module ActiveJob class ResqueAdapter class << self def queue(job, *args) - Resque.enqueue *JobWrapper.wrap(job, args) + Resque.enqueue JobWrapper.new(job), job, *args end end class JobWrapper class << self - def wrap(job, args) - [ new(job), *args.prepend(job) ] - end - def perform(job_name, *args) job_name.constantize.new.perform *Parameters.deserialize(args) end @@ -27,9 +23,9 @@ module ActiveJob end def to_s - self.class.to_s + self.class.name end end end end -end \ No newline at end of file +end -- cgit v1.2.3 From 29c9dcc4aff563085e94ec89c148b6e779fd4e46 Mon Sep 17 00:00:00 2001 From: Larry Lv Date: Tue, 20 May 2014 00:58:19 +0800 Subject: Add doc for setting the queue adapter. --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index ee6a07bf34..bbaaaf73ac 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,13 @@ switch between them without having to rewrite your jobs. ## Usage +Set the queue adapter for Active Job: + +``` ruby +ActiveJob::Base.queue_adapter = :inline # default queue adapter +# Adapters currently supported: :resque, :sidekiq, :sucker_punch, :delayed_job +``` + Declare a job like so: ```ruby -- cgit v1.2.3 From 6889c6152dea729638e40297c8d709b41419ad6b Mon Sep 17 00:00:00 2001 From: Larry Lv Date: Tue, 20 May 2014 01:32:05 +0800 Subject: Fix `Person#==` method in test. --- test/models/person.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/models/person.rb b/test/models/person.rb index 6e91e8b9a2..fb12c1c614 100644 --- a/test/models/person.rb +++ b/test/models/person.rb @@ -14,6 +14,6 @@ class Person end def ==(other_person) - other_person.is_a?(Person) && id = other_person.id + other_person.is_a?(Person) && id == other_person.id end -end \ No newline at end of file +end -- cgit v1.2.3 From 6198978fa23e7acc22bc0196db9315a60bea52d0 Mon Sep 17 00:00:00 2001 From: Larry Lv Date: Tue, 20 May 2014 01:36:41 +0800 Subject: Make tests for `Person` pass. --- test/models/person.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/models/person.rb b/test/models/person.rb index fb12c1c614..a5bdbc462b 100644 --- a/test/models/person.rb +++ b/test/models/person.rb @@ -2,18 +2,18 @@ require 'active_model/global_identification' class Person include ActiveModel::GlobalIdentification - + attr_reader :id - + def self.find(id) new(id) end - + def initialize(id) @id = id end - + def ==(other_person) - other_person.is_a?(Person) && id == other_person.id + other_person.is_a?(Person) && id.to_s == other_person.id.to_s end end -- cgit v1.2.3 From fc80f4e53256235a2b1a40965f4679e5abccc907 Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Mon, 19 May 2014 13:13:40 -0700 Subject: RDoc enqueue --- lib/active_job/enqueuing.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index 94a9dbf8ab..324385216e 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -2,8 +2,16 @@ require 'active_job/parameters' module ActiveJob module Enqueuing + ## + # Push a job onto the queue. The arguments must be legal JSON types + # (string, int, float, nil, true, false, hash or array) or + # ActiveModel::GlobalIdentication instances. Arbitrary Ruby objects + # are not supported. + # + # The return value is adapter-specific and may change in a future + # ActiveJob release. def enqueue(*args) queue_adapter.queue self, *Parameters.serialize(args) end end -end \ No newline at end of file +end -- cgit v1.2.3 From eebec88e03b3e585f507832f41c22411a9777472 Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Mon, 19 May 2014 13:21:13 -0700 Subject: Update README.md beanstalkd and rabbitmq are MQ servers, they aren't something that would integrate directly with Rails and ActiveJob. That's like saying we want an adapter for Redis, when really you want adapters for Resque, Sidekiq, etc. Sneakers is the latest Ruby job system using rabbitmq. I don't know of a modern beanstalkd system for Ruby, Stalker is pretty dead these days. I added QueueClassic because it's reasonably popular on Heroku for the "postgresql only" crowd. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bbaaaf73ac..da73276e84 100644 --- a/README.md +++ b/README.md @@ -86,8 +86,8 @@ We currently have adapters for: We would like to have adapters for: -* beanstalkd -* rabbitmq +* QueueClassic +* Sneakers ## Under development as a gem, targeted for Rails inclusion -- cgit v1.2.3 From 5cad2c1b8aa1139ba3de45b3ce26b9d5c9931f6c Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Tue, 20 May 2014 00:01:56 +0300 Subject: Implemented queue_classic adapter --- Gemfile | 1 + Gemfile.lock | 4 ++++ README.md | 4 ++-- Rakefile | 4 ++-- .../queue_adapters/queue_classic_adapter.rb | 20 ++++++++++++++++++++ test/adapters/queue_classic.rb | 2 ++ test/support/queue_classic/inline.rb | 11 +++++++++++ 7 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 lib/active_job/queue_adapters/queue_classic_adapter.rb create mode 100644 test/adapters/queue_classic.rb create mode 100644 test/support/queue_classic/inline.rb diff --git a/Gemfile b/Gemfile index 9f74b017f0..550da15615 100644 --- a/Gemfile +++ b/Gemfile @@ -7,3 +7,4 @@ gem 'resque' gem 'sidekiq' gem 'sucker_punch' gem 'delayed_job' +gem 'queue_classic' diff --git a/Gemfile.lock b/Gemfile.lock index 849ccf6634..c1df0ebc36 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -31,6 +31,9 @@ GEM minitest (5.3.4) mono_logger (1.1.0) multi_json (1.9.3) + pg (0.17.1) + queue_classic (2.2.3) + pg (~> 0.17.0) rack (1.5.2) rack-protection (1.5.2) rack @@ -70,6 +73,7 @@ PLATFORMS DEPENDENCIES activejob! delayed_job + queue_classic rake resque sidekiq diff --git a/README.md b/README.md index da73276e84..a0e5f01e29 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ of the request-response cycle, so the user doesn't have to wait on it. The main point is to ensure that all Rails apps will have a job infrastructure in place, even if it's in the form of an "immediate runner". We can then have framework features and other gems build on top of that, without having to worry -about API differences between Delayed Job and Resque. Picking your queuing +about API differences between Delayed Job and Resque. Picking your queuing backend becomes more of an operational concern, then. And you'll be able to switch between them without having to rewrite your jobs. @@ -83,10 +83,10 @@ We currently have adapters for: * Sidekiq * Sucker Punch * Delayed Job +* QueueClassic We would like to have adapters for: -* QueueClassic * Sneakers diff --git a/Rakefile b/Rakefile index 5242926c7f..2b8b81248d 100644 --- a/Rakefile +++ b/Rakefile @@ -20,11 +20,11 @@ task :default => :test desc 'Run all adapter tests' task :test do - tasks = %w(test_inline test_resque test_sidekiq test_sucker_punch test_delayed_job) + tasks = %w(test_inline test_resque test_sidekiq test_sucker_punch test_delayed_job test_queue_classic) run_without_aborting(*tasks) end -%w(inline resque sidekiq sucker_punch delayed_job).each do |adapter| +%w(inline resque sidekiq sucker_punch delayed_job queue_classic).each do |adapter| Rake::TestTask.new("test_#{adapter}") do |t| t.libs << 'test' t.test_files = FileList['test/cases/**/*_test.rb'] diff --git a/lib/active_job/queue_adapters/queue_classic_adapter.rb b/lib/active_job/queue_adapters/queue_classic_adapter.rb new file mode 100644 index 0000000000..e3392a646e --- /dev/null +++ b/lib/active_job/queue_adapters/queue_classic_adapter.rb @@ -0,0 +1,20 @@ +require 'queue_classic' + +module ActiveJob + module QueueAdapters + class QueueClassicAdapter + class << self + def queue(job, *args) + qc_queue = QC::Queue.new(job.queue_name) + qc_queue.enqueue("ActiveJob::QueueAdapters::QueueClassicAdapter::JobWrapper.perform", job, *args) + end + end + + class JobWrapper + def self.perform(job, *args) + job.new.perform *Parameters.deserialize(args) + end + end + end + end +end diff --git a/test/adapters/queue_classic.rb b/test/adapters/queue_classic.rb new file mode 100644 index 0000000000..ad5ced3cc2 --- /dev/null +++ b/test/adapters/queue_classic.rb @@ -0,0 +1,2 @@ +require 'support/queue_classic/inline' +ActiveJob::Base.queue_adapter = :queue_classic diff --git a/test/support/queue_classic/inline.rb b/test/support/queue_classic/inline.rb new file mode 100644 index 0000000000..5e9c295e01 --- /dev/null +++ b/test/support/queue_classic/inline.rb @@ -0,0 +1,11 @@ +require 'queue_classic' + +module QC + class Queue + def enqueue(method, *args) + receiver_str, _, message = method.rpartition('.') + receiver = eval(receiver_str) + receiver.send(message, *args) + end + end +end -- cgit v1.2.3 From 3648838173a1d7217560ed1cf8ab2a217ccbc6d1 Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Mon, 19 May 2014 15:27:28 -0700 Subject: Implement enqueue_at/enqueue_in Delayed jobs are supported by all systems except QueueClassic. For it I decided to raise NotImplementedError. The inline implementation is a bit rough. --- Gemfile | 1 + Gemfile.lock | 7 +++++++ lib/active_job/enqueuing.rb | 20 ++++++++++++++++++++ lib/active_job/queue_adapters/delayed_job_adapter.rb | 6 +++++- lib/active_job/queue_adapters/inline_adapter.rb | 15 ++++++++++++++- .../queue_adapters/queue_classic_adapter.rb | 4 ++++ lib/active_job/queue_adapters/resque_adapter.rb | 6 ++++++ lib/active_job/queue_adapters/sidekiq_adapter.rb | 7 +++++++ .../queue_adapters/sucker_punch_adapter.rb | 14 ++++++++++++++ test/cases/queuing_test.rb | 9 +++++++++ 10 files changed, 87 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 550da15615..cc2d17b5b7 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,7 @@ gemspec gem 'rake' gem 'resque' +gem 'resque-scheduler' gem 'sidekiq' gem 'sucker_punch' gem 'delayed_job' diff --git a/Gemfile.lock b/Gemfile.lock index c1df0ebc36..17f0d032e3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -47,6 +47,12 @@ GEM redis-namespace (~> 1.2) sinatra (>= 0.9.2) vegas (~> 0.1.2) + resque-scheduler (2.2.0) + redis (>= 3.0.0) + resque (>= 1.20.0, < 1.25) + rufus-scheduler (~> 2.0) + rufus-scheduler (2.0.24) + tzinfo (>= 0.3.22) sidekiq (3.0.2) celluloid (>= 0.15.2) connection_pool (>= 2.0.0) @@ -76,5 +82,6 @@ DEPENDENCIES queue_classic rake resque + resque-scheduler sidekiq sucker_punch diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index 324385216e..a5a50d69db 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -13,5 +13,25 @@ module ActiveJob def enqueue(*args) queue_adapter.queue self, *Parameters.serialize(args) end + + ## + # Enqueue a job to be performed at +interval+ from now. + # + # enqueue_in(1.week, "mike") + # + # Returns truthy if a job was scheduled. + def enqueue_in(interval, *args) + enqueue_at(interval.from_now, *args) + end + + ## + # Enqueue a job to be performed at an explicit point in time. + # + # enqueue_at(Date.tomorrow.midnight, "mike") + # + # Returns truthy if a job was scheduled. + def enqueue_at(timestamp, *args) + queue_adapter.queue_at self, timestamp.to_f, *Parameters.serialize(args) + end end end diff --git a/lib/active_job/queue_adapters/delayed_job_adapter.rb b/lib/active_job/queue_adapters/delayed_job_adapter.rb index 14072e2801..214733e3a6 100644 --- a/lib/active_job/queue_adapters/delayed_job_adapter.rb +++ b/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -7,8 +7,12 @@ module ActiveJob def queue(job, *args) JobWrapper.new.delay(queue: job.queue_name).perform(job, *args) end + + def queue_at(job, timestamp, *args) + JobWrapper.new.delay(queue: job.queue_name, run_at: timestamp).perform(job, *args) + end end - + class JobWrapper def perform(job, *args) job.new.perform *Parameters.deserialize(args) diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index cffa55af82..dd4b3f4fc0 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -5,7 +5,20 @@ module ActiveJob def queue(job, *args) job.new.perform *Parameters.deserialize(args) end + + def queue_at(job, ts, *args) + # TODO better error handling? + Thread.new do + begin + interval = Time.now.to_f - ts + sleep(interval) if interval > 0 + job.new.perform *Parameters.deserialize(args) + rescue => ex + puts ex.message + end + end + end end end end -end \ No newline at end of file +end diff --git a/lib/active_job/queue_adapters/queue_classic_adapter.rb b/lib/active_job/queue_adapters/queue_classic_adapter.rb index e3392a646e..38c04ca5c9 100644 --- a/lib/active_job/queue_adapters/queue_classic_adapter.rb +++ b/lib/active_job/queue_adapters/queue_classic_adapter.rb @@ -8,6 +8,10 @@ module ActiveJob qc_queue = QC::Queue.new(job.queue_name) qc_queue.enqueue("ActiveJob::QueueAdapters::QueueClassicAdapter::JobWrapper.perform", job, *args) end + + def queue_at(job, timestamp, *args) + raise NotImplementedError + end end class JobWrapper diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index 6686f10593..8fa8dddd11 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -1,6 +1,7 @@ require 'resque' require 'active_support/core_ext/enumerable' require 'active_support/core_ext/array/access' +require 'resque_scheduler' module ActiveJob module QueueAdapters @@ -9,6 +10,11 @@ module ActiveJob def queue(job, *args) Resque.enqueue JobWrapper.new(job), job, *args end + + def queue_at(job, timestamp, *args) + # requires resque-scheduler + Resque.enqueue_at timestamp, JobWrapper.new(job), job, *args + end end class JobWrapper diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index c8fac32963..41f58f554a 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -7,6 +7,13 @@ module ActiveJob def queue(job, *args) JobWrapper.client_push class: JobWrapper, queue: job.queue_name, args: [ job, *args ] end + + def queue_at(job, timestamp, *args) + job = { class: JobWrapper, queue: job.queue_name, args: [ job, *args ], at: timestamp } + # Optimization to enqueue something now that is scheduled to go out now or in the past + job.delete(:at) if timestamp <= Time.now.to_f + JobWrapper.client_push(job) + end end class JobWrapper diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index 182b5ce018..713da08359 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -7,6 +7,16 @@ module ActiveJob def queue(job, *args) JobWrapper.new.async.perform(job, *args) end + + def queue_at(job, timestamp, *args) + secs = Time.now.to_f - timestamp + if secs < 1 + # Optimization to enqueue something now that is scheduled to go out now or in the past + JobWrapper.new.async.perform(job, *args) + else + JobWrapper.new.async.later(secs, job, *args) + end + end end class JobWrapper @@ -15,6 +25,10 @@ module ActiveJob def perform(job_name, *args) job_name.new.perform *Parameters.deserialize(args) end + + def later(sec, job_name, *args) + after(sec) { p args; perform(job_name, *args) } + end end end end diff --git a/test/cases/queuing_test.rb b/test/cases/queuing_test.rb index b6180a23dd..f29f627da1 100644 --- a/test/cases/queuing_test.rb +++ b/test/cases/queuing_test.rb @@ -1,5 +1,6 @@ require 'helper' require 'jobs/hello_job' +require 'active_support/core_ext/numeric/time' class QueuingTest < ActiveSupport::TestCase @@ -16,4 +17,12 @@ class QueuingTest < ActiveSupport::TestCase HelloJob.enqueue "Jamie" assert_equal "Jamie says hello", $BUFFER.pop end + + test 'run queued job later' do + begin + result = HelloJob.enqueue_at 1.second.ago, "Jamie" + assert_not_nil result + rescue NotImplementedError + end + end end -- cgit v1.2.3 From 23a87236cc36f3852c07aecad94ae540fa5de3c9 Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Mon, 19 May 2014 15:30:31 -0700 Subject: remove debugging --- lib/active_job/queue_adapters/sucker_punch_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index 713da08359..63019e37c0 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -27,7 +27,7 @@ module ActiveJob end def later(sec, job_name, *args) - after(sec) { p args; perform(job_name, *args) } + after(sec) { perform(job_name, *args) } end end end -- cgit v1.2.3 From 1b71fe59b8cd6c7347729703c064b64866b3502e Mon Sep 17 00:00:00 2001 From: John DeSilva Date: Tue, 20 May 2014 00:56:08 +0200 Subject: Add Sneakers wrapper --- Gemfile | 1 + Gemfile.lock | 14 +++++++++++++ README.md | 3 --- Rakefile | 4 ++-- lib/active_job/queue_adapters/sneakers_adapter.rb | 24 +++++++++++++++++++++++ test/adapters/sneakers.rb | 2 ++ test/support/sneakers/inline.rb | 12 ++++++++++++ 7 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 lib/active_job/queue_adapters/sneakers_adapter.rb create mode 100644 test/adapters/sneakers.rb create mode 100644 test/support/sneakers/inline.rb diff --git a/Gemfile b/Gemfile index 550da15615..e40962dc92 100644 --- a/Gemfile +++ b/Gemfile @@ -8,3 +8,4 @@ gem 'sidekiq' gem 'sucker_punch' gem 'delayed_job' gem 'queue_classic' +gem 'sneakers', '0.1.1.pre' diff --git a/Gemfile.lock b/Gemfile.lock index c1df0ebc36..5f5745b53b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -20,7 +20,10 @@ GEM minitest (~> 5.1) thread_safe (~> 0.1) tzinfo (~> 1.1) + amq-protocol (1.9.2) builder (3.2.2) + bunny (1.1.9) + amq-protocol (>= 1.9.2) celluloid (0.15.2) timers (~> 1.1.0) connection_pool (2.0.0) @@ -47,18 +50,28 @@ GEM redis-namespace (~> 1.2) sinatra (>= 0.9.2) vegas (~> 0.1.2) + serverengine (1.5.7) + sigdump (~> 0.2.2) sidekiq (3.0.2) celluloid (>= 0.15.2) connection_pool (>= 2.0.0) json redis (>= 3.0.6) redis-namespace (>= 1.3.1) + sigdump (0.2.2) sinatra (1.4.4) rack (~> 1.4) rack-protection (~> 1.4) tilt (~> 1.3, >= 1.3.4) + sneakers (0.1.1.pre) + bunny (~> 1.1.3) + serverengine + thor + thread sucker_punch (1.0.5) celluloid (~> 0.15.2) + thor (0.19.1) + thread (0.1.4) thread_safe (0.3.3) tilt (1.4.1) timers (1.1.0) @@ -77,4 +90,5 @@ DEPENDENCIES rake resque sidekiq + sneakers (= 0.1.1.pre) sucker_punch diff --git a/README.md b/README.md index a0e5f01e29..588f0a2a2e 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,6 @@ We currently have adapters for: * Sucker Punch * Delayed Job * QueueClassic - -We would like to have adapters for: - * Sneakers diff --git a/Rakefile b/Rakefile index 2b8b81248d..e0d8e8e0d8 100644 --- a/Rakefile +++ b/Rakefile @@ -20,11 +20,11 @@ task :default => :test desc 'Run all adapter tests' task :test do - tasks = %w(test_inline test_resque test_sidekiq test_sucker_punch test_delayed_job test_queue_classic) + tasks = %w(test_inline test_resque test_sidekiq test_sucker_punch test_delayed_job test_queue_classic test_sneakers) run_without_aborting(*tasks) end -%w(inline resque sidekiq sucker_punch delayed_job queue_classic).each do |adapter| +%w(inline resque sidekiq sucker_punch delayed_job queue_classic sneakers).each do |adapter| Rake::TestTask.new("test_#{adapter}") do |t| t.libs << 'test' t.test_files = FileList['test/cases/**/*_test.rb'] diff --git a/lib/active_job/queue_adapters/sneakers_adapter.rb b/lib/active_job/queue_adapters/sneakers_adapter.rb new file mode 100644 index 0000000000..be400dc477 --- /dev/null +++ b/lib/active_job/queue_adapters/sneakers_adapter.rb @@ -0,0 +1,24 @@ +require 'sneakers' + +module ActiveJob + module QueueAdapters + class SneakersAdapter + class << self + def queue(job, *args) + JobWrapper.enqueue([job, *args]) + end + end + + class JobWrapper + include Sneakers::Worker + + self.from_queue("queue", {}) + + def work(*args) + job_name = args.shift + job_name.new.perform *Parameters.deserialize(args) + end + end + end + end +end diff --git a/test/adapters/sneakers.rb b/test/adapters/sneakers.rb new file mode 100644 index 0000000000..204166a700 --- /dev/null +++ b/test/adapters/sneakers.rb @@ -0,0 +1,2 @@ +require 'support/sneakers/inline' +ActiveJob::Base.queue_adapter = :sneakers diff --git a/test/support/sneakers/inline.rb b/test/support/sneakers/inline.rb new file mode 100644 index 0000000000..16d9b830fa --- /dev/null +++ b/test/support/sneakers/inline.rb @@ -0,0 +1,12 @@ +require 'sneakers' + +module Sneakers + module Worker + module ClassMethods + def enqueue(msg) + worker = self.new(nil, nil, {}) + worker.work(*msg) + end + end + end +end -- cgit v1.2.3 From c813a30c5a3031108bdd41b57571803c13f95569 Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Mon, 19 May 2014 21:08:18 -0700 Subject: Reimplement Sidekiq worker This better integrates various Sidekiq features into AJ jobs. Things like the JID will be set as expected and the user can use `sidekiq_options` in AJ::Base subclasses as usual to configure various features. --- lib/active_job/queue_adapters/sidekiq_adapter.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index c8fac32963..087e833d24 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -5,7 +5,8 @@ module ActiveJob class SidekiqAdapter class << self def queue(job, *args) - JobWrapper.client_push class: JobWrapper, queue: job.queue_name, args: [ job, *args ] + item = { 'class' => JobWrapper, 'queue' => job.queue_name, 'args' => [job, *args] } + Sidekiq::Client.push(job.get_sidekiq_options.merge(item)) end end @@ -13,9 +14,15 @@ module ActiveJob include Sidekiq::Worker def perform(job_name, *args) - job_name.constantize.new.perform *Parameters.deserialize(args) + instance = job_name.constantize.new + instance.jid = self.jid + instance.perform *Parameters.deserialize(args) end end end end end + +class ActiveJob::Base + include Sidekiq::Worker +end -- cgit v1.2.3 From 6f059b0b054e824c4634c65e21b0241dec11200d Mon Sep 17 00:00:00 2001 From: Larry Lv Date: Tue, 20 May 2014 12:51:44 +0800 Subject: Update README for currently supported adapters. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 588f0a2a2e..ca6e2c1e4b 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Set the queue adapter for Active Job: ``` ruby ActiveJob::Base.queue_adapter = :inline # default queue adapter -# Adapters currently supported: :resque, :sidekiq, :sucker_punch, :delayed_job +# Adapters currently supported: :resque, :sidekiq, :sucker_punch, :delayed_job, :queue_classic, :sneakers ``` Declare a job like so: -- cgit v1.2.3 From fc062f6601f6dba5a449d2e7008840bba6cb02e6 Mon Sep 17 00:00:00 2001 From: zhouguangming Date: Tue, 20 May 2014 16:26:04 +0800 Subject: Make consistent code style --- lib/active_job/queue_adapters/sneakers_adapter.rb | 5 ++--- lib/active_job/queue_adapters/sucker_punch_adapter.rb | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/active_job/queue_adapters/sneakers_adapter.rb b/lib/active_job/queue_adapters/sneakers_adapter.rb index be400dc477..c6dbfa75bf 100644 --- a/lib/active_job/queue_adapters/sneakers_adapter.rb +++ b/lib/active_job/queue_adapters/sneakers_adapter.rb @@ -14,9 +14,8 @@ module ActiveJob self.from_queue("queue", {}) - def work(*args) - job_name = args.shift - job_name.new.perform *Parameters.deserialize(args) + def work(job, *args) + job.new.perform *Parameters.deserialize(args) end end end diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index 182b5ce018..6ca6726456 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -12,8 +12,8 @@ module ActiveJob class JobWrapper include SuckerPunch::Job - def perform(job_name, *args) - job_name.new.perform *Parameters.deserialize(args) + def perform(job, *args) + job.new.perform *Parameters.deserialize(args) end end end -- cgit v1.2.3 From 9b5562a4bfcab0e5634c46ba5b5831246d866e0c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 20 May 2014 11:37:04 +0200 Subject: Determine full class name dynamically in QC adapter. --- lib/active_job/queue_adapters/queue_classic_adapter.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/active_job/queue_adapters/queue_classic_adapter.rb b/lib/active_job/queue_adapters/queue_classic_adapter.rb index e3392a646e..b82edae977 100644 --- a/lib/active_job/queue_adapters/queue_classic_adapter.rb +++ b/lib/active_job/queue_adapters/queue_classic_adapter.rb @@ -5,8 +5,7 @@ module ActiveJob class QueueClassicAdapter class << self def queue(job, *args) - qc_queue = QC::Queue.new(job.queue_name) - qc_queue.enqueue("ActiveJob::QueueAdapters::QueueClassicAdapter::JobWrapper.perform", job, *args) + QC::Queue.new(job.queue_name).enqueue("#{JobWrapper.name}.perform", job, *args) end end -- cgit v1.2.3 From 6d009504a02fecb822820e18abd69dd569f72e82 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Mon, 19 May 2014 18:09:45 +0000 Subject: Add Que Adapter/Wrapper --- Gemfile | 1 + Gemfile.lock | 12 +++++++----- README.md | 18 ++++++++++-------- Rakefile | 4 ++-- lib/active_job/queue_adapters/que_adapter.rb | 19 +++++++++++++++++++ test/adapters/que.rb | 2 ++ test/cases/adapter_test.rb | 26 ++++++++++++++++++-------- 7 files changed, 59 insertions(+), 23 deletions(-) create mode 100644 lib/active_job/queue_adapters/que_adapter.rb create mode 100644 test/adapters/que.rb diff --git a/Gemfile b/Gemfile index e40962dc92..a9803f9284 100644 --- a/Gemfile +++ b/Gemfile @@ -9,3 +9,4 @@ gem 'sucker_punch' gem 'delayed_job' gem 'queue_classic' gem 'sneakers', '0.1.1.pre' +gem 'que' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 5f5745b53b..ff4e994b44 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -33,21 +33,22 @@ GEM json (1.8.1) minitest (5.3.4) mono_logger (1.1.0) - multi_json (1.9.3) + multi_json (1.10.1) pg (0.17.1) + que (0.7.3) queue_classic (2.2.3) pg (~> 0.17.0) rack (1.5.2) - rack-protection (1.5.2) + rack-protection (1.5.3) rack rake (10.3.2) redis (3.0.7) redis-namespace (1.4.1) redis (~> 3.0.4) - resque (1.24.1) + resque (1.25.2) mono_logger (~> 1.0) multi_json (~> 1.0) - redis-namespace (~> 1.2) + redis-namespace (~> 1.3) sinatra (>= 0.9.2) vegas (~> 0.1.2) serverengine (1.5.7) @@ -59,7 +60,7 @@ GEM redis (>= 3.0.6) redis-namespace (>= 1.3.1) sigdump (0.2.2) - sinatra (1.4.4) + sinatra (1.4.5) rack (~> 1.4) rack-protection (~> 1.4) tilt (~> 1.3, >= 1.3.4) @@ -86,6 +87,7 @@ PLATFORMS DEPENDENCIES activejob! delayed_job + que queue_classic rake resque diff --git a/README.md b/README.md index ca6e2c1e4b..e8e12e859c 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ of the request-response cycle, so the user doesn't have to wait on it. The main point is to ensure that all Rails apps will have a job infrastructure in place, even if it's in the form of an "immediate runner". We can then have framework features and other gems build on top of that, without having to worry -about API differences between Delayed Job and Resque. Picking your queuing +about API differences between Delayed Job and Resque. Picking your queuing backend becomes more of an operational concern, then. And you'll be able to switch between them without having to rewrite your jobs. @@ -24,7 +24,8 @@ Set the queue adapter for Active Job: ``` ruby ActiveJob::Base.queue_adapter = :inline # default queue adapter -# Adapters currently supported: :resque, :sidekiq, :sucker_punch, :delayed_job, :queue_classic, :sneakers +# Adapters currently supported: :delayed_job, :que, :queue_classic, :resque, +# :sidekiq, :sneakers, :sucker_punch ``` Declare a job like so: @@ -79,12 +80,13 @@ by default has been mixed into Active Record classes. We currently have adapters for: -* Resque 1.x -* Sidekiq -* Sucker Punch -* Delayed Job -* QueueClassic -* Sneakers +* [Delayed Job](https://github.com/collectiveidea/delayed_job) +* [Que](https://github.com/chanks/que) +* [QueueClassic](https://github.com/ryandotsmith/queue_classic) +* [Resque 1.x](https://github.com/resque/resque) +* [Sidekiq](https://github.com/mperham/sidekiq) +* [Sneakers](https://github.com/jondot/sneakers) +* [Sucker Punch](https://github.com/brandonhilkert/sucker_punch) ## Under development as a gem, targeted for Rails inclusion diff --git a/Rakefile b/Rakefile index e0d8e8e0d8..933c049a68 100644 --- a/Rakefile +++ b/Rakefile @@ -20,11 +20,11 @@ task :default => :test desc 'Run all adapter tests' task :test do - tasks = %w(test_inline test_resque test_sidekiq test_sucker_punch test_delayed_job test_queue_classic test_sneakers) + tasks = %w(test_inline test_delayed_job test_que test_queue_classic test_resque test_sidekiq test_sneakers test_sucker_punch) run_without_aborting(*tasks) end -%w(inline resque sidekiq sucker_punch delayed_job queue_classic sneakers).each do |adapter| +%w(inline delayed_job que queue_classic resque sidekiq sneakers sucker_punch).each do |adapter| Rake::TestTask.new("test_#{adapter}") do |t| t.libs << 'test' t.test_files = FileList['test/cases/**/*_test.rb'] diff --git a/lib/active_job/queue_adapters/que_adapter.rb b/lib/active_job/queue_adapters/que_adapter.rb new file mode 100644 index 0000000000..6750882b91 --- /dev/null +++ b/lib/active_job/queue_adapters/que_adapter.rb @@ -0,0 +1,19 @@ +require 'que' + +module ActiveJob + module QueueAdapters + class QueAdapter + class << self + def queue(job, *args) + JobWrapper.enqueue job, *args, queue: job.queue_name + end + end + + class JobWrapper < Que::Job + def run(job, *args) + job.new.perform *Parameters.deserialize(args) + end + end + end + end +end diff --git a/test/adapters/que.rb b/test/adapters/que.rb new file mode 100644 index 0000000000..640061bf54 --- /dev/null +++ b/test/adapters/que.rb @@ -0,0 +1,2 @@ +ActiveJob::Base.queue_adapter = :que +Que.mode = :sync diff --git a/test/cases/adapter_test.rb b/test/cases/adapter_test.rb index 05efdc33e9..e922750ab4 100644 --- a/test/cases/adapter_test.rb +++ b/test/cases/adapter_test.rb @@ -10,26 +10,36 @@ class AdapterTest < ActiveSupport::TestCase assert_equal ActiveJob::QueueAdapters::InlineAdapter, ActiveJob::Base.queue_adapter end - test 'should load resque adapter' do + test 'should load Delayed Job adapter' do + ActiveJob::Base.queue_adapter = :delayed_job + assert_equal ActiveJob::QueueAdapters::DelayedJobAdapter, ActiveJob::Base.queue_adapter + end + + test 'should load Que adapter' do + ActiveJob::Base.queue_adapter = :que + assert_equal ActiveJob::QueueAdapters::QueAdapter, ActiveJob::Base.queue_adapter + end + + test 'should load Queue Classic adapter' do + ActiveJob::Base.queue_adapter = :queue_classic + assert_equal ActiveJob::QueueAdapters::QueueClassicAdapter, ActiveJob::Base.queue_adapter + end + + test 'should load Resque adapter' do ActiveJob::Base.queue_adapter = :resque assert_equal ActiveJob::QueueAdapters::ResqueAdapter, ActiveJob::Base.queue_adapter end - test 'should load sidekiq adapter' do + test 'should load Sidekiq adapter' do ActiveJob::Base.queue_adapter = :sidekiq assert_equal ActiveJob::QueueAdapters::SidekiqAdapter, ActiveJob::Base.queue_adapter end - test 'should load sucker punch adapter' do + test 'should load Sucker Punch adapter' do ActiveJob::Base.queue_adapter = :sucker_punch assert_equal ActiveJob::QueueAdapters::SuckerPunchAdapter, ActiveJob::Base.queue_adapter end - test 'should load delayed_job adapter' do - ActiveJob::Base.queue_adapter = :delayed_job - assert_equal ActiveJob::QueueAdapters::DelayedJobAdapter, ActiveJob::Base.queue_adapter - end - def teardown ActiveJob::Base.queue_adapter = @old_adapter end -- cgit v1.2.3 From fa478329507319571b578ea3dd883405d0b8d4fc Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 12:05:26 +0200 Subject: No need for the local variable --- lib/active_job/queue_adapter.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/active_job/queue_adapter.rb b/lib/active_job/queue_adapter.rb index 2033f6fe56..0f510530a6 100644 --- a/lib/active_job/queue_adapter.rb +++ b/lib/active_job/queue_adapter.rb @@ -6,14 +6,13 @@ module ActiveJob mattr_reader(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter } def queue_adapter=(name_or_adapter) - case name_or_adapter - when Symbol, String - adapter = load_adapter(name_or_adapter) - else - adapter = name_or_adapter - end - - @@queue_adapter = adapter + @@queue_adapter = \ + case name_or_adapter + when Symbol, String + load_adapter(name_or_adapter) + else + name_or_adapter + end end private -- cgit v1.2.3 From 9c8c6bf4390a60be15fa69dfdd682f82e8639bc7 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 12:06:59 +0200 Subject: Clarify that the other option for name_or_adapter is to be a class (the Adapter class) --- lib/active_job/queue_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_job/queue_adapter.rb b/lib/active_job/queue_adapter.rb index 0f510530a6..8f2f8b86ea 100644 --- a/lib/active_job/queue_adapter.rb +++ b/lib/active_job/queue_adapter.rb @@ -10,7 +10,7 @@ module ActiveJob case name_or_adapter when Symbol, String load_adapter(name_or_adapter) - else + when Class name_or_adapter end end -- cgit v1.2.3 From 34f0e4240e7c7f18c1925dcd8d33e1282aec6e60 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 12:19:14 +0200 Subject: Update README with queue_as example and the desire for a Resque 2.x adapter --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index e8e12e859c..1be57a1808 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,8 @@ Declare a job like so: ```ruby class MyJob < ActiveJob::Base + queue_as :my_jobs + def perform(record) record.do_work end @@ -88,6 +90,10 @@ We currently have adapters for: * [Sneakers](https://github.com/jondot/sneakers) * [Sucker Punch](https://github.com/brandonhilkert/sucker_punch) +We would like to have adapters for: + +* [Resque 2.x](https://github.com/resque/resque) (see [#7](https://github.com/rails/activejob/issues/7)) + ## Under development as a gem, targeted for Rails inclusion -- cgit v1.2.3 From 2d19c711d041dd955d3fcaf952d101df9241a65e Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 12:22:20 +0200 Subject: Add Sneakers test and inline setup/teardown --- test/cases/adapter_test.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/cases/adapter_test.rb b/test/cases/adapter_test.rb index e922750ab4..1a08fa2617 100644 --- a/test/cases/adapter_test.rb +++ b/test/cases/adapter_test.rb @@ -1,9 +1,8 @@ require 'helper' class AdapterTest < ActiveSupport::TestCase - def setup - @old_adapter = ActiveJob::Base.queue_adapter - end + setup { @old_adapter = ActiveJob::Base.queue_adapter } + teardown { ActiveJob::Base.queue_adapter = @old_adapter } test 'should load inline adapter' do ActiveJob::Base.queue_adapter = :inline @@ -40,7 +39,8 @@ class AdapterTest < ActiveSupport::TestCase assert_equal ActiveJob::QueueAdapters::SuckerPunchAdapter, ActiveJob::Base.queue_adapter end - def teardown - ActiveJob::Base.queue_adapter = @old_adapter + test 'should load Sneakers adapter' do + ActiveJob::Base.queue_adapter = :sneakers + assert_equal ActiveJob::QueueAdapters::SneakersAdapter, ActiveJob::Base.queue_adapter end end -- cgit v1.2.3 From 243afc061cf527bebf6c76956c53c5cb637876c6 Mon Sep 17 00:00:00 2001 From: Dimitar Kostov Date: Tue, 20 May 2014 14:03:46 +0300 Subject: Adds backburner adapter --- Gemfile | 3 ++- Gemfile.lock | 6 ++++++ Rakefile | 4 ++-- lib/active_job/queue_adapters/backburner_adapter.rb | 21 +++++++++++++++++++++ test/adapters/backburner.rb | 3 +++ test/cases/adapter_test.rb | 5 +++++ test/support/backburner/inline.rb | 8 ++++++++ 7 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 lib/active_job/queue_adapters/backburner_adapter.rb create mode 100644 test/adapters/backburner.rb create mode 100644 test/support/backburner/inline.rb diff --git a/Gemfile b/Gemfile index a9803f9284..582214dd65 100644 --- a/Gemfile +++ b/Gemfile @@ -9,4 +9,5 @@ gem 'sucker_punch' gem 'delayed_job' gem 'queue_classic' gem 'sneakers', '0.1.1.pre' -gem 'que' \ No newline at end of file +gem 'que' +gem 'backburner' diff --git a/Gemfile.lock b/Gemfile.lock index ff4e994b44..74758c73fe 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -21,12 +21,17 @@ GEM thread_safe (~> 0.1) tzinfo (~> 1.1) amq-protocol (1.9.2) + backburner (0.4.5) + beaneater (~> 0.3.1) + dante (~> 0.1.5) + beaneater (0.3.2) builder (3.2.2) bunny (1.1.9) amq-protocol (>= 1.9.2) celluloid (0.15.2) timers (~> 1.1.0) connection_pool (2.0.0) + dante (0.1.5) delayed_job (4.0.1) activesupport (>= 3.0, < 4.2) i18n (0.6.9) @@ -86,6 +91,7 @@ PLATFORMS DEPENDENCIES activejob! + backburner delayed_job que queue_classic diff --git a/Rakefile b/Rakefile index 933c049a68..8ab372e08c 100644 --- a/Rakefile +++ b/Rakefile @@ -20,11 +20,11 @@ task :default => :test desc 'Run all adapter tests' task :test do - tasks = %w(test_inline test_delayed_job test_que test_queue_classic test_resque test_sidekiq test_sneakers test_sucker_punch) + tasks = %w(test_inline test_delayed_job test_que test_queue_classic test_resque test_sidekiq test_sneakers test_sucker_punch test_backburner) run_without_aborting(*tasks) end -%w(inline delayed_job que queue_classic resque sidekiq sneakers sucker_punch).each do |adapter| +%w(inline delayed_job que queue_classic resque sidekiq sneakers sucker_punch backburner).each do |adapter| Rake::TestTask.new("test_#{adapter}") do |t| t.libs << 'test' t.test_files = FileList['test/cases/**/*_test.rb'] diff --git a/lib/active_job/queue_adapters/backburner_adapter.rb b/lib/active_job/queue_adapters/backburner_adapter.rb new file mode 100644 index 0000000000..0ac745c7f2 --- /dev/null +++ b/lib/active_job/queue_adapters/backburner_adapter.rb @@ -0,0 +1,21 @@ +require 'backburner' + +module ActiveJob + module QueueAdapters + class BackburnerAdapter + class << self + def queue(job, *args) + Backburner::Worker.enqueue JobWrapper, [ job.name, *args ], queue: job.queue_name + end + end + + class JobWrapper + class << self + def perform(job_name, *args) + job_name.constantize.new.perform *Parameters.deserialize(args) + end + end + end + end + end +end diff --git a/test/adapters/backburner.rb b/test/adapters/backburner.rb new file mode 100644 index 0000000000..65d05f850b --- /dev/null +++ b/test/adapters/backburner.rb @@ -0,0 +1,3 @@ +require 'support/backburner/inline' + +ActiveJob::Base.queue_adapter = :backburner \ No newline at end of file diff --git a/test/cases/adapter_test.rb b/test/cases/adapter_test.rb index 1a08fa2617..703058dacb 100644 --- a/test/cases/adapter_test.rb +++ b/test/cases/adapter_test.rb @@ -43,4 +43,9 @@ class AdapterTest < ActiveSupport::TestCase ActiveJob::Base.queue_adapter = :sneakers assert_equal ActiveJob::QueueAdapters::SneakersAdapter, ActiveJob::Base.queue_adapter end + + test 'should load Backburner adapter' do + ActiveJob::Base.queue_adapter = :backburner + assert_equal ActiveJob::QueueAdapters::BackburnerAdapter, ActiveJob::Base.queue_adapter + end end diff --git a/test/support/backburner/inline.rb b/test/support/backburner/inline.rb new file mode 100644 index 0000000000..f761b53e27 --- /dev/null +++ b/test/support/backburner/inline.rb @@ -0,0 +1,8 @@ +require 'backburner' + +Backburner::Worker.class_eval do + class << self; alias_method :original_enqueue, :enqueue; end + def self.enqueue(job_class, args=[], opts={}) + job_class.perform(*args) + end +end \ No newline at end of file -- cgit v1.2.3 From 0227af92b1851d5eac4cb423dc0c9935dbc733a3 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Tue, 20 May 2014 14:41:14 +0300 Subject: Added logging capabilities --- lib/active_job/base.rb | 5 ++++- lib/active_job/enqueuing.rb | 1 + lib/active_job/log_subscriber.rb | 19 +++++++++++++++++++ lib/active_job/logging.rb | 5 +++++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 lib/active_job/log_subscriber.rb create mode 100644 lib/active_job/logging.rb diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index 77b929d4af..ed402bd8d3 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,11 +1,14 @@ require 'active_job/queue_adapter' require 'active_job/queue_name' require 'active_job/enqueuing' +require 'active_job/logging' +require 'active_job/log_subscriber' module ActiveJob class Base extend QueueAdapter extend QueueName extend Enqueuing + extend Logging end -end \ No newline at end of file +end diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index 324385216e..46f703481a 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -11,6 +11,7 @@ module ActiveJob # The return value is adapter-specific and may change in a future # ActiveJob release. def enqueue(*args) + ActiveSupport::Notifications.instrument "enqueue.active_job", adapter: queue_adapter, job: self, params: args queue_adapter.queue self, *Parameters.serialize(args) end end diff --git a/lib/active_job/log_subscriber.rb b/lib/active_job/log_subscriber.rb new file mode 100644 index 0000000000..81859d8da1 --- /dev/null +++ b/lib/active_job/log_subscriber.rb @@ -0,0 +1,19 @@ +module ActiveJob + class LogSubscriber < ActiveSupport::LogSubscriber + + def enqueue(event) + payload = event.payload + params = payload[:params] + adapter = payload[:adapter] + job = payload[:job] + + info "ActiveJob enqueued to #{adapter.name.demodulize} job #{job.name}: #{params.inspect}" + end + + def logger + ActiveJob::Base.logger + end + end +end + +ActiveJob::LogSubscriber.attach_to :active_job diff --git a/lib/active_job/logging.rb b/lib/active_job/logging.rb new file mode 100644 index 0000000000..7bd77b7ca5 --- /dev/null +++ b/lib/active_job/logging.rb @@ -0,0 +1,5 @@ +module ActiveJob + module Logging + mattr_accessor(:logger) { ActiveSupport::Logger.new(STDOUT) } + end +end -- cgit v1.2.3 From 42f5ba37a4bf3cd9369643e479b91a5f97bce779 Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Tue, 20 May 2014 06:57:29 -0700 Subject: Remove all Sidekiq-specific stuff from job, enable retries by default --- lib/active_job/queue_adapters/sidekiq_adapter.rb | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index 087e833d24..0cf9e47287 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -5,8 +5,11 @@ module ActiveJob class SidekiqAdapter class << self def queue(job, *args) - item = { 'class' => JobWrapper, 'queue' => job.queue_name, 'args' => [job, *args] } - Sidekiq::Client.push(job.get_sidekiq_options.merge(item)) + item = { 'class' => JobWrapper, + 'queue' => job.queue_name, + 'args' => [job, *args], + 'retry' => true } + Sidekiq::Client.push(item) end end @@ -15,14 +18,9 @@ module ActiveJob def perform(job_name, *args) instance = job_name.constantize.new - instance.jid = self.jid instance.perform *Parameters.deserialize(args) end end end end end - -class ActiveJob::Base - include Sidekiq::Worker -end -- cgit v1.2.3 From a2650112d9971460b257dbe6d440e1641226a11e Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Tue, 20 May 2014 17:02:36 +0300 Subject: Moved log_subcriber dependency and cleanup --- lib/active_job/base.rb | 1 - lib/active_job/log_subscriber.rb | 1 - lib/active_job/logging.rb | 2 ++ 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index ed402bd8d3..3d16f38275 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -2,7 +2,6 @@ require 'active_job/queue_adapter' require 'active_job/queue_name' require 'active_job/enqueuing' require 'active_job/logging' -require 'active_job/log_subscriber' module ActiveJob class Base diff --git a/lib/active_job/log_subscriber.rb b/lib/active_job/log_subscriber.rb index 81859d8da1..31c61a6068 100644 --- a/lib/active_job/log_subscriber.rb +++ b/lib/active_job/log_subscriber.rb @@ -1,6 +1,5 @@ module ActiveJob class LogSubscriber < ActiveSupport::LogSubscriber - def enqueue(event) payload = event.payload params = payload[:params] diff --git a/lib/active_job/logging.rb b/lib/active_job/logging.rb index 7bd77b7ca5..0e994a8f54 100644 --- a/lib/active_job/logging.rb +++ b/lib/active_job/logging.rb @@ -1,3 +1,5 @@ +require 'active_job/log_subscriber' + module ActiveJob module Logging mattr_accessor(:logger) { ActiveSupport::Logger.new(STDOUT) } -- cgit v1.2.3 From cbfc8b367ed04fdbb748d0712c33b0b62d3de331 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 20 May 2014 11:32:29 +0200 Subject: Have Sneakers adapter take queue_name into account. --- lib/active_job/queue_adapters/sneakers_adapter.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/active_job/queue_adapters/sneakers_adapter.rb b/lib/active_job/queue_adapters/sneakers_adapter.rb index c6dbfa75bf..7be6b2a085 100644 --- a/lib/active_job/queue_adapters/sneakers_adapter.rb +++ b/lib/active_job/queue_adapters/sneakers_adapter.rb @@ -1,19 +1,23 @@ require 'sneakers' +require 'thread' module ActiveJob module QueueAdapters class SneakersAdapter + @mutex = Mutex.new + class << self def queue(job, *args) - JobWrapper.enqueue([job, *args]) + @mutex.synchronize do + JobWrapper.from_queue job.queue_name + JobWrapper.enqueue [ job, *args ] + end end end class JobWrapper include Sneakers::Worker - self.from_queue("queue", {}) - def work(job, *args) job.new.perform *Parameters.deserialize(args) end -- cgit v1.2.3 From 1d519aae29228186d3e82906d5d6e8dda2e91bd3 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 17:36:17 +0200 Subject: Reformat the logging line and ensure we are logging the serialized args --- lib/active_job/enqueuing.rb | 5 +++-- lib/active_job/log_subscriber.rb | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index 46f703481a..6fb6f15ce2 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -11,8 +11,9 @@ module ActiveJob # The return value is adapter-specific and may change in a future # ActiveJob release. def enqueue(*args) - ActiveSupport::Notifications.instrument "enqueue.active_job", adapter: queue_adapter, job: self, params: args - queue_adapter.queue self, *Parameters.serialize(args) + serialized_args = Parameters.serialize(args) + ActiveSupport::Notifications.instrument "enqueue.active_job", adapter: queue_adapter, job: self, args: serialized_args + queue_adapter.queue self, *serialized_args end end end diff --git a/lib/active_job/log_subscriber.rb b/lib/active_job/log_subscriber.rb index 31c61a6068..472c9f3081 100644 --- a/lib/active_job/log_subscriber.rb +++ b/lib/active_job/log_subscriber.rb @@ -1,12 +1,13 @@ +require 'active_support/core_ext/string/filters' + module ActiveJob class LogSubscriber < ActiveSupport::LogSubscriber def enqueue(event) - payload = event.payload - params = payload[:params] - adapter = payload[:adapter] - job = payload[:job] + queue_name = event.payload[:adapter].name.demodulize.remove('Adapter') + job_name = event.payload[:job].name + args = event.payload[:args].any? ? ": #{event.payload[:args].inspect}" : "" - info "ActiveJob enqueued to #{adapter.name.demodulize} job #{job.name}: #{params.inspect}" + info "Enqueued #{job_name} to #{queue_name}" + args end def logger -- cgit v1.2.3 From 1d3ba6881e03bc3096be2c68e3d28f88e3136f05 Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Tue, 20 May 2014 08:36:24 -0700 Subject: Move past time check out of adapters --- lib/active_job/enqueuing.rb | 6 +++++- lib/active_job/queue_adapters/sidekiq_adapter.rb | 2 -- lib/active_job/queue_adapters/sucker_punch_adapter.rb | 8 +------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index b2d142ee96..1e0dd58b59 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -32,7 +32,11 @@ module ActiveJob # # Returns truthy if a job was scheduled. def enqueue_at(timestamp, *args) - queue_adapter.queue_at self, timestamp.to_f, *Parameters.serialize(args) + if Time.now.to_f > timestamp + queue.adapter.queue self, *Parameters.serialize(args) + else + queue_adapter.queue_at self, timestamp.to_f, *Parameters.serialize(args) + end end end end diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index 41f58f554a..c6faf9d054 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -10,8 +10,6 @@ module ActiveJob def queue_at(job, timestamp, *args) job = { class: JobWrapper, queue: job.queue_name, args: [ job, *args ], at: timestamp } - # Optimization to enqueue something now that is scheduled to go out now or in the past - job.delete(:at) if timestamp <= Time.now.to_f JobWrapper.client_push(job) end end diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index e483c0844b..8125499695 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -9,13 +9,7 @@ module ActiveJob end def queue_at(job, timestamp, *args) - secs = Time.now.to_f - timestamp - if secs < 1 - # Optimization to enqueue something now that is scheduled to go out now or in the past - JobWrapper.new.async.perform(job, *args) - else - JobWrapper.new.async.later(secs, job, *args) - end + JobWrapper.new.async.later(secs, job, *args) end end -- cgit v1.2.3 From a88d9ebc0564111cd6c127a4c953ac4a607c2bf0 Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Tue, 20 May 2014 08:36:46 -0700 Subject: Add logging for enqueued_at and perform errors --- lib/active_job/enqueuing.rb | 1 + lib/active_job/log_subscriber.rb | 19 +++++++++++++++++++ lib/active_job/queue_adapters/inline_adapter.rb | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index 1e0dd58b59..bb681d1e32 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -32,6 +32,7 @@ module ActiveJob # # Returns truthy if a job was scheduled. def enqueue_at(timestamp, *args) + ActiveSupport::Notifications.instrument "enqueue_at.active_job", adapter: queue_adapter, timestamp: timestamp, job: self, params: args if Time.now.to_f > timestamp queue.adapter.queue self, *Parameters.serialize(args) else diff --git a/lib/active_job/log_subscriber.rb b/lib/active_job/log_subscriber.rb index 31c61a6068..2c389ed431 100644 --- a/lib/active_job/log_subscriber.rb +++ b/lib/active_job/log_subscriber.rb @@ -9,6 +9,25 @@ module ActiveJob info "ActiveJob enqueued to #{adapter.name.demodulize} job #{job.name}: #{params.inspect}" end + def enqueue_at(event) + payload = event.payload + params = payload[:params] + adapter = payload[:adapter] + job = payload[:job] + time = payload[:timestamp] + + info "ActiveJob enqueued at #{time} to #{adapter.name.demodulize} job #{job.name}: #{params.inspect}" + end + + def perform_error(event) + payload = event.payload + params = payload[:params] + job = payload[:job] + error = payload[:error] + + warn "ActiveJob caught error executing #{job} with #{params.inspect}: #{error.message}" + end + def logger ActiveJob::Base.logger end diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index dd4b3f4fc0..cad600a765 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -14,7 +14,7 @@ module ActiveJob sleep(interval) if interval > 0 job.new.perform *Parameters.deserialize(args) rescue => ex - puts ex.message + ActiveSupport::Notifications.instrument "error.perform.active_job", adapter: self, job: job, params: args, error: ex end end end -- cgit v1.2.3 From b13a3cb29b7b8823a085977dc6de801fb65acf71 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 17:37:10 +0200 Subject: Styling --- lib/active_job/queue_adapters/sidekiq_adapter.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index 0cf9e47287..43bd69790c 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -5,11 +5,11 @@ module ActiveJob class SidekiqAdapter class << self def queue(job, *args) - item = { 'class' => JobWrapper, - 'queue' => job.queue_name, - 'args' => [job, *args], - 'retry' => true } - Sidekiq::Client.push(item) + Sidekiq::Client.push \ + 'class' => JobWrapper, + 'queue' => job.queue_name, + 'args' => [ job, *args ], + 'retry' => true end end @@ -17,8 +17,7 @@ module ActiveJob include Sidekiq::Worker def perform(job_name, *args) - instance = job_name.constantize.new - instance.perform *Parameters.deserialize(args) + job_name.constantize.new.perform *Parameters.deserialize(args) end end end -- cgit v1.2.3 From 2aa9024eede567ae4daa97df62e32f57265096bc Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Tue, 20 May 2014 08:44:00 -0700 Subject: Fix boogs, stub not implemented adapters --- lib/active_job/enqueuing.rb | 9 +++++---- lib/active_job/queue_adapters/inline_adapter.rb | 2 +- lib/active_job/queue_adapters/que_adapter.rb | 4 ++++ lib/active_job/queue_adapters/sneakers_adapter.rb | 4 ++++ 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index bb681d1e32..43bce125b5 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -32,11 +32,12 @@ module ActiveJob # # Returns truthy if a job was scheduled. def enqueue_at(timestamp, *args) - ActiveSupport::Notifications.instrument "enqueue_at.active_job", adapter: queue_adapter, timestamp: timestamp, job: self, params: args - if Time.now.to_f > timestamp - queue.adapter.queue self, *Parameters.serialize(args) + ts = timestamp.to_f + ActiveSupport::Notifications.instrument "enqueue_at.active_job", adapter: queue_adapter, timestamp: ts, job: self, params: args + if Time.now.to_f > ts + queue_adapter.queue self, *Parameters.serialize(args) else - queue_adapter.queue_at self, timestamp.to_f, *Parameters.serialize(args) + queue_adapter.queue_at self, ts, *Parameters.serialize(args) end end end diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index cad600a765..4afd4f708e 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -14,7 +14,7 @@ module ActiveJob sleep(interval) if interval > 0 job.new.perform *Parameters.deserialize(args) rescue => ex - ActiveSupport::Notifications.instrument "error.perform.active_job", adapter: self, job: job, params: args, error: ex + ActiveSupport::Notifications.instrument "perform_error.active_job", adapter: self, job: job, params: args, error: ex end end end diff --git a/lib/active_job/queue_adapters/que_adapter.rb b/lib/active_job/queue_adapters/que_adapter.rb index 6750882b91..30c23a35b9 100644 --- a/lib/active_job/queue_adapters/que_adapter.rb +++ b/lib/active_job/queue_adapters/que_adapter.rb @@ -7,6 +7,10 @@ module ActiveJob def queue(job, *args) JobWrapper.enqueue job, *args, queue: job.queue_name end + + def queue_at(job, timestamp, *args) + raise NotImplementedError + end end class JobWrapper < Que::Job diff --git a/lib/active_job/queue_adapters/sneakers_adapter.rb b/lib/active_job/queue_adapters/sneakers_adapter.rb index c6dbfa75bf..ae9e49a0bf 100644 --- a/lib/active_job/queue_adapters/sneakers_adapter.rb +++ b/lib/active_job/queue_adapters/sneakers_adapter.rb @@ -7,6 +7,10 @@ module ActiveJob def queue(job, *args) JobWrapper.enqueue([job, *args]) end + + def queue_at(job, timestamp, *args) + raise NotImplementedError + end end class JobWrapper -- cgit v1.2.3 From 5adc311f05f30477199499ab5aee29117bb6b8d0 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Tue, 20 May 2014 15:59:50 +0000 Subject: Silence logger --- test/helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/helper.rb b/test/helper.rb index fc3e2642df..a5adc882d9 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -7,3 +7,5 @@ require 'active_job' require "adapters/#{ENV['AJADAPTER'] || 'inline'}" require 'active_support/testing/autorun' + +ActiveJob::Logging.logger.level = Logger::ERROR -- cgit v1.2.3 From 85880eb427191bdd46967ff3bacf95419df3ef7a Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Tue, 20 May 2014 09:05:16 -0700 Subject: Assert truthy --- test/cases/queuing_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/queuing_test.rb b/test/cases/queuing_test.rb index f29f627da1..958b81ca3a 100644 --- a/test/cases/queuing_test.rb +++ b/test/cases/queuing_test.rb @@ -21,7 +21,7 @@ class QueuingTest < ActiveSupport::TestCase test 'run queued job later' do begin result = HelloJob.enqueue_at 1.second.ago, "Jamie" - assert_not_nil result + assert result rescue NotImplementedError end end -- cgit v1.2.3 From ccd1c21fe52a8183dc4470f08dd9c85f4b53afec Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 18:14:01 +0200 Subject: Remove stray ## --- lib/active_job/enqueuing.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index 6fb6f15ce2..61734fdfb6 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -2,7 +2,6 @@ require 'active_job/parameters' module ActiveJob module Enqueuing - ## # Push a job onto the queue. The arguments must be legal JSON types # (string, int, float, nil, true, false, hash or array) or # ActiveModel::GlobalIdentication instances. Arbitrary Ruby objects -- cgit v1.2.3 From a76ec4ea5eef3069a4c1c226137cb61c2c1e23c5 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Tue, 20 May 2014 16:18:35 +0000 Subject: Update Readme.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1be57a1808..e12a589778 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ by default has been mixed into Active Record classes. We currently have adapters for: +* [Backburner](https://github.com/nesquena/backburner) * [Delayed Job](https://github.com/collectiveidea/delayed_job) * [Que](https://github.com/chanks/que) * [QueueClassic](https://github.com/ryandotsmith/queue_classic) -- cgit v1.2.3 From 0e60e8878378f29f63e8d7e5ee0455e011ac3840 Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Tue, 20 May 2014 09:37:38 -0700 Subject: More feedback --- lib/active_job/enqueuing.rb | 10 ++++------ lib/active_job/queue_adapters/inline_adapter.rb | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index fb1e004a33..5ca901516c 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -2,7 +2,7 @@ require 'active_job/parameters' module ActiveJob module Enqueuing - # + # Push a job onto the queue. The arguments must be legal JSON types # (string, int, float, nil, true, false, hash or array) or # ActiveModel::GlobalIdentication instances. Arbitrary Ruby objects @@ -16,7 +16,6 @@ module ActiveJob queue_adapter.queue self, *serialized_args end - # # Enqueue a job to be performed at +interval+ from now. # # enqueue_in(1.week, "mike") @@ -26,16 +25,15 @@ module ActiveJob enqueue_at(interval.from_now, *args) end - # # Enqueue a job to be performed at an explicit point in time. # # enqueue_at(Date.tomorrow.midnight, "mike") # # Returns truthy if a job was scheduled. def enqueue_at(timestamp, *args) - ts = timestamp.to_f - ActiveSupport::Notifications.instrument "enqueue_at.active_job", adapter: queue_adapter, timestamp: ts, job: self, args: args - queue_adapter.queue_at self, ts, *Parameters.serialize(args) + timestamp = timestamp.to_f + ActiveSupport::Notifications.instrument "enqueue_at.active_job", adapter: queue_adapter, timestamp: timestamp, job: self, args: args + queue_adapter.queue_at self, timestamp, *Parameters.serialize(args) end end end diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index 8a2c7d9d92..414a918d2b 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -7,14 +7,13 @@ module ActiveJob end def queue_at(job, ts, *args) - # TODO better error handling? Thread.new do begin interval = Time.now.to_f - ts sleep(interval) if interval > 0 job.new.perform *Parameters.deserialize(args) rescue => ex - ActiveJob::Base.logger "Error performing #{job}: #{ex.message}" + ActiveJob::Base.logger.info "Error performing #{job}: #{ex.message}" end end end -- cgit v1.2.3 From b48050d0ef4c17038457e1abc7a14dbde997b587 Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Tue, 20 May 2014 09:45:37 -0700 Subject: cleanup --- lib/active_job/queue_adapters/sucker_punch_adapter.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index 79043b06e0..7bddf5ce8a 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -9,12 +9,7 @@ module ActiveJob end def queue_at(job, timestamp, *args) - delay = Time.now.to_f - timestamp - if delay > 0 - JobWrapper.new.async.later(delay, job, *args) - else - JobWrapper.new.async.perform(job, *args) - end + JobWrapper.new.async.later(timestamp, job, *args) end end @@ -26,7 +21,8 @@ module ActiveJob end def later(sec, job_name, *args) - after(sec) { perform(job_name, *args) } + delay = Time.now.to_f - sec + after(delay > 0 ? delay : 0) { perform(job_name, *args) } end end end -- cgit v1.2.3 From 0c46094c770f233a6d3aa4660c84c9b9b4248d30 Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Tue, 20 May 2014 09:52:20 -0700 Subject: Switch to NIE --- lib/active_job/queue_adapters/sucker_punch_adapter.rb | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index 7bddf5ce8a..8a3d6d10a1 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -9,7 +9,7 @@ module ActiveJob end def queue_at(job, timestamp, *args) - JobWrapper.new.async.later(timestamp, job, *args) + raise NotImplementedError end end @@ -19,11 +19,6 @@ module ActiveJob def perform(job, *args) job.new.perform *Parameters.deserialize(args) end - - def later(sec, job_name, *args) - delay = Time.now.to_f - sec - after(delay > 0 ? delay : 0) { perform(job_name, *args) } - end end end end -- cgit v1.2.3 From 4650a7916b94adde2fbc2271ad54692573d2d3de Mon Sep 17 00:00:00 2001 From: Mike Perham Date: Tue, 20 May 2014 09:53:17 -0700 Subject: Remove unnecessary comment --- lib/active_job/queue_adapters/resque_adapter.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index 8fa8dddd11..353b3ae690 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -12,7 +12,6 @@ module ActiveJob end def queue_at(job, timestamp, *args) - # requires resque-scheduler Resque.enqueue_at timestamp, JobWrapper.new(job), job, *args end end -- cgit v1.2.3 From 373c38a6f757eff6239fda8de5a037831510ddf0 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 19:08:06 +0200 Subject: DRY up the two event loggings --- lib/active_job/log_subscriber.rb | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/active_job/log_subscriber.rb b/lib/active_job/log_subscriber.rb index eae6c30745..5f0de902b5 100644 --- a/lib/active_job/log_subscriber.rb +++ b/lib/active_job/log_subscriber.rb @@ -3,25 +3,25 @@ require 'active_support/core_ext/string/filters' module ActiveJob class LogSubscriber < ActiveSupport::LogSubscriber def enqueue(event) - queue_name = event.payload[:adapter].name.demodulize.remove('Adapter') - job_name = event.payload[:job].name - args = event.payload[:args].any? ? ": #{event.payload[:args].inspect}" : "" - - info "Enqueued #{job_name} to #{queue_name}" + args + info "Enqueued #{event.payload[:job].name} to #{queue_name(event)}" + args_info(event) end def enqueue_at(event) - queue_name = event.payload[:adapter].name.demodulize.remove('Adapter') - job_name = event.payload[:job].name - args = event.payload[:args].any? ? ": #{event.payload[:args].inspect}" : "" - time = event.payload[:timestamp] - - info "Enqueued #{job_name} to #{queue_name} at #{time}" + args + info "Enqueued #{event.payload[:job].name} to #{queue_name(event)} at #{event.payload[:timestamp]}" + args_info(event) end - def logger - ActiveJob::Base.logger - end + private + def queue_name(event) + event.payload[:adapter].name.demodulize.remove('Adapter') + end + + def args_info(event) + event.payload[:args].any? ? ": #{event.payload[:args].inspect}" : "" + end + + def logger + ActiveJob::Base.logger + end end end -- cgit v1.2.3 From fd372e66f0934617bef3b840cd0ea75429a24544 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 19:09:45 +0200 Subject: Collapse LogSubscriber into Logging --- lib/active_job/log_subscriber.rb | 28 ---------------------------- lib/active_job/logging.rb | 27 ++++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 29 deletions(-) delete mode 100644 lib/active_job/log_subscriber.rb diff --git a/lib/active_job/log_subscriber.rb b/lib/active_job/log_subscriber.rb deleted file mode 100644 index 5f0de902b5..0000000000 --- a/lib/active_job/log_subscriber.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'active_support/core_ext/string/filters' - -module ActiveJob - class LogSubscriber < ActiveSupport::LogSubscriber - def enqueue(event) - info "Enqueued #{event.payload[:job].name} to #{queue_name(event)}" + args_info(event) - end - - def enqueue_at(event) - info "Enqueued #{event.payload[:job].name} to #{queue_name(event)} at #{event.payload[:timestamp]}" + args_info(event) - end - - private - def queue_name(event) - event.payload[:adapter].name.demodulize.remove('Adapter') - end - - def args_info(event) - event.payload[:args].any? ? ": #{event.payload[:args].inspect}" : "" - end - - def logger - ActiveJob::Base.logger - end - end -end - -ActiveJob::LogSubscriber.attach_to :active_job diff --git a/lib/active_job/logging.rb b/lib/active_job/logging.rb index 0e994a8f54..5e33e0da1f 100644 --- a/lib/active_job/logging.rb +++ b/lib/active_job/logging.rb @@ -1,7 +1,32 @@ -require 'active_job/log_subscriber' +require 'active_support/core_ext/string/filters' module ActiveJob module Logging mattr_accessor(:logger) { ActiveSupport::Logger.new(STDOUT) } + + class LogSubscriber < ActiveSupport::LogSubscriber + def enqueue(event) + info "Enqueued #{event.payload[:job].name} to #{queue_name(event)}" + args_info(event) + end + + def enqueue_at(event) + info "Enqueued #{event.payload[:job].name} to #{queue_name(event)} at #{event.payload[:timestamp]}" + args_info(event) + end + + private + def queue_name(event) + event.payload[:adapter].name.demodulize.remove('Adapter') + end + + def args_info(event) + event.payload[:args].any? ? ": #{event.payload[:args].inspect}" : "" + end + + def logger + ActiveJob::Base.logger + end + end end end + +ActiveJob::Logging::LogSubscriber.attach_to :active_job -- cgit v1.2.3 From b367830af7fb75cd59863b3a1311d4a5bc4b37a8 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 19:14:02 +0200 Subject: Clarify control flow --- lib/active_job/parameters.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/active_job/parameters.rb b/lib/active_job/parameters.rb index 5f814f846d..955fd887d7 100644 --- a/lib/active_job/parameters.rb +++ b/lib/active_job/parameters.rb @@ -3,12 +3,17 @@ require 'active_support/core_ext/object/try' module ActiveJob class Parameters - TYPE_WHITELIST = [NilClass, Fixnum, Float, String, TrueClass, FalseClass, Hash, Array, Bignum] + TYPE_WHITELIST = [ NilClass, Fixnum, Float, String, TrueClass, FalseClass, Hash, Array, Bignum ] def self.serialize(params) params.collect do |param| - raise "Unsupported parameter type: #{param.class.name}" unless param.respond_to?(:global_id) || TYPE_WHITELIST.include?(param.class) - param.try(:global_id) || param + if param.respond_to?(:global_id) + param.global_id + elsif TYPE_WHITELIST.include?(param.class) + param + else + raise "Unsupported parameter type: #{param.class.name}" + end end end -- cgit v1.2.3 From 6bd45aaae1e244591faf784880709e5e64d18d21 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 19:15:57 +0200 Subject: Style --- lib/active_job/queue_adapters/inline_adapter.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index 414a918d2b..4f703a6c3f 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -6,14 +6,14 @@ module ActiveJob job.new.perform *Parameters.deserialize(args) end - def queue_at(job, ts, *args) + def queue_at(job, timestamp, *args) Thread.new do begin - interval = Time.now.to_f - ts + interval = Time.now.to_f - timestamp sleep(interval) if interval > 0 job.new.perform *Parameters.deserialize(args) - rescue => ex - ActiveJob::Base.logger.info "Error performing #{job}: #{ex.message}" + rescue => e + ActiveJob::Base.logger.info "Error performing #{job}: #{e.message}" end end end -- cgit v1.2.3 From 4dd21bff323c2ddae14ed4994a77cfe429f754d5 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 19:17:10 +0200 Subject: Highlight difference by placing addition at the end --- lib/active_job/queue_adapters/sidekiq_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index be6bd4ee01..9829049220 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -17,8 +17,8 @@ module ActiveJob 'class' => JobWrapper, 'queue' => job.queue_name, 'args' => [ job, *args ], - 'at' => timestamp, - 'retry' => true + 'retry' => true, + 'at' => timestamp end end -- cgit v1.2.3 From 8df6a0b2da21b05c4d3f88754bd6fbb6c64a961b Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 19:17:25 +0200 Subject: Style --- lib/active_job/queue_adapters/sucker_punch_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index 8a3d6d10a1..4163db1c35 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -5,7 +5,7 @@ module ActiveJob class SuckerPunchAdapter class << self def queue(job, *args) - JobWrapper.new.async.perform(job, *args) + JobWrapper.new.async.perform job, *args end def queue_at(job, timestamp, *args) -- cgit v1.2.3 From 708775cdb316abff21f655a822892c26d5db896c Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 19:19:14 +0200 Subject: Make it clear that the later test is skipped for adapters that do not implement it --- test/cases/queuing_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/cases/queuing_test.rb b/test/cases/queuing_test.rb index 958b81ca3a..23df35a8df 100644 --- a/test/cases/queuing_test.rb +++ b/test/cases/queuing_test.rb @@ -23,6 +23,7 @@ class QueuingTest < ActiveSupport::TestCase result = HelloJob.enqueue_at 1.second.ago, "Jamie" assert result rescue NotImplementedError + skip end end end -- cgit v1.2.3 From aa4edf4022ffd8862b2cbc81781c1ffae2c039b7 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 20 May 2014 20:29:29 +0200 Subject: Humanely readable timestamp for logging --- lib/active_job/logging.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/active_job/logging.rb b/lib/active_job/logging.rb index 5e33e0da1f..f06f12087b 100644 --- a/lib/active_job/logging.rb +++ b/lib/active_job/logging.rb @@ -10,7 +10,7 @@ module ActiveJob end def enqueue_at(event) - info "Enqueued #{event.payload[:job].name} to #{queue_name(event)} at #{event.payload[:timestamp]}" + args_info(event) + info "Enqueued #{event.payload[:job].name} to #{queue_name(event)} at #{enqueud_at(event)}" + args_info(event) end private @@ -21,6 +21,10 @@ module ActiveJob def args_info(event) event.payload[:args].any? ? ": #{event.payload[:args].inspect}" : "" end + + def enqueud_at(event) + Time.at(event.payload[:timestamp]).utc + end def logger ActiveJob::Base.logger -- cgit v1.2.3 From dae0a314077f5c26468a1a5e6881c3b7560282d0 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Tue, 20 May 2014 18:54:24 +0000 Subject: Use ActiveJob::Base.logger instead ActiveJob::Logging.logger --- test/helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/helper.rb b/test/helper.rb index a5adc882d9..4a7571ada4 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -8,4 +8,4 @@ require "adapters/#{ENV['AJADAPTER'] || 'inline'}" require 'active_support/testing/autorun' -ActiveJob::Logging.logger.level = Logger::ERROR +ActiveJob::Base.logger.level = Logger::ERROR -- cgit v1.2.3 From ce5a88d786c1d4665046aaec5bc1c9826b68aaa4 Mon Sep 17 00:00:00 2001 From: Larry Lv Date: Wed, 21 May 2014 12:48:23 +0800 Subject: Ensure we are logging the serialized args. --- lib/active_job/enqueuing.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index 80b10cdbcb..7f2aef871e 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -30,9 +30,10 @@ module ActiveJob # # Returns truthy if a job was scheduled. def enqueue_at(timestamp, *args) - timestamp = timestamp.to_f - ActiveSupport::Notifications.instrument "enqueue_at.active_job", adapter: queue_adapter, timestamp: timestamp, job: self, args: args - queue_adapter.queue_at self, timestamp, *Parameters.serialize(args) + timestamp = timestamp.to_f + serialized_args = Parameters.serialize(args) + ActiveSupport::Notifications.instrument "enqueue_at.active_job", adapter: queue_adapter, timestamp: timestamp, job: self, args: serialized_args + queue_adapter.queue_at self, timestamp, *serialized_args end end end -- cgit v1.2.3 From a00df7ea6e3307f46aab3cee6a9124ae3b801eec Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 21 May 2014 00:26:19 +0200 Subject: Don't deserialize parameters in individual adapters. It's not their responsibility and this makes logging (and rescuing from errors, eventually!) a lot easier. --- lib/active_job/base.rb | 2 ++ lib/active_job/enqueuing.rb | 2 +- lib/active_job/logging.rb | 8 ++++++-- lib/active_job/performing.rb | 14 ++++++++++++++ lib/active_job/queue_adapters/backburner_adapter.rb | 4 ++-- lib/active_job/queue_adapters/delayed_job_adapter.rb | 2 +- lib/active_job/queue_adapters/inline_adapter.rb | 4 ++-- lib/active_job/queue_adapters/que_adapter.rb | 2 +- lib/active_job/queue_adapters/queue_classic_adapter.rb | 2 +- lib/active_job/queue_adapters/resque_adapter.rb | 2 +- lib/active_job/queue_adapters/sidekiq_adapter.rb | 2 +- lib/active_job/queue_adapters/sneakers_adapter.rb | 2 +- lib/active_job/queue_adapters/sucker_punch_adapter.rb | 2 +- 13 files changed, 34 insertions(+), 14 deletions(-) create mode 100644 lib/active_job/performing.rb diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index 3d16f38275..8bddfde09f 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,6 +1,7 @@ require 'active_job/queue_adapter' require 'active_job/queue_name' require 'active_job/enqueuing' +require 'active_job/performing' require 'active_job/logging' module ActiveJob @@ -8,6 +9,7 @@ module ActiveJob extend QueueAdapter extend QueueName extend Enqueuing + include Performing extend Logging end end diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index 7f2aef871e..dd4556c237 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -32,7 +32,7 @@ module ActiveJob def enqueue_at(timestamp, *args) timestamp = timestamp.to_f serialized_args = Parameters.serialize(args) - ActiveSupport::Notifications.instrument "enqueue_at.active_job", adapter: queue_adapter, timestamp: timestamp, job: self, args: serialized_args + ActiveSupport::Notifications.instrument "enqueue_at.active_job", adapter: queue_adapter, job: self, args: serialized_args, timestamp: timestamp queue_adapter.queue_at self, timestamp, *serialized_args end end diff --git a/lib/active_job/logging.rb b/lib/active_job/logging.rb index f06f12087b..22748e3d5f 100644 --- a/lib/active_job/logging.rb +++ b/lib/active_job/logging.rb @@ -10,7 +10,11 @@ module ActiveJob end def enqueue_at(event) - info "Enqueued #{event.payload[:job].name} to #{queue_name(event)} at #{enqueud_at(event)}" + args_info(event) + info "Enqueued #{event.payload[:job].name} to #{queue_name(event)} at #{enqueued_at(event)}" + args_info(event) + end + + def perform(event) + info "Performed #{event.payload[:job].name} to #{queue_name(event)}" + args_info(event) end private @@ -22,7 +26,7 @@ module ActiveJob event.payload[:args].any? ? ": #{event.payload[:args].inspect}" : "" end - def enqueud_at(event) + def enqueued_at(event) Time.at(event.payload[:timestamp]).utc end diff --git a/lib/active_job/performing.rb b/lib/active_job/performing.rb new file mode 100644 index 0000000000..6c304a4bed --- /dev/null +++ b/lib/active_job/performing.rb @@ -0,0 +1,14 @@ +require 'active_job/parameters' + +module ActiveJob + module Performing + def perform_with_deserialization(*serialized_args) + ActiveSupport::Notifications.instrument "perform.active_job", adapter: self.class.queue_adapter, job: self.class, args: serialized_args + perform *Parameters.deserialize(serialized_args) + end + + def perform(*) + raise NotImplementedError + end + end +end diff --git a/lib/active_job/queue_adapters/backburner_adapter.rb b/lib/active_job/queue_adapters/backburner_adapter.rb index 0d023d9ee7..12760316f0 100644 --- a/lib/active_job/queue_adapters/backburner_adapter.rb +++ b/lib/active_job/queue_adapters/backburner_adapter.rb @@ -11,12 +11,12 @@ module ActiveJob def queue_at(job, timestamp, *args) raise NotImplementedError end - end + end class JobWrapper class << self def perform(job_name, *args) - job_name.constantize.new.perform *Parameters.deserialize(args) + job_name.constantize.new.perform_with_deserialization *args end end end diff --git a/lib/active_job/queue_adapters/delayed_job_adapter.rb b/lib/active_job/queue_adapters/delayed_job_adapter.rb index 214733e3a6..db7b8a5528 100644 --- a/lib/active_job/queue_adapters/delayed_job_adapter.rb +++ b/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -15,7 +15,7 @@ module ActiveJob class JobWrapper def perform(job, *args) - job.new.perform *Parameters.deserialize(args) + job.new.perform_with_deserialization *args end end end diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index 4f703a6c3f..8f790943c4 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -3,7 +3,7 @@ module ActiveJob class InlineAdapter class << self def queue(job, *args) - job.new.perform *Parameters.deserialize(args) + job.new.perform_with_deserialization *args end def queue_at(job, timestamp, *args) @@ -11,7 +11,7 @@ module ActiveJob begin interval = Time.now.to_f - timestamp sleep(interval) if interval > 0 - job.new.perform *Parameters.deserialize(args) + job.new.perform_with_deserialization *args rescue => e ActiveJob::Base.logger.info "Error performing #{job}: #{e.message}" end diff --git a/lib/active_job/queue_adapters/que_adapter.rb b/lib/active_job/queue_adapters/que_adapter.rb index 30c23a35b9..30a69dbd64 100644 --- a/lib/active_job/queue_adapters/que_adapter.rb +++ b/lib/active_job/queue_adapters/que_adapter.rb @@ -15,7 +15,7 @@ module ActiveJob class JobWrapper < Que::Job def run(job, *args) - job.new.perform *Parameters.deserialize(args) + job.new.perform_with_deserialization *args end end end diff --git a/lib/active_job/queue_adapters/queue_classic_adapter.rb b/lib/active_job/queue_adapters/queue_classic_adapter.rb index d0e2e1aa22..762b0370d5 100644 --- a/lib/active_job/queue_adapters/queue_classic_adapter.rb +++ b/lib/active_job/queue_adapters/queue_classic_adapter.rb @@ -15,7 +15,7 @@ module ActiveJob class JobWrapper def self.perform(job, *args) - job.new.perform *Parameters.deserialize(args) + job.new.perform_with_deserialization *args end end end diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index 353b3ae690..f16d546c3c 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -19,7 +19,7 @@ module ActiveJob class JobWrapper class << self def perform(job_name, *args) - job_name.constantize.new.perform *Parameters.deserialize(args) + job_name.constantize.new.perform_with_deserialization *args end end diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index 9829049220..15cfd22bf5 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -26,7 +26,7 @@ module ActiveJob include Sidekiq::Worker def perform(job_name, *args) - job_name.constantize.new.perform *Parameters.deserialize(args) + job_name.constantize.new.perform_with_deserialization *args end end end diff --git a/lib/active_job/queue_adapters/sneakers_adapter.rb b/lib/active_job/queue_adapters/sneakers_adapter.rb index b299b25a96..91be8790dd 100644 --- a/lib/active_job/queue_adapters/sneakers_adapter.rb +++ b/lib/active_job/queue_adapters/sneakers_adapter.rb @@ -23,7 +23,7 @@ module ActiveJob include Sneakers::Worker def work(job, *args) - job.new.perform *Parameters.deserialize(args) + job.new.perform_with_deserialization *args end end end diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index 4163db1c35..22bf3ed799 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -17,7 +17,7 @@ module ActiveJob include SuckerPunch::Job def perform(job, *args) - job.new.perform *Parameters.deserialize(args) + job.new.perform_with_deserialization *args end end end -- cgit v1.2.3 From 484dbd8ed6703a9ba89617ad92f9ffb7d0812d59 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 21 May 2014 01:07:45 +0200 Subject: Rename Adapter.queue and .queue_at to .enqueue and .enqueue_at for consistency. --- lib/active_job/enqueuing.rb | 4 ++-- lib/active_job/queue_adapters/backburner_adapter.rb | 4 ++-- lib/active_job/queue_adapters/delayed_job_adapter.rb | 4 ++-- lib/active_job/queue_adapters/inline_adapter.rb | 4 ++-- lib/active_job/queue_adapters/que_adapter.rb | 4 ++-- lib/active_job/queue_adapters/queue_classic_adapter.rb | 4 ++-- lib/active_job/queue_adapters/resque_adapter.rb | 4 ++-- lib/active_job/queue_adapters/sidekiq_adapter.rb | 4 ++-- lib/active_job/queue_adapters/sneakers_adapter.rb | 4 ++-- lib/active_job/queue_adapters/sucker_punch_adapter.rb | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index dd4556c237..dafa2399c8 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -12,7 +12,7 @@ module ActiveJob def enqueue(*args) serialized_args = Parameters.serialize(args) ActiveSupport::Notifications.instrument "enqueue.active_job", adapter: queue_adapter, job: self, args: serialized_args - queue_adapter.queue self, *serialized_args + queue_adapter.enqueue self, *serialized_args end # Enqueue a job to be performed at +interval+ from now. @@ -33,7 +33,7 @@ module ActiveJob timestamp = timestamp.to_f serialized_args = Parameters.serialize(args) ActiveSupport::Notifications.instrument "enqueue_at.active_job", adapter: queue_adapter, job: self, args: serialized_args, timestamp: timestamp - queue_adapter.queue_at self, timestamp, *serialized_args + queue_adapter.enqueue_at self, timestamp, *serialized_args end end end diff --git a/lib/active_job/queue_adapters/backburner_adapter.rb b/lib/active_job/queue_adapters/backburner_adapter.rb index 12760316f0..5230acc625 100644 --- a/lib/active_job/queue_adapters/backburner_adapter.rb +++ b/lib/active_job/queue_adapters/backburner_adapter.rb @@ -4,11 +4,11 @@ module ActiveJob module QueueAdapters class BackburnerAdapter class << self - def queue(job, *args) + def enqueue(job, *args) Backburner::Worker.enqueue JobWrapper, [ job.name, *args ], queue: job.queue_name end - def queue_at(job, timestamp, *args) + def enqueue_at(job, timestamp, *args) raise NotImplementedError end end diff --git a/lib/active_job/queue_adapters/delayed_job_adapter.rb b/lib/active_job/queue_adapters/delayed_job_adapter.rb index db7b8a5528..5a9c4c708d 100644 --- a/lib/active_job/queue_adapters/delayed_job_adapter.rb +++ b/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -4,11 +4,11 @@ module ActiveJob module QueueAdapters class DelayedJobAdapter class << self - def queue(job, *args) + def enqueue(job, *args) JobWrapper.new.delay(queue: job.queue_name).perform(job, *args) end - def queue_at(job, timestamp, *args) + def enqueue_at(job, timestamp, *args) JobWrapper.new.delay(queue: job.queue_name, run_at: timestamp).perform(job, *args) end end diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index 8f790943c4..d826ce51b4 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -2,11 +2,11 @@ module ActiveJob module QueueAdapters class InlineAdapter class << self - def queue(job, *args) + def enqueue(job, *args) job.new.perform_with_deserialization *args end - def queue_at(job, timestamp, *args) + def enqueue_at(job, timestamp, *args) Thread.new do begin interval = Time.now.to_f - timestamp diff --git a/lib/active_job/queue_adapters/que_adapter.rb b/lib/active_job/queue_adapters/que_adapter.rb index 30a69dbd64..9dd57d65f3 100644 --- a/lib/active_job/queue_adapters/que_adapter.rb +++ b/lib/active_job/queue_adapters/que_adapter.rb @@ -4,11 +4,11 @@ module ActiveJob module QueueAdapters class QueAdapter class << self - def queue(job, *args) + def enqueue(job, *args) JobWrapper.enqueue job, *args, queue: job.queue_name end - def queue_at(job, timestamp, *args) + def enqueue_at(job, timestamp, *args) raise NotImplementedError end end diff --git a/lib/active_job/queue_adapters/queue_classic_adapter.rb b/lib/active_job/queue_adapters/queue_classic_adapter.rb index 762b0370d5..eacc6b5548 100644 --- a/lib/active_job/queue_adapters/queue_classic_adapter.rb +++ b/lib/active_job/queue_adapters/queue_classic_adapter.rb @@ -4,11 +4,11 @@ module ActiveJob module QueueAdapters class QueueClassicAdapter class << self - def queue(job, *args) + def enqueue(job, *args) QC::Queue.new(job.queue_name).enqueue("#{JobWrapper.name}.perform", job, *args) end - def queue_at(job, timestamp, *args) + def enqueue_at(job, timestamp, *args) raise NotImplementedError end end diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index f16d546c3c..3b87f25b80 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -7,11 +7,11 @@ module ActiveJob module QueueAdapters class ResqueAdapter class << self - def queue(job, *args) + def enqueue(job, *args) Resque.enqueue JobWrapper.new(job), job, *args end - def queue_at(job, timestamp, *args) + def enqueue_at(job, timestamp, *args) Resque.enqueue_at timestamp, JobWrapper.new(job), job, *args end end diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index 15cfd22bf5..74fbe632d6 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -4,7 +4,7 @@ module ActiveJob module QueueAdapters class SidekiqAdapter class << self - def queue(job, *args) + def enqueue(job, *args) Sidekiq::Client.push \ 'class' => JobWrapper, 'queue' => job.queue_name, @@ -12,7 +12,7 @@ module ActiveJob 'retry' => true end - def queue_at(job, timestamp, *args) + def enqueue_at(job, timestamp, *args) Sidekiq::Client.push \ 'class' => JobWrapper, 'queue' => job.queue_name, diff --git a/lib/active_job/queue_adapters/sneakers_adapter.rb b/lib/active_job/queue_adapters/sneakers_adapter.rb index 91be8790dd..6bb575e907 100644 --- a/lib/active_job/queue_adapters/sneakers_adapter.rb +++ b/lib/active_job/queue_adapters/sneakers_adapter.rb @@ -7,14 +7,14 @@ module ActiveJob @mutex = Mutex.new class << self - def queue(job, *args) + def enqueue(job, *args) @mutex.synchronize do JobWrapper.from_queue job.queue_name JobWrapper.enqueue [ job, *args ] end end - def queue_at(job, timestamp, *args) + def enqueue_at(job, timestamp, *args) raise NotImplementedError end end diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index 22bf3ed799..30718fc05f 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -4,11 +4,11 @@ module ActiveJob module QueueAdapters class SuckerPunchAdapter class << self - def queue(job, *args) + def enqueue(job, *args) JobWrapper.new.async.perform job, *args end - def queue_at(job, timestamp, *args) + def enqueue_at(job, timestamp, *args) raise NotImplementedError end end -- cgit v1.2.3 From 1513705b9d8d1d77d84ef56cf7b14ba33b8ecfa1 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Wed, 21 May 2014 15:50:29 +0200 Subject: Show which adapter the tests are currently run for --- test/helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/helper.rb b/test/helper.rb index 4a7571ada4..0e51396517 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -6,6 +6,8 @@ $LOAD_PATH << File.dirname(__FILE__) + "/../lib" require 'active_job' require "adapters/#{ENV['AJADAPTER'] || 'inline'}" +puts "Testing using #{ENV['AJADAPTER'] || 'inline'}" require 'active_support/testing/autorun' + ActiveJob::Base.logger.level = Logger::ERROR -- cgit v1.2.3 From 6187cf8ebc16f6abc3e5dec3845a704c3531f1f7 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Wed, 21 May 2014 17:08:59 +0000 Subject: DRY --- test/helper.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/helper.rb b/test/helper.rb index 0e51396517..7ba7607df2 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -4,9 +4,11 @@ Bundler.setup $LOAD_PATH << File.dirname(__FILE__) + "/../lib" require 'active_job' -require "adapters/#{ENV['AJADAPTER'] || 'inline'}" -puts "Testing using #{ENV['AJADAPTER'] || 'inline'}" +adapter = ENV['AJADAPTER'] || 'inline' +require "adapters/#{adapter}" +puts "Testing using #{adapter}" + require 'active_support/testing/autorun' -- cgit v1.2.3 From 0cb85a6e8c1a1a0714ec3bb290ad802d80b1ae07 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Wed, 21 May 2014 20:45:56 +0000 Subject: Active Job Railtie --- lib/active_job/railitie.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 lib/active_job/railitie.rb diff --git a/lib/active_job/railitie.rb b/lib/active_job/railitie.rb new file mode 100644 index 0000000000..e6bd447150 --- /dev/null +++ b/lib/active_job/railitie.rb @@ -0,0 +1,11 @@ +require 'active_model/global_id' +require 'active_job' + +module ActiveJob + # = Active Job Railtie + class Railtie < Rails::Railtie # :nodoc: + initializer 'active_job' do + ActiveSupport.on_load(:active_job) { Base.logger = ::Rails.logger } + end + end +end \ No newline at end of file -- cgit v1.2.3 From af117de37a5183e53f9c23dc9d4743e007a098d0 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Wed, 21 May 2014 21:10:17 +0000 Subject: Railtie hook on ActiveJob::Base --- lib/active_job.rb | 1 + lib/active_job/base.rb | 2 ++ lib/active_job/railitie.rb | 11 ----------- lib/active_job/railtie.rb | 10 ++++++++++ 4 files changed, 13 insertions(+), 11 deletions(-) delete mode 100644 lib/active_job/railitie.rb create mode 100644 lib/active_job/railtie.rb diff --git a/lib/active_job.rb b/lib/active_job.rb index ba18cc093a..ddfdda4fb4 100644 --- a/lib/active_job.rb +++ b/lib/active_job.rb @@ -24,6 +24,7 @@ require 'active_support' require 'active_support/rails' +require 'active_job/railtie' if defined?(Rails) require 'active_job/version' module ActiveJob diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index 3d16f38275..d4f0f6777c 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -9,5 +9,7 @@ module ActiveJob extend QueueName extend Enqueuing extend Logging + + ActiveSupport.run_load_hooks(:active_job, self) end end diff --git a/lib/active_job/railitie.rb b/lib/active_job/railitie.rb deleted file mode 100644 index e6bd447150..0000000000 --- a/lib/active_job/railitie.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'active_model/global_id' -require 'active_job' - -module ActiveJob - # = Active Job Railtie - class Railtie < Rails::Railtie # :nodoc: - initializer 'active_job' do - ActiveSupport.on_load(:active_job) { Base.logger = ::Rails.logger } - end - end -end \ No newline at end of file diff --git a/lib/active_job/railtie.rb b/lib/active_job/railtie.rb new file mode 100644 index 0000000000..08555d1d77 --- /dev/null +++ b/lib/active_job/railtie.rb @@ -0,0 +1,10 @@ +require 'active_model/railtie' + +module ActiveJob + # = Active Job Railtie + class Railtie < Rails::Railtie # :nodoc: + initializer 'active_job' do + ActiveSupport.on_load(:active_job) { self.logger = ::Rails.logger } + end + end +end \ No newline at end of file -- cgit v1.2.3 From 3fe95c82bc108eee5a615698c6796389cdca6d51 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Thu, 22 May 2014 14:19:48 +0300 Subject: Added tests for logging --- lib/active_job/enqueuing.rb | 2 +- lib/active_job/logging.rb | 4 ++-- test/cases/logging_test.rb | 49 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 test/cases/logging_test.rb diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index dafa2399c8..652de5521b 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -21,7 +21,7 @@ module ActiveJob # # Returns truthy if a job was scheduled. def enqueue_in(interval, *args) - enqueue_at(interval.from_now, *args) + enqueue_at(interval.seconds.from_now, *args) end # Enqueue a job to be performed at an explicit point in time. diff --git a/lib/active_job/logging.rb b/lib/active_job/logging.rb index 22748e3d5f..dc432679f8 100644 --- a/lib/active_job/logging.rb +++ b/lib/active_job/logging.rb @@ -12,7 +12,7 @@ module ActiveJob def enqueue_at(event) info "Enqueued #{event.payload[:job].name} to #{queue_name(event)} at #{enqueued_at(event)}" + args_info(event) end - + def perform(event) info "Performed #{event.payload[:job].name} to #{queue_name(event)}" + args_info(event) end @@ -25,7 +25,7 @@ module ActiveJob def args_info(event) event.payload[:args].any? ? ": #{event.payload[:args].inspect}" : "" end - + def enqueued_at(event) Time.at(event.payload[:timestamp]).utc end diff --git a/test/cases/logging_test.rb b/test/cases/logging_test.rb new file mode 100644 index 0000000000..95d3f1c6f1 --- /dev/null +++ b/test/cases/logging_test.rb @@ -0,0 +1,49 @@ +require 'helper' +require "active_support/log_subscriber/test_helper" + +class AdapterTest < ActiveSupport::TestCase + include ActiveSupport::LogSubscriber::TestHelper + include ActiveSupport::Logger::Severity + + def setup + super + $BUFFER = [] + @old_logger = ActiveJob::Base.logger + ActiveJob::Base.logger = @logger + ActiveJob::Logging::LogSubscriber.attach_to :active_job + end + + def teardown + super + ActiveJob::Logging::LogSubscriber.log_subscribers.pop + ActiveJob::Base.logger = @old_logger + end + + def set_logger(logger) + ActiveJob::Base.logger = logger + end + + def test_enqueue_job_logging + HelloJob.enqueue "Cristian" + assert_match(/Enqueued HelloJob to .*?:.*Cristian/, @logger.logged(:info).join) + end + + def test_perform_job_logging + HelloJob.enqueue "Cristian" + assert_match(/Performed HelloJob to .*?:.*Cristian/, @logger.logged(:info).join) + end + + def test_enqueue_at_job_logging + HelloJob.enqueue_at 1, "Cristian" + assert_match(/Enqueued HelloJob to .*? at.*Cristian/, @logger.logged(:info).join) + rescue NotImplementedError + skip + end + + def test_enqueue_in_job_logging + HelloJob.enqueue_in 2, "Cristian" + assert_match(/Enqueued HelloJob to .*? at.*Cristian/, @logger.logged(:info).join) + rescue NotImplementedError + skip + end +end -- cgit v1.2.3 From c27ff043d3221165ecb6e5cb4c1a2b843a883b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 22 May 2014 11:28:02 -0300 Subject: Add travis configuration --- .travis.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..9e8fa3e834 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,30 @@ +script: ci/travis.rb +before_install: +- travis_retry gem install bundler +rvm: +- 1.9.3 +- 2.0.0 +- 2.1 +- ruby-head +- rbx-2 +- jruby +matrix: + allow_failures: + - rvm: rbx-2 + - rvm: jruby + - rvm: ruby-head + fast_finish: true +notifications: + email: false + irc: + on_success: change + on_failure: always + channels: + - irc.freenode.org#rails-contrib + campfire: + on_success: change + on_failure: always + rooms: + - secure: AgZwJA+9VdnWAw7QN9Z5s6RpQIzsEB0q7V+p3pCzXY45156WocL8iNQx+KnyOQ8jbRUt4L/XIOiZl5xHf4pHjXytHWHNhetAlVQP/hPeDcCSk/h0g5gqgf6QABdp38tBNaUq866bXHgCOZYPwwP9bypcmuv2SLyfIO+b/PBgqN0= +services: +- redis -- cgit v1.2.3 From 50abad3fe7450f87606d36a196604ff9ad79d3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 22 May 2014 11:35:22 -0300 Subject: Fix the script --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9e8fa3e834..cadc24d01f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,3 @@ -script: ci/travis.rb before_install: - travis_retry gem install bundler rvm: -- cgit v1.2.3 From b1793ac0043e815356b2a7a5f35bc9cae91c2385 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 22 May 2014 17:08:23 +0200 Subject: Hide the instrumentation mechanics a little better --- lib/active_job/enqueuing.rb | 12 ++++++++---- lib/active_job/performing.rb | 7 ++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index 652de5521b..e8f3272782 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -11,7 +11,7 @@ module ActiveJob # ActiveJob release. def enqueue(*args) serialized_args = Parameters.serialize(args) - ActiveSupport::Notifications.instrument "enqueue.active_job", adapter: queue_adapter, job: self, args: serialized_args + instrument_enqueuing :enqueue, args: serialized_args queue_adapter.enqueue self, *serialized_args end @@ -30,10 +30,14 @@ module ActiveJob # # Returns truthy if a job was scheduled. def enqueue_at(timestamp, *args) - timestamp = timestamp.to_f serialized_args = Parameters.serialize(args) - ActiveSupport::Notifications.instrument "enqueue_at.active_job", adapter: queue_adapter, job: self, args: serialized_args, timestamp: timestamp - queue_adapter.enqueue_at self, timestamp, *serialized_args + instrument_enqueuing :enqueue_at, args: serialized_args, timestamp: timestamp + queue_adapter.enqueue_at self, timestamp.to_f, *serialized_args end + + private + def instrument_enqueuing(method_name, options = {}) + ActiveSupport::Notifications.instrument "#{method_name}.active_job", options.merge(adapter: queue_adapter, job: self) + end end end diff --git a/lib/active_job/performing.rb b/lib/active_job/performing.rb index 6c304a4bed..eca311578d 100644 --- a/lib/active_job/performing.rb +++ b/lib/active_job/performing.rb @@ -3,12 +3,17 @@ require 'active_job/parameters' module ActiveJob module Performing def perform_with_deserialization(*serialized_args) - ActiveSupport::Notifications.instrument "perform.active_job", adapter: self.class.queue_adapter, job: self.class, args: serialized_args + instrument_performing serialized_args perform *Parameters.deserialize(serialized_args) end def perform(*) raise NotImplementedError end + + private + def instrument_performing(args) + ActiveSupport::Notifications.instrument "perform.active_job", adapter: self.class.queue_adapter, job: self.class, args: args + end end end -- cgit v1.2.3 From f82bc7e5515d439b5125d7596b56563249b2d83e Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 22 May 2014 17:09:17 +0200 Subject: Change logging on perform to read more naturally --- lib/active_job/logging.rb | 2 +- test/cases/logging_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_job/logging.rb b/lib/active_job/logging.rb index dc432679f8..f3cc599a14 100644 --- a/lib/active_job/logging.rb +++ b/lib/active_job/logging.rb @@ -14,7 +14,7 @@ module ActiveJob end def perform(event) - info "Performed #{event.payload[:job].name} to #{queue_name(event)}" + args_info(event) + info "Performed #{event.payload[:job].name} from #{queue_name(event)}" + args_info(event) end private diff --git a/test/cases/logging_test.rb b/test/cases/logging_test.rb index 95d3f1c6f1..fa9430436d 100644 --- a/test/cases/logging_test.rb +++ b/test/cases/logging_test.rb @@ -30,7 +30,7 @@ class AdapterTest < ActiveSupport::TestCase def test_perform_job_logging HelloJob.enqueue "Cristian" - assert_match(/Performed HelloJob to .*?:.*Cristian/, @logger.logged(:info).join) + assert_match(/Performed HelloJob from .*?:.*Cristian/, @logger.logged(:info).join) end def test_enqueue_at_job_logging -- cgit v1.2.3 From c073ba339a6820625718f7320989cfa527534563 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 22 May 2014 19:33:23 +0200 Subject: Add callbacks, implement instrumentation as callbacks, and have the enqueue methods return a job instance --- lib/active_job/base.rb | 7 +- lib/active_job/callbacks.rb | 40 +++++++++++ lib/active_job/enqueuing.rb | 82 +++++++++++++--------- lib/active_job/logging.rb | 25 ++++++- lib/active_job/performing.rb | 14 ++-- .../queue_adapters/backburner_adapter.rb | 2 +- .../queue_adapters/delayed_job_adapter.rb | 2 +- lib/active_job/queue_adapters/inline_adapter.rb | 4 +- lib/active_job/queue_adapters/que_adapter.rb | 2 +- .../queue_adapters/queue_classic_adapter.rb | 2 +- lib/active_job/queue_adapters/resque_adapter.rb | 2 +- lib/active_job/queue_adapters/sidekiq_adapter.rb | 2 +- lib/active_job/queue_adapters/sneakers_adapter.rb | 2 +- .../queue_adapters/sucker_punch_adapter.rb | 2 +- test/cases/callbacks_test.rb | 23 ++++++ test/cases/queuing_test.rb | 15 ++++ test/jobs/callback_job.rb | 32 +++++++++ 17 files changed, 204 insertions(+), 54 deletions(-) create mode 100644 lib/active_job/callbacks.rb create mode 100644 test/cases/callbacks_test.rb create mode 100644 test/jobs/callback_job.rb diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index e6b02708a1..1b88bc5bcc 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -3,14 +3,17 @@ require 'active_job/queue_name' require 'active_job/enqueuing' require 'active_job/performing' require 'active_job/logging' +require 'active_job/callbacks' module ActiveJob class Base extend QueueAdapter extend QueueName - extend Enqueuing + + include Enqueuing include Performing - extend Logging + include Callbacks + include Logging ActiveSupport.run_load_hooks(:active_job, self) end diff --git a/lib/active_job/callbacks.rb b/lib/active_job/callbacks.rb new file mode 100644 index 0000000000..c69e4a3b55 --- /dev/null +++ b/lib/active_job/callbacks.rb @@ -0,0 +1,40 @@ +require 'active_support/callbacks' + +module ActiveJob + module Callbacks + extend ActiveSupport::Concern + include ActiveSupport::Callbacks + + included do + define_callbacks :perform + define_callbacks :enqueue + end + + module ClassMethods + def before_perform(*filters, &blk) + set_callback(:perform, :before, *filters, &blk) + end + + def after_perform(*filters, &blk) + set_callback(:perform, :after, *filters, &blk) + end + + def around_perform(*filters, &blk) + set_callback(:perform, :around, *filters, &blk) + end + + + def before_enqueue(*filters, &blk) + set_callback(:enqueue, :before, *filters, &blk) + end + + def after_enqueue(*filters, &blk) + set_callback(:enqueue, :after, *filters, &blk) + end + + def around_enqueue(*filters, &blk) + set_callback(:enqueue, :around, *filters, &blk) + end + end + end +end \ No newline at end of file diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index e8f3272782..8b1f29ce77 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -2,42 +2,58 @@ require 'active_job/parameters' module ActiveJob module Enqueuing - # Push a job onto the queue. The arguments must be legal JSON types - # (string, int, float, nil, true, false, hash or array) or - # ActiveModel::GlobalIdentication instances. Arbitrary Ruby objects - # are not supported. - # - # The return value is adapter-specific and may change in a future - # ActiveJob release. - def enqueue(*args) - serialized_args = Parameters.serialize(args) - instrument_enqueuing :enqueue, args: serialized_args - queue_adapter.enqueue self, *serialized_args - end + extend ActiveSupport::Concern + + module ClassMethods + # Push a job onto the queue. The arguments must be legal JSON types + # (string, int, float, nil, true, false, hash or array) or + # ActiveModel::GlobalIdentication instances. Arbitrary Ruby objects + # are not supported. + # + # Returns an instance of the job class queued with args available in + # Job#arguments. + def enqueue(*args) + new(args).tap do |job| + job.run_callbacks :enqueue do + queue_adapter.enqueue self, *Parameters.serialize(args) + end + end + end - # Enqueue a job to be performed at +interval+ from now. - # - # enqueue_in(1.week, "mike") - # - # Returns truthy if a job was scheduled. - def enqueue_in(interval, *args) - enqueue_at(interval.seconds.from_now, *args) - end + # Enqueue a job to be performed at +interval+ from now. + # + # enqueue_in(1.week, "mike") + # + # Returns an instance of the job class queued with args available in + # Job#arguments and the timestamp in Job#enqueue_at. + def enqueue_in(interval, *args) + enqueue_at interval.seconds.from_now, *args + end + + # Enqueue a job to be performed at an explicit point in time. + # + # enqueue_at(Date.tomorrow.midnight, "mike") + # + # Returns an instance of the job class queued with args available in + # Job#arguments and the timestamp in Job#enqueue_at. + def enqueue_at(timestamp, *args) + new(args).tap do |job| + job.enqueued_at = timestamp - # Enqueue a job to be performed at an explicit point in time. - # - # enqueue_at(Date.tomorrow.midnight, "mike") - # - # Returns truthy if a job was scheduled. - def enqueue_at(timestamp, *args) - serialized_args = Parameters.serialize(args) - instrument_enqueuing :enqueue_at, args: serialized_args, timestamp: timestamp - queue_adapter.enqueue_at self, timestamp.to_f, *serialized_args + job.run_callbacks :enqueue do + queue_adapter.enqueue_at self, timestamp.to_f, *Parameters.serialize(args) + end + end + end end - private - def instrument_enqueuing(method_name, options = {}) - ActiveSupport::Notifications.instrument "#{method_name}.active_job", options.merge(adapter: queue_adapter, job: self) - end + included do + attr_accessor :arguments + attr_accessor :enqueued_at + end + + def initialize(arguments = nil) + @arguments = arguments + end end end diff --git a/lib/active_job/logging.rb b/lib/active_job/logging.rb index f3cc599a14..f4a33ffe19 100644 --- a/lib/active_job/logging.rb +++ b/lib/active_job/logging.rb @@ -2,7 +2,29 @@ require 'active_support/core_ext/string/filters' module ActiveJob module Logging - mattr_accessor(:logger) { ActiveSupport::Logger.new(STDOUT) } + extend ActiveSupport::Concern + + module ClassMethods + mattr_accessor(:logger) { ActiveSupport::Logger.new(STDOUT) } + end + + included do + before_enqueue do |job| + if job.enqueued_at + ActiveSupport::Notifications.instrument "enqueue_at.active_job", + adapter: job.class.queue_adapter, job: job.class, args: job.arguments, timestamp: job.enqueued_at + else + ActiveSupport::Notifications.instrument "enqueue.active_job", + adapter: job.class.queue_adapter, job: job.class, args: job.arguments + end + end + + before_perform do |job| + ActiveSupport::Notifications.instrument "perform.active_job", + adapter: job.class.queue_adapter, job: job.class, args: job.arguments + end + end + class LogSubscriber < ActiveSupport::LogSubscriber def enqueue(event) @@ -17,6 +39,7 @@ module ActiveJob info "Performed #{event.payload[:job].name} from #{queue_name(event)}" + args_info(event) end + private def queue_name(event) event.payload[:adapter].name.demodulize.remove('Adapter') diff --git a/lib/active_job/performing.rb b/lib/active_job/performing.rb index eca311578d..126193995c 100644 --- a/lib/active_job/performing.rb +++ b/lib/active_job/performing.rb @@ -2,18 +2,16 @@ require 'active_job/parameters' module ActiveJob module Performing - def perform_with_deserialization(*serialized_args) - instrument_performing serialized_args - perform *Parameters.deserialize(serialized_args) + def perform_with_hooks(*serialized_args) + self.arguments = Parameters.deserialize(serialized_args) + + run_callbacks :perform do + perform *arguments + end end def perform(*) raise NotImplementedError end - - private - def instrument_performing(args) - ActiveSupport::Notifications.instrument "perform.active_job", adapter: self.class.queue_adapter, job: self.class, args: args - end end end diff --git a/lib/active_job/queue_adapters/backburner_adapter.rb b/lib/active_job/queue_adapters/backburner_adapter.rb index 5230acc625..b7e963cd6f 100644 --- a/lib/active_job/queue_adapters/backburner_adapter.rb +++ b/lib/active_job/queue_adapters/backburner_adapter.rb @@ -16,7 +16,7 @@ module ActiveJob class JobWrapper class << self def perform(job_name, *args) - job_name.constantize.new.perform_with_deserialization *args + job_name.constantize.new.perform_with_hooks *args end end end diff --git a/lib/active_job/queue_adapters/delayed_job_adapter.rb b/lib/active_job/queue_adapters/delayed_job_adapter.rb index 5a9c4c708d..fa08d779fb 100644 --- a/lib/active_job/queue_adapters/delayed_job_adapter.rb +++ b/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -15,7 +15,7 @@ module ActiveJob class JobWrapper def perform(job, *args) - job.new.perform_with_deserialization *args + job.new.perform_with_hooks *args end end end diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index d826ce51b4..8b82d7c25a 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -3,7 +3,7 @@ module ActiveJob class InlineAdapter class << self def enqueue(job, *args) - job.new.perform_with_deserialization *args + job.new.perform_with_hooks *args end def enqueue_at(job, timestamp, *args) @@ -11,7 +11,7 @@ module ActiveJob begin interval = Time.now.to_f - timestamp sleep(interval) if interval > 0 - job.new.perform_with_deserialization *args + job.new.perform_with_hooks *args rescue => e ActiveJob::Base.logger.info "Error performing #{job}: #{e.message}" end diff --git a/lib/active_job/queue_adapters/que_adapter.rb b/lib/active_job/queue_adapters/que_adapter.rb index 9dd57d65f3..adb9125666 100644 --- a/lib/active_job/queue_adapters/que_adapter.rb +++ b/lib/active_job/queue_adapters/que_adapter.rb @@ -15,7 +15,7 @@ module ActiveJob class JobWrapper < Que::Job def run(job, *args) - job.new.perform_with_deserialization *args + job.new.perform_with_hooks *args end end end diff --git a/lib/active_job/queue_adapters/queue_classic_adapter.rb b/lib/active_job/queue_adapters/queue_classic_adapter.rb index eacc6b5548..01d6d30caf 100644 --- a/lib/active_job/queue_adapters/queue_classic_adapter.rb +++ b/lib/active_job/queue_adapters/queue_classic_adapter.rb @@ -15,7 +15,7 @@ module ActiveJob class JobWrapper def self.perform(job, *args) - job.new.perform_with_deserialization *args + job.new.perform_with_hooks *args end end end diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index 3b87f25b80..99da3c63ce 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -19,7 +19,7 @@ module ActiveJob class JobWrapper class << self def perform(job_name, *args) - job_name.constantize.new.perform_with_deserialization *args + job_name.constantize.new.perform_with_hooks *args end end diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index 74fbe632d6..2a2c2ce442 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -26,7 +26,7 @@ module ActiveJob include Sidekiq::Worker def perform(job_name, *args) - job_name.constantize.new.perform_with_deserialization *args + job_name.constantize.new.perform_with_hooks *args end end end diff --git a/lib/active_job/queue_adapters/sneakers_adapter.rb b/lib/active_job/queue_adapters/sneakers_adapter.rb index 6bb575e907..ebd794fca1 100644 --- a/lib/active_job/queue_adapters/sneakers_adapter.rb +++ b/lib/active_job/queue_adapters/sneakers_adapter.rb @@ -23,7 +23,7 @@ module ActiveJob include Sneakers::Worker def work(job, *args) - job.new.perform_with_deserialization *args + job.new.perform_with_hooks *args end end end diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index 30718fc05f..a166081f9f 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -17,7 +17,7 @@ module ActiveJob include SuckerPunch::Job def perform(job, *args) - job.new.perform_with_deserialization *args + job.new.perform_with_hooks *args end end end diff --git a/test/cases/callbacks_test.rb b/test/cases/callbacks_test.rb new file mode 100644 index 0000000000..391c7e87c4 --- /dev/null +++ b/test/cases/callbacks_test.rb @@ -0,0 +1,23 @@ +require 'helper' +require 'active_job/parameters' +require 'jobs/callback_job' + +require 'active_support/core_ext/object/inclusion' + +class CallbacksTest < ActiveSupport::TestCase + test 'perform callbacks' do + performed_callback_job = CallbackJob.new.tap { |j| j.perform_with_hooks } + assert "CallbackJob ran before_perform".in? performed_callback_job.history + assert "CallbackJob ran after_perform".in? performed_callback_job.history + assert "CallbackJob ran around_perform_start".in? performed_callback_job.history + assert "CallbackJob ran around_perform_stop".in? performed_callback_job.history + end + + test 'enqueue callbacks' do + enqueued_callback_job = CallbackJob.enqueue + assert "CallbackJob ran before_enqueue".in? enqueued_callback_job.history + assert "CallbackJob ran after_enqueue".in? enqueued_callback_job.history + assert "CallbackJob ran around_enqueue_start".in? enqueued_callback_job.history + assert "CallbackJob ran around_enqueue_stop".in? enqueued_callback_job.history + end +end diff --git a/test/cases/queuing_test.rb b/test/cases/queuing_test.rb index 23df35a8df..029f60f246 100644 --- a/test/cases/queuing_test.rb +++ b/test/cases/queuing_test.rb @@ -26,4 +26,19 @@ class QueuingTest < ActiveSupport::TestCase skip end end + + test 'job returned by enqueue has the arguments available' do + job = HelloJob.enqueue "Jamie" + assert_equal [ "Jamie" ], job.arguments + end + + + test 'job returned by enqueue_at has the timestamp available' do + begin + job = HelloJob.enqueue_at Time.utc(2014, 1, 1) + assert_equal Time.utc(2014, 1, 1), job.enqueued_at + rescue NotImplementedError + skip + end + end end diff --git a/test/jobs/callback_job.rb b/test/jobs/callback_job.rb new file mode 100644 index 0000000000..056dd073e8 --- /dev/null +++ b/test/jobs/callback_job.rb @@ -0,0 +1,32 @@ +class CallbackJob < ActiveJob::Base + before_perform ->(job) { job.history << "CallbackJob ran before_perform" } + after_perform ->(job) { job.history << "CallbackJob ran after_perform" } + + before_enqueue ->(job) { job.history << "CallbackJob ran before_enqueue" } + after_enqueue ->(job) { job.history << "CallbackJob ran after_enqueue" } + + around_perform :around_perform + around_enqueue :around_enqueue + + + def perform(person = "david") + # NOTHING! + end + + def history + @history ||= [] + end + + # FIXME: Not sure why these can't be declared inline like before/after + def around_perform + history << "CallbackJob ran around_perform_start" + yield + history << "CallbackJob ran around_perform_stop" + end + + def around_enqueue + history << "CallbackJob ran around_enqueue_start" + yield + history << "CallbackJob ran around_enqueue_stop" + end +end -- cgit v1.2.3 From 387a7d993573b1492b1ed39f6803acc77af9233b Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 22 May 2014 19:35:45 +0200 Subject: Standardize on "arguments" for everything -- no more "paramters" --- lib/active_job/arguments.rb | 24 ++++++++++++++++++++++++ lib/active_job/enqueuing.rb | 6 +++--- lib/active_job/parameters.rb | 24 ------------------------ lib/active_job/performing.rb | 4 ++-- test/cases/callbacks_test.rb | 2 +- test/cases/parameters_test.rb | 36 ++++++++++++++++++------------------ test/cases/queuing_test.rb | 2 +- 7 files changed, 49 insertions(+), 49 deletions(-) create mode 100644 lib/active_job/arguments.rb delete mode 100644 lib/active_job/parameters.rb diff --git a/lib/active_job/arguments.rb b/lib/active_job/arguments.rb new file mode 100644 index 0000000000..d1cc6d3177 --- /dev/null +++ b/lib/active_job/arguments.rb @@ -0,0 +1,24 @@ +require 'active_model/global_locator' +require 'active_support/core_ext/object/try' + +module ActiveJob + class Arguments + TYPE_WHITELIST = [ NilClass, Fixnum, Float, String, TrueClass, FalseClass, Hash, Array, Bignum ] + + def self.serialize(arguments) + arguments.collect do |argument| + if argument.respond_to?(:global_id) + argument.global_id + elsif TYPE_WHITELIST.include?(argument.class) + argument + else + raise "Unsupported argument type: #{argument.class.name}" + end + end + end + + def self.deserialize(arguments) + arguments.collect { |argument| ActiveModel::GlobalLocator.locate(argument) || argument } + end + end +end diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index 8b1f29ce77..4ff7ec7e7f 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -1,4 +1,4 @@ -require 'active_job/parameters' +require 'active_job/arguments' module ActiveJob module Enqueuing @@ -15,7 +15,7 @@ module ActiveJob def enqueue(*args) new(args).tap do |job| job.run_callbacks :enqueue do - queue_adapter.enqueue self, *Parameters.serialize(args) + queue_adapter.enqueue self, *Arguments.serialize(args) end end end @@ -41,7 +41,7 @@ module ActiveJob job.enqueued_at = timestamp job.run_callbacks :enqueue do - queue_adapter.enqueue_at self, timestamp.to_f, *Parameters.serialize(args) + queue_adapter.enqueue_at self, timestamp.to_f, *Arguments.serialize(args) end end end diff --git a/lib/active_job/parameters.rb b/lib/active_job/parameters.rb deleted file mode 100644 index 955fd887d7..0000000000 --- a/lib/active_job/parameters.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'active_model/global_locator' -require 'active_support/core_ext/object/try' - -module ActiveJob - class Parameters - TYPE_WHITELIST = [ NilClass, Fixnum, Float, String, TrueClass, FalseClass, Hash, Array, Bignum ] - - def self.serialize(params) - params.collect do |param| - if param.respond_to?(:global_id) - param.global_id - elsif TYPE_WHITELIST.include?(param.class) - param - else - raise "Unsupported parameter type: #{param.class.name}" - end - end - end - - def self.deserialize(params) - params.collect { |param| ActiveModel::GlobalLocator.locate(param) || param } - end - end -end diff --git a/lib/active_job/performing.rb b/lib/active_job/performing.rb index 126193995c..ef17a39f85 100644 --- a/lib/active_job/performing.rb +++ b/lib/active_job/performing.rb @@ -1,9 +1,9 @@ -require 'active_job/parameters' +require 'active_job/arguments' module ActiveJob module Performing def perform_with_hooks(*serialized_args) - self.arguments = Parameters.deserialize(serialized_args) + self.arguments = Arguments.deserialize(serialized_args) run_callbacks :perform do perform *arguments diff --git a/test/cases/callbacks_test.rb b/test/cases/callbacks_test.rb index 391c7e87c4..06e2ea4a2e 100644 --- a/test/cases/callbacks_test.rb +++ b/test/cases/callbacks_test.rb @@ -1,5 +1,5 @@ require 'helper' -require 'active_job/parameters' +require 'active_job/arguments' require 'jobs/callback_job' require 'active_support/core_ext/object/inclusion' diff --git a/test/cases/parameters_test.rb b/test/cases/parameters_test.rb index fb0b920c6e..625c59c404 100644 --- a/test/cases/parameters_test.rb +++ b/test/cases/parameters_test.rb @@ -1,48 +1,48 @@ require 'helper' -require 'active_job/parameters' +require 'active_job/arguments' require 'models/person' class ParameterSerializationTest < ActiveSupport::TestCase test 'should make no change to regular values' do - assert_equal [ 1, "something" ], ActiveJob::Parameters.serialize([ 1, "something" ]) + assert_equal [ 1, "something" ], ActiveJob::Arguments.serialize([ 1, "something" ]) end test 'should not allow complex objects' do - assert_equal [ nil ], ActiveJob::Parameters.serialize([ nil ]) - assert_equal [ 1 ], ActiveJob::Parameters.serialize([ 1 ]) - assert_equal [ 1.0 ], ActiveJob::Parameters.serialize([ 1.0 ]) - assert_equal [ 'a' ], ActiveJob::Parameters.serialize([ 'a' ]) - assert_equal [ true ], ActiveJob::Parameters.serialize([ true ]) - assert_equal [ false ], ActiveJob::Parameters.serialize([ false ]) - assert_equal [ { a: 1 } ], ActiveJob::Parameters.serialize([ { a: 1 } ]) - assert_equal [ [ 1 ] ], ActiveJob::Parameters.serialize([ [ 1 ] ]) - assert_equal [ 1_000_000_000_000_000_000_000 ], ActiveJob::Parameters.serialize([ 1_000_000_000_000_000_000_000 ]) + assert_equal [ nil ], ActiveJob::Arguments.serialize([ nil ]) + assert_equal [ 1 ], ActiveJob::Arguments.serialize([ 1 ]) + assert_equal [ 1.0 ], ActiveJob::Arguments.serialize([ 1.0 ]) + assert_equal [ 'a' ], ActiveJob::Arguments.serialize([ 'a' ]) + assert_equal [ true ], ActiveJob::Arguments.serialize([ true ]) + assert_equal [ false ], ActiveJob::Arguments.serialize([ false ]) + assert_equal [ { a: 1 } ], ActiveJob::Arguments.serialize([ { a: 1 } ]) + assert_equal [ [ 1 ] ], ActiveJob::Arguments.serialize([ [ 1 ] ]) + assert_equal [ 1_000_000_000_000_000_000_000 ], ActiveJob::Arguments.serialize([ 1_000_000_000_000_000_000_000 ]) err = assert_raises RuntimeError do - ActiveJob::Parameters.serialize([ 1, self ]) + ActiveJob::Arguments.serialize([ 1, self ]) end - assert_equal "Unsupported parameter type: #{self.class.name}", err.message + assert_equal "Unsupported argument type: #{self.class.name}", err.message end test 'should serialize records with global id' do - assert_equal [ Person.find(5).gid ], ActiveJob::Parameters.serialize([ Person.find(5) ]) + assert_equal [ Person.find(5).gid ], ActiveJob::Arguments.serialize([ Person.find(5) ]) end test 'should serialize values and records together' do - assert_equal [ 3, Person.find(5).gid ], ActiveJob::Parameters.serialize([ 3, Person.find(5) ]) + assert_equal [ 3, Person.find(5).gid ], ActiveJob::Arguments.serialize([ 3, Person.find(5) ]) end end class ParameterDeserializationTest < ActiveSupport::TestCase test 'should make no change to regular values' do - assert_equal [ 1, "something" ], ActiveJob::Parameters.deserialize([ 1, "something" ]) + assert_equal [ 1, "something" ], ActiveJob::Arguments.deserialize([ 1, "something" ]) end test 'should deserialize records with global id' do - assert_equal [ Person.find(5) ], ActiveJob::Parameters.deserialize([ Person.find(5).gid ]) + assert_equal [ Person.find(5) ], ActiveJob::Arguments.deserialize([ Person.find(5).gid ]) end test 'should serialize values and records together' do - assert_equal [ 3, Person.find(5) ], ActiveJob::Parameters.deserialize([ 3, Person.find(5).gid ]) + assert_equal [ 3, Person.find(5) ], ActiveJob::Arguments.deserialize([ 3, Person.find(5).gid ]) end end diff --git a/test/cases/queuing_test.rb b/test/cases/queuing_test.rb index 029f60f246..3dd9ecd8d2 100644 --- a/test/cases/queuing_test.rb +++ b/test/cases/queuing_test.rb @@ -13,7 +13,7 @@ class QueuingTest < ActiveSupport::TestCase assert_equal "David says hello", $BUFFER.pop end - test 'run queued job with parameters' do + test 'run queued job with arguments' do HelloJob.enqueue "Jamie" assert_equal "Jamie says hello", $BUFFER.pop end -- cgit v1.2.3 From ef4aff07a41f1249baade695e68c75a8db3ded58 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 22 May 2014 20:35:02 +0200 Subject: Add exception handling with built-in retry options --- lib/active_job/enqueuing.rb | 12 ++++++++++++ lib/active_job/performing.rb | 9 +++++++++ test/cases/callbacks_test.rb | 1 - test/cases/rescue_test.rb | 16 ++++++++++++++++ test/jobs/rescue_job.rb | 15 +++++++++++++++ 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 test/cases/rescue_test.rb create mode 100644 test/jobs/rescue_job.rb diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index 4ff7ec7e7f..0f094fb624 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -55,5 +55,17 @@ module ActiveJob def initialize(arguments = nil) @arguments = arguments end + + def retry_now + self.class.enqueue *arguments + end + + def retry_in(interval) + self.class.enqueue_in interval, *arguments + end + + def retry_at(timestamp) + self.class.enqueue_at timestamp, *arguments + end end end diff --git a/lib/active_job/performing.rb b/lib/active_job/performing.rb index ef17a39f85..c3f57873e5 100644 --- a/lib/active_job/performing.rb +++ b/lib/active_job/performing.rb @@ -1,13 +1,22 @@ +require 'active_support/rescuable' require 'active_job/arguments' module ActiveJob module Performing + extend ActiveSupport::Concern + + included do + include ActiveSupport::Rescuable + end + def perform_with_hooks(*serialized_args) self.arguments = Arguments.deserialize(serialized_args) run_callbacks :perform do perform *arguments end + rescue => exception + rescue_with_handler(exception) end def perform(*) diff --git a/test/cases/callbacks_test.rb b/test/cases/callbacks_test.rb index 06e2ea4a2e..104519dcb7 100644 --- a/test/cases/callbacks_test.rb +++ b/test/cases/callbacks_test.rb @@ -1,5 +1,4 @@ require 'helper' -require 'active_job/arguments' require 'jobs/callback_job' require 'active_support/core_ext/object/inclusion' diff --git a/test/cases/rescue_test.rb b/test/cases/rescue_test.rb new file mode 100644 index 0000000000..ca4c69f91b --- /dev/null +++ b/test/cases/rescue_test.rb @@ -0,0 +1,16 @@ +require 'helper' +require 'jobs/rescue_job' + +require 'active_support/core_ext/object/inclusion' + +class RescueTest < ActiveSupport::TestCase + setup do + $BUFFER = [] + end + + test 'rescue perform exception with retry' do + job = RescueJob.new + job.perform_with_hooks("david") + assert_equal [ "rescued from StandardError", "performed beautifully" ], $BUFFER + end +end diff --git a/test/jobs/rescue_job.rb b/test/jobs/rescue_job.rb new file mode 100644 index 0000000000..66b28d768e --- /dev/null +++ b/test/jobs/rescue_job.rb @@ -0,0 +1,15 @@ +class RescueJob < ActiveJob::Base + rescue_from(StandardError) do + $BUFFER << "rescued from StandardError" + arguments[0] = "DIFFERENT!" + retry_now + end + + def perform(person = "david") + if person == "david" + raise "Hair too good" + else + $BUFFER << "performed beautifully" + end + end +end -- cgit v1.2.3 From 26dc3ea7b8ac770b8265fa8ce97ed67158cfb68a Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 22 May 2014 20:37:06 +0200 Subject: Rename #perform_with_hooks to #execution --- lib/active_job/base.rb | 4 ++-- lib/active_job/execution.rb | 26 ++++++++++++++++++++++ lib/active_job/performing.rb | 26 ---------------------- .../queue_adapters/backburner_adapter.rb | 2 +- .../queue_adapters/delayed_job_adapter.rb | 2 +- lib/active_job/queue_adapters/inline_adapter.rb | 4 ++-- lib/active_job/queue_adapters/que_adapter.rb | 2 +- .../queue_adapters/queue_classic_adapter.rb | 2 +- lib/active_job/queue_adapters/resque_adapter.rb | 2 +- lib/active_job/queue_adapters/sidekiq_adapter.rb | 2 +- lib/active_job/queue_adapters/sneakers_adapter.rb | 2 +- .../queue_adapters/sucker_punch_adapter.rb | 2 +- test/cases/callbacks_test.rb | 2 +- test/cases/rescue_test.rb | 2 +- 14 files changed, 40 insertions(+), 40 deletions(-) create mode 100644 lib/active_job/execution.rb delete mode 100644 lib/active_job/performing.rb diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index 1b88bc5bcc..27733577e4 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -1,7 +1,7 @@ require 'active_job/queue_adapter' require 'active_job/queue_name' require 'active_job/enqueuing' -require 'active_job/performing' +require 'active_job/execution' require 'active_job/logging' require 'active_job/callbacks' @@ -11,7 +11,7 @@ module ActiveJob extend QueueName include Enqueuing - include Performing + include Execution include Callbacks include Logging diff --git a/lib/active_job/execution.rb b/lib/active_job/execution.rb new file mode 100644 index 0000000000..563e42ff89 --- /dev/null +++ b/lib/active_job/execution.rb @@ -0,0 +1,26 @@ +require 'active_support/rescuable' +require 'active_job/arguments' + +module ActiveJob + module Execution + extend ActiveSupport::Concern + + included do + include ActiveSupport::Rescuable + end + + def execute(*serialized_args) + self.arguments = Arguments.deserialize(serialized_args) + + run_callbacks :perform do + perform *arguments + end + rescue => exception + rescue_with_handler(exception) + end + + def perform(*) + raise NotImplementedError + end + end +end diff --git a/lib/active_job/performing.rb b/lib/active_job/performing.rb deleted file mode 100644 index c3f57873e5..0000000000 --- a/lib/active_job/performing.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'active_support/rescuable' -require 'active_job/arguments' - -module ActiveJob - module Performing - extend ActiveSupport::Concern - - included do - include ActiveSupport::Rescuable - end - - def perform_with_hooks(*serialized_args) - self.arguments = Arguments.deserialize(serialized_args) - - run_callbacks :perform do - perform *arguments - end - rescue => exception - rescue_with_handler(exception) - end - - def perform(*) - raise NotImplementedError - end - end -end diff --git a/lib/active_job/queue_adapters/backburner_adapter.rb b/lib/active_job/queue_adapters/backburner_adapter.rb index b7e963cd6f..7a6032e56b 100644 --- a/lib/active_job/queue_adapters/backburner_adapter.rb +++ b/lib/active_job/queue_adapters/backburner_adapter.rb @@ -16,7 +16,7 @@ module ActiveJob class JobWrapper class << self def perform(job_name, *args) - job_name.constantize.new.perform_with_hooks *args + job_name.constantize.new.execute *args end end end diff --git a/lib/active_job/queue_adapters/delayed_job_adapter.rb b/lib/active_job/queue_adapters/delayed_job_adapter.rb index fa08d779fb..c0561044be 100644 --- a/lib/active_job/queue_adapters/delayed_job_adapter.rb +++ b/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -15,7 +15,7 @@ module ActiveJob class JobWrapper def perform(job, *args) - job.new.perform_with_hooks *args + job.new.execute *args end end end diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index 8b82d7c25a..d116520285 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -3,7 +3,7 @@ module ActiveJob class InlineAdapter class << self def enqueue(job, *args) - job.new.perform_with_hooks *args + job.new.execute *args end def enqueue_at(job, timestamp, *args) @@ -11,7 +11,7 @@ module ActiveJob begin interval = Time.now.to_f - timestamp sleep(interval) if interval > 0 - job.new.perform_with_hooks *args + job.new.execute *args rescue => e ActiveJob::Base.logger.info "Error performing #{job}: #{e.message}" end diff --git a/lib/active_job/queue_adapters/que_adapter.rb b/lib/active_job/queue_adapters/que_adapter.rb index adb9125666..3d30b2bc72 100644 --- a/lib/active_job/queue_adapters/que_adapter.rb +++ b/lib/active_job/queue_adapters/que_adapter.rb @@ -15,7 +15,7 @@ module ActiveJob class JobWrapper < Que::Job def run(job, *args) - job.new.perform_with_hooks *args + job.new.execute *args end end end diff --git a/lib/active_job/queue_adapters/queue_classic_adapter.rb b/lib/active_job/queue_adapters/queue_classic_adapter.rb index 01d6d30caf..db7189f076 100644 --- a/lib/active_job/queue_adapters/queue_classic_adapter.rb +++ b/lib/active_job/queue_adapters/queue_classic_adapter.rb @@ -15,7 +15,7 @@ module ActiveJob class JobWrapper def self.perform(job, *args) - job.new.perform_with_hooks *args + job.new.execute *args end end end diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index 99da3c63ce..f7ed4669bd 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -19,7 +19,7 @@ module ActiveJob class JobWrapper class << self def perform(job_name, *args) - job_name.constantize.new.perform_with_hooks *args + job_name.constantize.new.execute *args end end diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index 2a2c2ce442..53e7b4d7fa 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -26,7 +26,7 @@ module ActiveJob include Sidekiq::Worker def perform(job_name, *args) - job_name.constantize.new.perform_with_hooks *args + job_name.constantize.new.execute *args end end end diff --git a/lib/active_job/queue_adapters/sneakers_adapter.rb b/lib/active_job/queue_adapters/sneakers_adapter.rb index ebd794fca1..3ca7745506 100644 --- a/lib/active_job/queue_adapters/sneakers_adapter.rb +++ b/lib/active_job/queue_adapters/sneakers_adapter.rb @@ -23,7 +23,7 @@ module ActiveJob include Sneakers::Worker def work(job, *args) - job.new.perform_with_hooks *args + job.new.execute *args end end end diff --git a/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/lib/active_job/queue_adapters/sucker_punch_adapter.rb index a166081f9f..64b9c3ca15 100644 --- a/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -17,7 +17,7 @@ module ActiveJob include SuckerPunch::Job def perform(job, *args) - job.new.perform_with_hooks *args + job.new.execute *args end end end diff --git a/test/cases/callbacks_test.rb b/test/cases/callbacks_test.rb index 104519dcb7..01a9b9d26b 100644 --- a/test/cases/callbacks_test.rb +++ b/test/cases/callbacks_test.rb @@ -5,7 +5,7 @@ require 'active_support/core_ext/object/inclusion' class CallbacksTest < ActiveSupport::TestCase test 'perform callbacks' do - performed_callback_job = CallbackJob.new.tap { |j| j.perform_with_hooks } + performed_callback_job = CallbackJob.new.tap { |j| j.execute } assert "CallbackJob ran before_perform".in? performed_callback_job.history assert "CallbackJob ran after_perform".in? performed_callback_job.history assert "CallbackJob ran around_perform_start".in? performed_callback_job.history diff --git a/test/cases/rescue_test.rb b/test/cases/rescue_test.rb index ca4c69f91b..a91ed01f45 100644 --- a/test/cases/rescue_test.rb +++ b/test/cases/rescue_test.rb @@ -10,7 +10,7 @@ class RescueTest < ActiveSupport::TestCase test 'rescue perform exception with retry' do job = RescueJob.new - job.perform_with_hooks("david") + job.execute("david") assert_equal [ "rescued from StandardError", "performed beautifully" ], $BUFFER end end -- cgit v1.2.3 From 53948bf3a3628a96d5608653618d3770e5acc23b Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 22 May 2014 21:11:21 +0200 Subject: No need for the ClassMethods module --- lib/active_job/logging.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/active_job/logging.rb b/lib/active_job/logging.rb index f4a33ffe19..ede1606027 100644 --- a/lib/active_job/logging.rb +++ b/lib/active_job/logging.rb @@ -4,11 +4,9 @@ module ActiveJob module Logging extend ActiveSupport::Concern - module ClassMethods - mattr_accessor(:logger) { ActiveSupport::Logger.new(STDOUT) } - end - included do + cattr_accessor(:logger) { ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT)) } + before_enqueue do |job| if job.enqueued_at ActiveSupport::Notifications.instrument "enqueue_at.active_job", @@ -24,8 +22,7 @@ module ActiveJob adapter: job.class.queue_adapter, job: job.class, args: job.arguments end end - - + class LogSubscriber < ActiveSupport::LogSubscriber def enqueue(event) info "Enqueued #{event.payload[:job].name} to #{queue_name(event)}" + args_info(event) -- cgit v1.2.3 From 0f8dc7e69cb602ebc2e7428dd4e3d815fdea60de Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 23 May 2014 01:14:13 +0200 Subject: Don't swallow unhandled exceptions. --- lib/active_job/execution.rb | 2 +- test/cases/rescue_test.rb | 9 ++++++++- test/jobs/rescue_job.rb | 13 +++++++++---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/active_job/execution.rb b/lib/active_job/execution.rb index 563e42ff89..d21fd32292 100644 --- a/lib/active_job/execution.rb +++ b/lib/active_job/execution.rb @@ -16,7 +16,7 @@ module ActiveJob perform *arguments end rescue => exception - rescue_with_handler(exception) + rescue_with_handler(exception) || raise(exception) end def perform(*) diff --git a/test/cases/rescue_test.rb b/test/cases/rescue_test.rb index a91ed01f45..aea8b036d4 100644 --- a/test/cases/rescue_test.rb +++ b/test/cases/rescue_test.rb @@ -11,6 +11,13 @@ class RescueTest < ActiveSupport::TestCase test 'rescue perform exception with retry' do job = RescueJob.new job.execute("david") - assert_equal [ "rescued from StandardError", "performed beautifully" ], $BUFFER + assert_equal [ "rescued from ArgumentError", "performed beautifully" ], $BUFFER + end + + test 'let through unhandled perform exception' do + job = RescueJob.new + assert_raises(RescueJob::OtherError) do + job.execute("other") + end end end diff --git a/test/jobs/rescue_job.rb b/test/jobs/rescue_job.rb index 66b28d768e..acf2b81515 100644 --- a/test/jobs/rescue_job.rb +++ b/test/jobs/rescue_job.rb @@ -1,13 +1,18 @@ class RescueJob < ActiveJob::Base - rescue_from(StandardError) do - $BUFFER << "rescued from StandardError" + class OtherError < StandardError; end + + rescue_from(ArgumentError) do + $BUFFER << "rescued from ArgumentError" arguments[0] = "DIFFERENT!" retry_now end def perform(person = "david") - if person == "david" - raise "Hair too good" + case person + when "david" + raise ArgumentError, "Hair too good" + when "other" + raise OtherError else $BUFFER << "performed beautifully" end -- cgit v1.2.3 From 8e19b6af4a1253838a43215b15696110f5f43bb2 Mon Sep 17 00:00:00 2001 From: Kalman Hazins Date: Fri, 23 May 2014 10:06:16 -0400 Subject: Change to 1.9 symbol-hash syntax --- Rakefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index 8ab372e08c..aa48ea58e4 100644 --- a/Rakefile +++ b/Rakefile @@ -16,7 +16,7 @@ def run_without_aborting(*tasks) abort "Errors running #{errors.join(', ')}" if errors.any? end -task :default => :test +task default: :test desc 'Run all adapter tests' task :test do @@ -32,7 +32,7 @@ end end namespace adapter do - task :test => "test_#{adapter}" + task test: "test_#{adapter}" task(:env) { ENV['AJADAPTER'] = adapter } end -- cgit v1.2.3 From bffd4628e5c90a72ad3c5cba2dddb00dfde6afd6 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Mon, 26 May 2014 17:34:34 +0000 Subject: Update README.md We will add it when a stable version is out. --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index e12a589778..c74639acf2 100644 --- a/README.md +++ b/README.md @@ -91,10 +91,6 @@ We currently have adapters for: * [Sneakers](https://github.com/jondot/sneakers) * [Sucker Punch](https://github.com/brandonhilkert/sucker_punch) -We would like to have adapters for: - -* [Resque 2.x](https://github.com/resque/resque) (see [#7](https://github.com/rails/activejob/issues/7)) - ## Under development as a gem, targeted for Rails inclusion -- cgit v1.2.3 From 6eb163a706635c0ab5ede9056a0720f1a5f18f22 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Thu, 22 May 2014 23:38:01 +0300 Subject: Tagged logging --- README.md | 4 -- lib/active_job/base.rb | 4 +- lib/active_job/identifier.rb | 16 +++++++ lib/active_job/logging.rb | 50 +++++++++++++++----- lib/active_job/queue_adapters/sneakers_adapter.rb | 6 +-- test/cases/logging_test.rb | 57 ++++++++++++++++++++--- test/jobs/logging_job.rb | 10 ++++ test/jobs/nested_job.rb | 10 ++++ 8 files changed, 132 insertions(+), 25 deletions(-) create mode 100644 lib/active_job/identifier.rb create mode 100644 test/jobs/logging_job.rb create mode 100644 test/jobs/nested_job.rb diff --git a/README.md b/README.md index e12a589778..c74639acf2 100644 --- a/README.md +++ b/README.md @@ -91,10 +91,6 @@ We currently have adapters for: * [Sneakers](https://github.com/jondot/sneakers) * [Sucker Punch](https://github.com/brandonhilkert/sucker_punch) -We would like to have adapters for: - -* [Resque 2.x](https://github.com/resque/resque) (see [#7](https://github.com/rails/activejob/issues/7)) - ## Under development as a gem, targeted for Rails inclusion diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index 27733577e4..0c772e3126 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -2,8 +2,9 @@ require 'active_job/queue_adapter' require 'active_job/queue_name' require 'active_job/enqueuing' require 'active_job/execution' -require 'active_job/logging' require 'active_job/callbacks' +require 'active_job/identifier' +require 'active_job/logging' module ActiveJob class Base @@ -13,6 +14,7 @@ module ActiveJob include Enqueuing include Execution include Callbacks + include Identifier include Logging ActiveSupport.run_load_hooks(:active_job, self) diff --git a/lib/active_job/identifier.rb b/lib/active_job/identifier.rb new file mode 100644 index 0000000000..af32b03404 --- /dev/null +++ b/lib/active_job/identifier.rb @@ -0,0 +1,16 @@ +require 'active_job/arguments' + +module ActiveJob + module Identifier + extend ActiveSupport::Concern + + included do + attr_writer :job_id + end + + def job_id + @job_id ||= SecureRandom.uuid + end + + end +end diff --git a/lib/active_job/logging.rb b/lib/active_job/logging.rb index ede1606027..ea0ba2dddc 100644 --- a/lib/active_job/logging.rb +++ b/lib/active_job/logging.rb @@ -3,26 +3,51 @@ require 'active_support/core_ext/string/filters' module ActiveJob module Logging extend ActiveSupport::Concern - + included do cattr_accessor(:logger) { ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT)) } + around_enqueue do |job, block, _| + tag_logger do + block.call + end + end + + around_perform do |job, block, _| + tag_logger(job.class.name, job.job_id) do + payload = {adapter: job.class.queue_adapter, job: job.class, args: job.arguments} + ActiveSupport::Notifications.instrument("perform_start.active_job", payload.dup) + ActiveSupport::Notifications.instrument("perform.active_job", payload) do |payload| + block.call + end + end + end + before_enqueue do |job| if job.enqueued_at - ActiveSupport::Notifications.instrument "enqueue_at.active_job", + ActiveSupport::Notifications.instrument "enqueue_at.active_job", adapter: job.class.queue_adapter, job: job.class, args: job.arguments, timestamp: job.enqueued_at else ActiveSupport::Notifications.instrument "enqueue.active_job", adapter: job.class.queue_adapter, job: job.class, args: job.arguments end end - - before_perform do |job| - ActiveSupport::Notifications.instrument "perform.active_job", - adapter: job.class.queue_adapter, job: job.class, args: job.arguments - end end - + + private + def tag_logger(*tags) + if logger.respond_to?(:tagged) + tags.unshift "ActiveJob" unless logger_tagged_by_active_job? + ActiveJob::Base.logger.tagged(*tags){ yield } + else + yield + end + end + + def logger_tagged_by_active_job? + logger.formatter.current_tags.include?("ActiveJob") + end + class LogSubscriber < ActiveSupport::LogSubscriber def enqueue(event) info "Enqueued #{event.payload[:job].name} to #{queue_name(event)}" + args_info(event) @@ -32,10 +57,13 @@ module ActiveJob info "Enqueued #{event.payload[:job].name} to #{queue_name(event)} at #{enqueued_at(event)}" + args_info(event) end - def perform(event) - info "Performed #{event.payload[:job].name} from #{queue_name(event)}" + args_info(event) + def perform_start(event) + info "Performing #{event.payload[:job].name} from #{queue_name(event)}" + args_info(event) end + def perform(event) + info "Performed #{event.payload[:job].name} from #{queue_name(event)} in #{event.duration.round(2).to_s}ms" + end private def queue_name(event) @@ -43,7 +71,7 @@ module ActiveJob end def args_info(event) - event.payload[:args].any? ? ": #{event.payload[:args].inspect}" : "" + event.payload[:args].any? ? " with arguments: #{event.payload[:args].map(&:inspect).join(", ")}" : "" end def enqueued_at(event) diff --git a/lib/active_job/queue_adapters/sneakers_adapter.rb b/lib/active_job/queue_adapters/sneakers_adapter.rb index 3ca7745506..f7da691935 100644 --- a/lib/active_job/queue_adapters/sneakers_adapter.rb +++ b/lib/active_job/queue_adapters/sneakers_adapter.rb @@ -4,11 +4,11 @@ require 'thread' module ActiveJob module QueueAdapters class SneakersAdapter - @mutex = Mutex.new - + @monitor = Monitor.new + class << self def enqueue(job, *args) - @mutex.synchronize do + @monitor.synchronize do JobWrapper.from_queue job.queue_name JobWrapper.enqueue [ job, *args ] end diff --git a/test/cases/logging_test.rb b/test/cases/logging_test.rb index fa9430436d..194944db11 100644 --- a/test/cases/logging_test.rb +++ b/test/cases/logging_test.rb @@ -1,15 +1,30 @@ require 'helper' require "active_support/log_subscriber/test_helper" +require 'jobs/logging_job' +require 'jobs/nested_job' class AdapterTest < ActiveSupport::TestCase include ActiveSupport::LogSubscriber::TestHelper include ActiveSupport::Logger::Severity + class TestLogger < ActiveSupport::Logger + def initialize + @file = StringIO.new + super(@file) + end + + def messages + @file.rewind + @file.read + end + end + def setup super $BUFFER = [] @old_logger = ActiveJob::Base.logger - ActiveJob::Base.logger = @logger + @logger = ActiveSupport::TaggedLogging.new(TestLogger.new) + set_logger @logger ActiveJob::Logging::LogSubscriber.attach_to :active_job end @@ -23,26 +38,56 @@ class AdapterTest < ActiveSupport::TestCase ActiveJob::Base.logger = logger end + + def test_uses_active_job_as_tag + HelloJob.enqueue "Cristian" + assert_match(/\[ActiveJob\]/, @logger.messages) + end + def test_enqueue_job_logging HelloJob.enqueue "Cristian" - assert_match(/Enqueued HelloJob to .*?:.*Cristian/, @logger.logged(:info).join) + assert_match(/Enqueued HelloJob to .*?:.*Cristian/, @logger.messages) end def test_perform_job_logging - HelloJob.enqueue "Cristian" - assert_match(/Performed HelloJob from .*?:.*Cristian/, @logger.logged(:info).join) + LoggingJob.enqueue "Dummy" + assert_match(/Performing LoggingJob from .*? with arguments:.*Dummy/, @logger.messages) + assert_match(/Dummy, here is it: Dummy/, @logger.messages) + assert_match(/Performed LoggingJob from .*? in .*ms/, @logger.messages) + end + + def test_perform_uses_job_name_job_logging + LoggingJob.enqueue "Dummy" + assert_match(/\[LoggingJob\]/, @logger.messages) + end + + def test_perform_uses_job_id_job_logging + LoggingJob.enqueue "Dummy" + assert_match(/\[LOGGING-JOB-ID\]/, @logger.messages) + end + + def test_perform_nested_jobs_logging + NestedJob.enqueue + assert_match(/\[LoggingJob\] \[.*?\]/, @logger.messages) + assert_match(/\[ActiveJob\] Enqueued NestedJob to/, @logger.messages) + assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Performing NestedJob from/, @logger.messages) + assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Enqueued LoggingJob to .* with arguments: "NestedJob"/, @logger.messages) + assert_match(/\[ActiveJob\].*\[LoggingJob\] \[LOGGING-JOB-ID\] Performing LoggingJob from .* with arguments: "NestedJob"/, @logger.messages) + assert_match(/\[ActiveJob\].*\[LoggingJob\] \[LOGGING-JOB-ID\] Dummy, here is it: NestedJob/, @logger.messages) + assert_match(/\[ActiveJob\].*\[LoggingJob\] \[LOGGING-JOB-ID\] Performed LoggingJob from .* in/, @logger.messages) + assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Performed NestedJob from .* in/, @logger.messages) end def test_enqueue_at_job_logging HelloJob.enqueue_at 1, "Cristian" - assert_match(/Enqueued HelloJob to .*? at.*Cristian/, @logger.logged(:info).join) + assert_match(/Enqueued HelloJob to .*? at.*Cristian/, @logger.messages) rescue NotImplementedError skip end def test_enqueue_in_job_logging HelloJob.enqueue_in 2, "Cristian" - assert_match(/Enqueued HelloJob to .*? at.*Cristian/, @logger.logged(:info).join) + assert_match(/Enqueued HelloJob to .*? at.*Cristian/, @logger.messages) rescue NotImplementedError skip end diff --git a/test/jobs/logging_job.rb b/test/jobs/logging_job.rb new file mode 100644 index 0000000000..d84ed8589b --- /dev/null +++ b/test/jobs/logging_job.rb @@ -0,0 +1,10 @@ +class LoggingJob < ActiveJob::Base + def perform(dummy) + logger.info "Dummy, here is it: #{dummy}" + end + + def job_id + "LOGGING-JOB-ID" + end +end + diff --git a/test/jobs/nested_job.rb b/test/jobs/nested_job.rb new file mode 100644 index 0000000000..fd66f68991 --- /dev/null +++ b/test/jobs/nested_job.rb @@ -0,0 +1,10 @@ +class NestedJob < ActiveJob::Base + def perform + LoggingJob.enqueue "NestedJob" + end + + def job_id + "NESTED-JOB-ID" + end +end + -- cgit v1.2.3 From 895f22a98371c25291a117aeb891dd59468d6995 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Thu, 29 May 2014 10:20:13 +0000 Subject: Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c74639acf2..090fba2565 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ Set the queue adapter for Active Job: ``` ruby ActiveJob::Base.queue_adapter = :inline # default queue adapter -# Adapters currently supported: :delayed_job, :que, :queue_classic, :resque, -# :sidekiq, :sneakers, :sucker_punch +# Adapters currently supported: :backburner, :delayed_job, :que, :queue_classic, +# :resque, :sidekiq, :sneakers, :sucker_punch ``` Declare a job like so: -- cgit v1.2.3 From aa348150b97cd095ff95843a4385c26aeceea8c5 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Thu, 29 May 2014 10:56:08 +0000 Subject: Update README.md Documented enqueue_at and enqueue_in --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 090fba2565..6eb177c113 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,15 @@ end Enqueue a job like so: ```ruby -MyJob.enqueue record +MyJob.enqueue record # Enqueue a job to be performed as soon the queueing system is free. +``` + +```ruby +MyJob.enqueue_at Date.tomorrow.noon, record # Enqueue a job to be performed tomorrow at noon. +``` + +```ruby +MyJob.enqueue_in 1.week, record # Enqueue a job to be performed 1 week from now. ``` That's it! -- cgit v1.2.3 From 9fcdc3451e2405d04cb4fec9f421c394ddb575f7 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 29 May 2014 16:27:47 +0200 Subject: Stray CR --- lib/active_job/identifier.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/active_job/identifier.rb b/lib/active_job/identifier.rb index af32b03404..c7f522087d 100644 --- a/lib/active_job/identifier.rb +++ b/lib/active_job/identifier.rb @@ -11,6 +11,5 @@ module ActiveJob def job_id @job_id ||= SecureRandom.uuid end - end end -- cgit v1.2.3 From a4d29165df29eb77f73a8a52282548e62ba2034a Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Thu, 29 May 2014 16:29:33 +0000 Subject: Sidekiq::Client does not support symbols as keys --- lib/active_job/queue_adapters/sidekiq_adapter.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/active_job/queue_adapters/sidekiq_adapter.rb b/lib/active_job/queue_adapters/sidekiq_adapter.rb index 53e7b4d7fa..f738a7d91c 100644 --- a/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -5,6 +5,7 @@ module ActiveJob class SidekiqAdapter class << self def enqueue(job, *args) + #Sidekiq::Client does not support symbols as keys Sidekiq::Client.push \ 'class' => JobWrapper, 'queue' => job.queue_name, -- cgit v1.2.3 From ba3ae62a775182c663c6797e21618bb37de3aaba Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Sat, 31 May 2014 17:31:20 +0300 Subject: Deep serialization --- lib/active_job/arguments.rb | 32 ++++++++++++++++++++++++++++---- test/cases/parameters_test.rb | 27 ++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/lib/active_job/arguments.rb b/lib/active_job/arguments.rb index d1cc6d3177..6fe52397da 100644 --- a/lib/active_job/arguments.rb +++ b/lib/active_job/arguments.rb @@ -3,14 +3,19 @@ require 'active_support/core_ext/object/try' module ActiveJob class Arguments - TYPE_WHITELIST = [ NilClass, Fixnum, Float, String, TrueClass, FalseClass, Hash, Array, Bignum ] + TYPE_WHITELIST = [ NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum ] def self.serialize(arguments) arguments.collect do |argument| - if argument.respond_to?(:global_id) + case argument + when ActiveModel::GlobalIdentification argument.global_id - elsif TYPE_WHITELIST.include?(argument.class) + when *TYPE_WHITELIST argument + when Hash + Hash[ argument.map{ |key, value| [ serialize_hash_key(key), serialize([value]).first ] } ] + when Array + serialize(argument) else raise "Unsupported argument type: #{argument.class.name}" end @@ -18,7 +23,26 @@ module ActiveJob end def self.deserialize(arguments) - arguments.collect { |argument| ActiveModel::GlobalLocator.locate(argument) || argument } + arguments.collect do |argument| + case argument + when Array + deserialize(argument) + when Hash + argument.with_indifferent_access + else + ActiveModel::GlobalLocator.locate(argument) || argument + end + end end + + private + def self.serialize_hash_key(key) + case key + when String, Symbol + key.to_s + else + raise "Unsupported hash key type: #{key.class.name}" + end + end end end diff --git a/test/cases/parameters_test.rb b/test/cases/parameters_test.rb index 625c59c404..93b34278a5 100644 --- a/test/cases/parameters_test.rb +++ b/test/cases/parameters_test.rb @@ -1,6 +1,7 @@ require 'helper' require 'active_job/arguments' require 'models/person' +require 'active_support/core_ext/hash/indifferent_access' class ParameterSerializationTest < ActiveSupport::TestCase test 'should make no change to regular values' do @@ -14,7 +15,7 @@ class ParameterSerializationTest < ActiveSupport::TestCase assert_equal [ 'a' ], ActiveJob::Arguments.serialize([ 'a' ]) assert_equal [ true ], ActiveJob::Arguments.serialize([ true ]) assert_equal [ false ], ActiveJob::Arguments.serialize([ false ]) - assert_equal [ { a: 1 } ], ActiveJob::Arguments.serialize([ { a: 1 } ]) + assert_equal [ { "a" => 1, "b" => 2 } ], ActiveJob::Arguments.serialize([ { a: 1, "b" => 2 } ]) assert_equal [ [ 1 ] ], ActiveJob::Arguments.serialize([ [ 1 ] ]) assert_equal [ 1_000_000_000_000_000_000_000 ], ActiveJob::Arguments.serialize([ 1_000_000_000_000_000_000_000 ]) @@ -24,6 +25,25 @@ class ParameterSerializationTest < ActiveSupport::TestCase assert_equal "Unsupported argument type: #{self.class.name}", err.message end + test 'should dive deep into arrays or hashes' do + assert_equal [ { "a" => Person.find(5).gid }.with_indifferent_access ], ActiveJob::Arguments.serialize([ { a: Person.find(5) } ]) + assert_equal [ [ Person.find(5).gid ] ], ActiveJob::Arguments.serialize([ [ Person.find(5) ] ]) + end + + test 'should dive deep into arrays or hashes and raise exception on complex objects' do + err = assert_raises RuntimeError do + ActiveJob::Arguments.serialize([ 1, [self] ]) + end + assert_equal "Unsupported argument type: #{self.class.name}", err.message + end + + test 'shoud dive deep into hashes and allow raise exception on not string/symbol keys' do + err = assert_raises RuntimeError do + ActiveJob::Arguments.serialize([ [ { 1 => 2 } ] ]) + end + assert_equal "Unsupported hash key type: Fixnum", err.message + end + test 'should serialize records with global id' do assert_equal [ Person.find(5).gid ], ActiveJob::Arguments.serialize([ Person.find(5) ]) end @@ -45,4 +65,9 @@ class ParameterDeserializationTest < ActiveSupport::TestCase test 'should serialize values and records together' do assert_equal [ 3, Person.find(5) ], ActiveJob::Arguments.deserialize([ 3, Person.find(5).gid ]) end + + test 'should dive deep when desierializing' do + assert_equal [ [ 3, Person.find(5) ] ], ActiveJob::Arguments.deserialize([ [ 3, Person.find(5).gid ] ]) + end + end -- cgit v1.2.3 From a4a7f0279af91307a8b36a8c437c4734721741d6 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Tue, 3 Jun 2014 00:54:07 +0300 Subject: Fixed Hash deserialisation --- lib/active_job/arguments.rb | 2 +- test/cases/parameters_test.rb | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/active_job/arguments.rb b/lib/active_job/arguments.rb index 6fe52397da..553ab20f65 100644 --- a/lib/active_job/arguments.rb +++ b/lib/active_job/arguments.rb @@ -28,7 +28,7 @@ module ActiveJob when Array deserialize(argument) when Hash - argument.with_indifferent_access + Hash[argument.map{ |key, value| [ key, deserialize([value]).first ] }].with_indifferent_access else ActiveModel::GlobalLocator.locate(argument) || argument end diff --git a/test/cases/parameters_test.rb b/test/cases/parameters_test.rb index 93b34278a5..737f3c26b7 100644 --- a/test/cases/parameters_test.rb +++ b/test/cases/parameters_test.rb @@ -66,8 +66,12 @@ class ParameterDeserializationTest < ActiveSupport::TestCase assert_equal [ 3, Person.find(5) ], ActiveJob::Arguments.deserialize([ 3, Person.find(5).gid ]) end - test 'should dive deep when desierializing' do + test 'should dive deep when deserialising arrays' do assert_equal [ [ 3, Person.find(5) ] ], ActiveJob::Arguments.deserialize([ [ 3, Person.find(5).gid ] ]) end + test 'should dive deep when deserialising hashes' do + assert_equal [ { "5" => Person.find(5) } ], ActiveJob::Arguments.deserialize([ { "5" => Person.find(5).gid } ]) + end + end -- cgit v1.2.3 From eaacb88fcb1428c84cd35575a177d2140a226ae2 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Tue, 3 Jun 2014 01:04:08 +0300 Subject: Raise exception for scheduling jobs in the inline adapter --- lib/active_job/queue_adapters/inline_adapter.rb | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/active_job/queue_adapters/inline_adapter.rb b/lib/active_job/queue_adapters/inline_adapter.rb index d116520285..50d14a321d 100644 --- a/lib/active_job/queue_adapters/inline_adapter.rb +++ b/lib/active_job/queue_adapters/inline_adapter.rb @@ -6,16 +6,8 @@ module ActiveJob job.new.execute *args end - def enqueue_at(job, timestamp, *args) - Thread.new do - begin - interval = Time.now.to_f - timestamp - sleep(interval) if interval > 0 - job.new.execute *args - rescue => e - ActiveJob::Base.logger.info "Error performing #{job}: #{e.message}" - end - end + def enqueue_at(*) + raise NotImplementedError.new("Use a queueing backend to enqueue jobs in the future. Read more at https://github.com/rails/activejob") end end end -- cgit v1.2.3 From c0f5b7346da26908d47021d473a2f590939d7ba0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 5 Jun 2014 22:10:41 +0200 Subject: Clean up Arguments somewhat --- lib/active_job/arguments.rb | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/active_job/arguments.rb b/lib/active_job/arguments.rb index 553ab20f65..604c3bfe1d 100644 --- a/lib/active_job/arguments.rb +++ b/lib/active_job/arguments.rb @@ -1,41 +1,44 @@ require 'active_model/global_locator' -require 'active_support/core_ext/object/try' module ActiveJob class Arguments TYPE_WHITELIST = [ NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum ] def self.serialize(arguments) - arguments.collect do |argument| + arguments.map { |argument| serialize_argument(argument) } + end + + def self.deserialize(arguments) + arguments.map { |argument| deserialize_argument(argument) } + end + + private + def self.serialize_argument(argument) case argument when ActiveModel::GlobalIdentification argument.global_id when *TYPE_WHITELIST argument - when Hash - Hash[ argument.map{ |key, value| [ serialize_hash_key(key), serialize([value]).first ] } ] when Array serialize(argument) + when Hash + Hash[ argument.map { |key, value| [ serialize_hash_key(key), serialize_argument(value) ] } ] else raise "Unsupported argument type: #{argument.class.name}" end end - end - def self.deserialize(arguments) - arguments.collect do |argument| + def self.deserialize_argument(argument) case argument when Array deserialize(argument) when Hash - Hash[argument.map{ |key, value| [ key, deserialize([value]).first ] }].with_indifferent_access + Hash[ argument.map { |key, value| [ key, deserialize_argument(value) ] } ].with_indifferent_access else ActiveModel::GlobalLocator.locate(argument) || argument end end - end - private def self.serialize_hash_key(key) case key when String, Symbol -- cgit v1.2.3 From da6a86f875199cf28872d9db06efb5c6908ee457 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 9 Jun 2014 18:49:14 +0200 Subject: Have .queue_as only set queue name for job in question. --- lib/active_job/base.rb | 2 +- lib/active_job/queue_name.rb | 16 ++++++++++++---- test/cases/queue_naming_test.rb | 6 +++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/active_job/base.rb b/lib/active_job/base.rb index 0c772e3126..d5ba253016 100644 --- a/lib/active_job/base.rb +++ b/lib/active_job/base.rb @@ -9,8 +9,8 @@ require 'active_job/logging' module ActiveJob class Base extend QueueAdapter - extend QueueName + include QueueName include Enqueuing include Execution include Callbacks diff --git a/lib/active_job/queue_name.rb b/lib/active_job/queue_name.rb index a606b67370..859ddad034 100644 --- a/lib/active_job/queue_name.rb +++ b/lib/active_job/queue_name.rb @@ -1,10 +1,18 @@ module ActiveJob module QueueName - mattr_accessor(:queue_base_name) { "active_jobs" } - mattr_accessor(:queue_name) { queue_base_name } + extend ActiveSupport::Concern - def queue_as(part_name) - self.queue_name = "#{queue_base_name}_#{part_name}" + module ClassMethods + mattr_accessor(:queue_base_name) { "active_jobs" } + + def queue_as(part_name) + self.queue_name = "#{queue_base_name}_#{part_name}" + end + end + + included do + class_attribute :queue_name + self.queue_name = queue_base_name end end end \ No newline at end of file diff --git a/test/cases/queue_naming_test.rb b/test/cases/queue_naming_test.rb index e171474686..852643b9f6 100644 --- a/test/cases/queue_naming_test.rb +++ b/test/cases/queue_naming_test.rb @@ -9,9 +9,13 @@ class QueueNamingTest < ActiveSupport::TestCase test 'name appended in job' do begin HelloJob.queue_as :greetings + LoggingJob.queue_as :bookkeeping + + assert_equal "active_jobs", NestedJob.queue_name assert_equal "active_jobs_greetings", HelloJob.queue_name + assert_equal "active_jobs_bookkeeping", LoggingJob.queue_name ensure - HelloJob.queue_name = HelloJob.queue_base_name + HelloJob.queue_name = LoggingJob.queue_name = ActiveJob::Base.queue_base_name end end end -- cgit v1.2.3 From b36d4da340fe81827601f9c192aefb2982cbcefb Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Thu, 12 Jun 2014 11:14:42 +0300 Subject: Implemented :qu adapter --- Gemfile | 2 ++ Gemfile.lock | 32 +++++++++++++++++++++++++++++ README.md | 7 ++++--- Rakefile | 4 ++-- lib/active_job/queue_adapters/qu_adapter.rb | 28 +++++++++++++++++++++++++ test/adapters/qu.rb | 3 +++ test/cases/adapter_test.rb | 5 +++++ 7 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 lib/active_job/queue_adapters/qu_adapter.rb create mode 100644 test/adapters/qu.rb diff --git a/Gemfile b/Gemfile index e5b9a8fec6..aa228d5669 100644 --- a/Gemfile +++ b/Gemfile @@ -12,3 +12,5 @@ gem 'queue_classic' gem 'sneakers', '0.1.1.pre' gem 'que' gem 'backburner' +gem 'qu-rails', github: "bkeepers/qu", branch: "master" +gem 'qu-redis' diff --git a/Gemfile.lock b/Gemfile.lock index 7dda62c1fe..7dac1c6e88 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,16 @@ +GIT + remote: git://github.com/bkeepers/qu.git + revision: 2175633a834504423368d71cb10fb9f072d76cd2 + branch: master + specs: + qu (0.2.0) + qu-rails (0.2.0) + qu (= 0.2.0) + railties (>= 3.2, < 5) + qu-redis (0.2.0) + qu (= 0.2.0) + redis-namespace + PATH remote: . specs: @@ -8,6 +21,15 @@ PATH GEM remote: https://rubygems.org/ specs: + actionpack (4.1.1) + actionview (= 4.1.1) + activesupport (= 4.1.1) + rack (~> 1.5.2) + rack-test (~> 0.6.2) + actionview (4.1.1) + activesupport (= 4.1.1) + builder (~> 3.1) + erubis (~> 2.7.0) activemodel (4.1.1) activesupport (= 4.1.1) builder (~> 3.1) @@ -34,6 +56,7 @@ GEM dante (0.1.5) delayed_job (4.0.1) activesupport (>= 3.0, < 4.2) + erubis (2.7.0) i18n (0.6.9) json (1.8.1) minitest (5.3.4) @@ -46,6 +69,13 @@ GEM rack (1.5.2) rack-protection (1.5.2) rack + rack-test (0.6.2) + rack (>= 1.0) + railties (4.1.1) + actionpack (= 4.1.1) + activesupport (= 4.1.1) + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) rake (10.3.2) redis (3.0.7) redis-namespace (1.4.1) @@ -99,6 +129,8 @@ DEPENDENCIES activejob! backburner delayed_job + qu-rails! + qu-redis que queue_classic rake diff --git a/README.md b/README.md index 6eb177c113..9644b37119 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ of the request-response cycle, so the user doesn't have to wait on it. The main point is to ensure that all Rails apps will have a job infrastructure in place, even if it's in the form of an "immediate runner". We can then have framework features and other gems build on top of that, without having to worry -about API differences between Delayed Job and Resque. Picking your queuing +about API differences between Delayed Job and Resque. Picking your queuing backend becomes more of an operational concern, then. And you'll be able to switch between them without having to rewrite your jobs. @@ -24,7 +24,7 @@ Set the queue adapter for Active Job: ``` ruby ActiveJob::Base.queue_adapter = :inline # default queue adapter -# Adapters currently supported: :backburner, :delayed_job, :que, :queue_classic, +# Adapters currently supported: :backburner, :delayed_job, :qu, :que, :queue_classic, # :resque, :sidekiq, :sneakers, :sucker_punch ``` @@ -44,7 +44,7 @@ Enqueue a job like so: ```ruby MyJob.enqueue record # Enqueue a job to be performed as soon the queueing system is free. -``` +``` ```ruby MyJob.enqueue_at Date.tomorrow.noon, record # Enqueue a job to be performed tomorrow at noon. @@ -92,6 +92,7 @@ We currently have adapters for: * [Backburner](https://github.com/nesquena/backburner) * [Delayed Job](https://github.com/collectiveidea/delayed_job) +* [Qu](https://github.com/bkeepers/qu) * [Que](https://github.com/chanks/que) * [QueueClassic](https://github.com/ryandotsmith/queue_classic) * [Resque 1.x](https://github.com/resque/resque) diff --git a/Rakefile b/Rakefile index aa48ea58e4..5780410eda 100644 --- a/Rakefile +++ b/Rakefile @@ -20,11 +20,11 @@ task default: :test desc 'Run all adapter tests' task :test do - tasks = %w(test_inline test_delayed_job test_que test_queue_classic test_resque test_sidekiq test_sneakers test_sucker_punch test_backburner) + tasks = %w(test_inline test_delayed_job test_qu test_que test_queue_classic test_resque test_sidekiq test_sneakers test_sucker_punch test_backburner) run_without_aborting(*tasks) end -%w(inline delayed_job que queue_classic resque sidekiq sneakers sucker_punch backburner).each do |adapter| +%w(inline delayed_job qu que queue_classic resque sidekiq sneakers sucker_punch backburner).each do |adapter| Rake::TestTask.new("test_#{adapter}") do |t| t.libs << 'test' t.test_files = FileList['test/cases/**/*_test.rb'] diff --git a/lib/active_job/queue_adapters/qu_adapter.rb b/lib/active_job/queue_adapters/qu_adapter.rb new file mode 100644 index 0000000000..7e69229801 --- /dev/null +++ b/lib/active_job/queue_adapters/qu_adapter.rb @@ -0,0 +1,28 @@ +require 'qu' + +module ActiveJob + module QueueAdapters + class QuAdapter + class << self + def enqueue(job, *args) + Qu::Payload.new(klass: JobWrapper, args: [job, *args], queue: job.queue_name).push + end + + def enqueue_at(job, timestamp, *args) + raise NotImplementedError + end + end + + class JobWrapper < Qu::Job + def initialize(job, *args) + @job = job + @args = args + end + + def perform + @job.new.execute *@args + end + end + end + end +end diff --git a/test/adapters/qu.rb b/test/adapters/qu.rb new file mode 100644 index 0000000000..7728c843b4 --- /dev/null +++ b/test/adapters/qu.rb @@ -0,0 +1,3 @@ +require 'qu-immediate' + +ActiveJob::Base.queue_adapter = :qu diff --git a/test/cases/adapter_test.rb b/test/cases/adapter_test.rb index 703058dacb..7f6f4c1159 100644 --- a/test/cases/adapter_test.rb +++ b/test/cases/adapter_test.rb @@ -14,6 +14,11 @@ class AdapterTest < ActiveSupport::TestCase assert_equal ActiveJob::QueueAdapters::DelayedJobAdapter, ActiveJob::Base.queue_adapter end + test 'should load Qu adapter' do + ActiveJob::Base.queue_adapter = :qu + assert_equal ActiveJob::QueueAdapters::QuAdapter, ActiveJob::Base.queue_adapter + end + test 'should load Que adapter' do ActiveJob::Base.queue_adapter = :que assert_equal ActiveJob::QueueAdapters::QueAdapter, ActiveJob::Base.queue_adapter -- cgit v1.2.3 From 243d74eb30464dc95cb07c0bd14cc086f9cd7022 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Sat, 31 May 2014 02:19:30 +0300 Subject: Persist job_id --- lib/active_job/enqueuing.rb | 18 +++++++++--------- lib/active_job/execution.rb | 5 +++-- lib/active_job/logging.rb | 10 +++++----- lib/active_job/queue_adapters/resque_adapter.rb | 12 ++---------- lib/activejob.rb | 1 + test/cases/callbacks_test.rb | 2 +- test/cases/logging_test.rb | 10 +++++----- test/cases/rescue_test.rb | 8 +++----- 8 files changed, 29 insertions(+), 37 deletions(-) create mode 100644 lib/activejob.rb diff --git a/lib/active_job/enqueuing.rb b/lib/active_job/enqueuing.rb index 0f094fb624..e3ac11ba97 100644 --- a/lib/active_job/enqueuing.rb +++ b/lib/active_job/enqueuing.rb @@ -3,19 +3,19 @@ require 'active_job/arguments' module ActiveJob module Enqueuing extend ActiveSupport::Concern - + module ClassMethods # Push a job onto the queue. The arguments must be legal JSON types # (string, int, float, nil, true, false, hash or array) or # ActiveModel::GlobalIdentication instances. Arbitrary Ruby objects # are not supported. # - # Returns an instance of the job class queued with args available in + # Returns an instance of the job class queued with args available in # Job#arguments. def enqueue(*args) new(args).tap do |job| job.run_callbacks :enqueue do - queue_adapter.enqueue self, *Arguments.serialize(args) + queue_adapter.enqueue self, job.job_id, *Arguments.serialize(args) end end end @@ -24,7 +24,7 @@ module ActiveJob # # enqueue_in(1.week, "mike") # - # Returns an instance of the job class queued with args available in + # Returns an instance of the job class queued with args available in # Job#arguments and the timestamp in Job#enqueue_at. def enqueue_in(interval, *args) enqueue_at interval.seconds.from_now, *args @@ -34,19 +34,19 @@ module ActiveJob # # enqueue_at(Date.tomorrow.midnight, "mike") # - # Returns an instance of the job class queued with args available in + # Returns an instance of the job class queued with args available in # Job#arguments and the timestamp in Job#enqueue_at. def enqueue_at(timestamp, *args) new(args).tap do |job| job.enqueued_at = timestamp job.run_callbacks :enqueue do - queue_adapter.enqueue_at self, timestamp.to_f, *Arguments.serialize(args) + queue_adapter.enqueue_at self, timestamp.to_f, job.job_id, *Arguments.serialize(args) end end end end - + included do attr_accessor :arguments attr_accessor :enqueued_at @@ -55,11 +55,11 @@ module ActiveJob def initialize(arguments = nil) @arguments = arguments end - + def retry_now self.class.enqueue *arguments end - + def retry_in(interval) self.class.enqueue_in interval, *arguments end diff --git a/lib/active_job/execution.rb b/lib/active_job/execution.rb index d21fd32292..78ada3d908 100644 --- a/lib/active_job/execution.rb +++ b/lib/active_job/execution.rb @@ -4,12 +4,13 @@ require 'active_job/arguments' module ActiveJob module Execution extend ActiveSupport::Concern - + included do include ActiveSupport::Rescuable end - def execute(*serialized_args) + def execute(job_id, *serialized_args) + self.job_id = job_id self.arguments = Arguments.deserialize(serialized_args) run_callbacks :perform do diff --git a/lib/active_job/logging.rb b/lib/active_job/logging.rb index ea0ba2dddc..d913aee03d 100644 --- a/lib/active_job/logging.rb +++ b/lib/active_job/logging.rb @@ -26,10 +26,10 @@ module ActiveJob before_enqueue do |job| if job.enqueued_at ActiveSupport::Notifications.instrument "enqueue_at.active_job", - adapter: job.class.queue_adapter, job: job.class, args: job.arguments, timestamp: job.enqueued_at + adapter: job.class.queue_adapter, job: job.class, job_id: job.job_id, args: job.arguments, timestamp: job.enqueued_at else ActiveSupport::Notifications.instrument "enqueue.active_job", - adapter: job.class.queue_adapter, job: job.class, args: job.arguments + adapter: job.class.queue_adapter, job: job.class, job_id: job.job_id, args: job.arguments end end end @@ -50,11 +50,11 @@ module ActiveJob class LogSubscriber < ActiveSupport::LogSubscriber def enqueue(event) - info "Enqueued #{event.payload[:job].name} to #{queue_name(event)}" + args_info(event) + info "Enqueued #{event.payload[:job].name} (Job ID: #{event.payload[:job_id]}) to #{queue_name(event)}" + args_info(event) end def enqueue_at(event) - info "Enqueued #{event.payload[:job].name} to #{queue_name(event)} at #{enqueued_at(event)}" + args_info(event) + info "Enqueued #{event.payload[:job].name} (Job ID: #{event.payload[:job_id]}) to #{queue_name(event)} at #{enqueued_at(event)}" + args_info(event) end def perform_start(event) @@ -67,7 +67,7 @@ module ActiveJob private def queue_name(event) - event.payload[:adapter].name.demodulize.remove('Adapter') + event.payload[:adapter].name.demodulize.remove('Adapter') + "(#{event.payload[:job].queue_name})" end def args_info(event) diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index f7ed4669bd..84904f84f2 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -8,11 +8,11 @@ module ActiveJob class ResqueAdapter class << self def enqueue(job, *args) - Resque.enqueue JobWrapper.new(job), job, *args + Resque.enqueue_to job.queue_name, JobWrapper, job.name, *args end def enqueue_at(job, timestamp, *args) - Resque.enqueue_at timestamp, JobWrapper.new(job), job, *args + Resque.enqueue_at_with_queue job.queue_name, timestamp, JobWrapper, job.name, *args end end @@ -22,14 +22,6 @@ module ActiveJob job_name.constantize.new.execute *args end end - - def initialize(job) - @queue = job.queue_name - end - - def to_s - self.class.name - end end end end diff --git a/lib/activejob.rb b/lib/activejob.rb new file mode 100644 index 0000000000..fea38731af --- /dev/null +++ b/lib/activejob.rb @@ -0,0 +1 @@ +require 'active_job' diff --git a/test/cases/callbacks_test.rb b/test/cases/callbacks_test.rb index 01a9b9d26b..9a0657ee89 100644 --- a/test/cases/callbacks_test.rb +++ b/test/cases/callbacks_test.rb @@ -5,7 +5,7 @@ require 'active_support/core_ext/object/inclusion' class CallbacksTest < ActiveSupport::TestCase test 'perform callbacks' do - performed_callback_job = CallbackJob.new.tap { |j| j.execute } + performed_callback_job = CallbackJob.new.tap { |j| j.execute("A-JOB-ID") } assert "CallbackJob ran before_perform".in? performed_callback_job.history assert "CallbackJob ran after_perform".in? performed_callback_job.history assert "CallbackJob ran around_perform_start".in? performed_callback_job.history diff --git a/test/cases/logging_test.rb b/test/cases/logging_test.rb index 194944db11..537702edd4 100644 --- a/test/cases/logging_test.rb +++ b/test/cases/logging_test.rb @@ -46,7 +46,7 @@ class AdapterTest < ActiveSupport::TestCase def test_enqueue_job_logging HelloJob.enqueue "Cristian" - assert_match(/Enqueued HelloJob to .*?:.*Cristian/, @logger.messages) + assert_match(/Enqueued HelloJob \(Job ID: .*?\) to .*?:.*Cristian/, @logger.messages) end def test_perform_job_logging @@ -69,9 +69,9 @@ class AdapterTest < ActiveSupport::TestCase def test_perform_nested_jobs_logging NestedJob.enqueue assert_match(/\[LoggingJob\] \[.*?\]/, @logger.messages) - assert_match(/\[ActiveJob\] Enqueued NestedJob to/, @logger.messages) + assert_match(/\[ActiveJob\] Enqueued NestedJob \(Job ID: .*\) to/, @logger.messages) assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Performing NestedJob from/, @logger.messages) - assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Enqueued LoggingJob to .* with arguments: "NestedJob"/, @logger.messages) + assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Enqueued LoggingJob \(Job ID: .*?\) to .* with arguments: "NestedJob"/, @logger.messages) assert_match(/\[ActiveJob\].*\[LoggingJob\] \[LOGGING-JOB-ID\] Performing LoggingJob from .* with arguments: "NestedJob"/, @logger.messages) assert_match(/\[ActiveJob\].*\[LoggingJob\] \[LOGGING-JOB-ID\] Dummy, here is it: NestedJob/, @logger.messages) assert_match(/\[ActiveJob\].*\[LoggingJob\] \[LOGGING-JOB-ID\] Performed LoggingJob from .* in/, @logger.messages) @@ -80,14 +80,14 @@ class AdapterTest < ActiveSupport::TestCase def test_enqueue_at_job_logging HelloJob.enqueue_at 1, "Cristian" - assert_match(/Enqueued HelloJob to .*? at.*Cristian/, @logger.messages) + assert_match(/Enqueued HelloJob \(Job ID: .*\) to .*? at.*Cristian/, @logger.messages) rescue NotImplementedError skip end def test_enqueue_in_job_logging HelloJob.enqueue_in 2, "Cristian" - assert_match(/Enqueued HelloJob to .*? at.*Cristian/, @logger.messages) + assert_match(/Enqueued HelloJob \(Job ID: .*\) to .*? at.*Cristian/, @logger.messages) rescue NotImplementedError skip end diff --git a/test/cases/rescue_test.rb b/test/cases/rescue_test.rb index aea8b036d4..0e995405f7 100644 --- a/test/cases/rescue_test.rb +++ b/test/cases/rescue_test.rb @@ -7,17 +7,15 @@ class RescueTest < ActiveSupport::TestCase setup do $BUFFER = [] end - + test 'rescue perform exception with retry' do - job = RescueJob.new - job.execute("david") + RescueJob.enqueue("david") assert_equal [ "rescued from ArgumentError", "performed beautifully" ], $BUFFER end test 'let through unhandled perform exception' do - job = RescueJob.new assert_raises(RescueJob::OtherError) do - job.execute("other") + RescueJob.enqueue("other") end end end -- cgit v1.2.3 From 68f25a40f528967142d1a37762d96e0020897005 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Thu, 12 Jun 2014 14:30:53 +0300 Subject: Fixed failing test --- test/cases/rescue_test.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/cases/rescue_test.rb b/test/cases/rescue_test.rb index 0e995405f7..3d4831bc62 100644 --- a/test/cases/rescue_test.rb +++ b/test/cases/rescue_test.rb @@ -9,13 +9,15 @@ class RescueTest < ActiveSupport::TestCase end test 'rescue perform exception with retry' do - RescueJob.enqueue("david") + job = RescueJob.new + job.execute(SecureRandom.uuid, "david") assert_equal [ "rescued from ArgumentError", "performed beautifully" ], $BUFFER end test 'let through unhandled perform exception' do + job = RescueJob.new assert_raises(RescueJob::OtherError) do - RescueJob.enqueue("other") + job.execute(SecureRandom.uuid, "other") end end end -- cgit v1.2.3 From bab64e62ec6c609ece50f5ad7e5c16e56846b263 Mon Sep 17 00:00:00 2001 From: Ignacio Tolosa Date: Mon, 16 Jun 2014 13:45:29 -0400 Subject: fix delayed job adapter timestamp --- lib/active_job/queue_adapters/delayed_job_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_job/queue_adapters/delayed_job_adapter.rb b/lib/active_job/queue_adapters/delayed_job_adapter.rb index c0561044be..bfeaa836d2 100644 --- a/lib/active_job/queue_adapters/delayed_job_adapter.rb +++ b/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -9,7 +9,7 @@ module ActiveJob end def enqueue_at(job, timestamp, *args) - JobWrapper.new.delay(queue: job.queue_name, run_at: timestamp).perform(job, *args) + JobWrapper.new.delay(queue: job.queue_name, run_at: Time.at(timestamp)).perform(job, *args) end end -- cgit v1.2.3 From fc9267e34ac7ccb7f5cdfc9296bb38cd105c728b Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Thu, 26 Jun 2014 22:29:21 +0300 Subject: Require global_identification in serialisations --- lib/active_job/arguments.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/active_job/arguments.rb b/lib/active_job/arguments.rb index 604c3bfe1d..ba314ccf14 100644 --- a/lib/active_job/arguments.rb +++ b/lib/active_job/arguments.rb @@ -1,4 +1,5 @@ require 'active_model/global_locator' +require 'active_model/global_identification' module ActiveJob class Arguments -- cgit v1.2.3 From ba49be1c3ab2088e6a88d66a4b9a90f2d3dc5111 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Sun, 29 Jun 2014 15:59:38 +0000 Subject: [feature] ActiveJob generator --- lib/rails/generators/active_job/job_generator.rb | 22 ++++++++++++++++++++++ lib/rails/generators/active_job/templates/job.rb | 9 +++++++++ 2 files changed, 31 insertions(+) create mode 100644 lib/rails/generators/active_job/job_generator.rb create mode 100644 lib/rails/generators/active_job/templates/job.rb diff --git a/lib/rails/generators/active_job/job_generator.rb b/lib/rails/generators/active_job/job_generator.rb new file mode 100644 index 0000000000..fe4af0d2cc --- /dev/null +++ b/lib/rails/generators/active_job/job_generator.rb @@ -0,0 +1,22 @@ +require 'rails/generators/named_base' + +module ActiveJob + module Generators # :nodoc: + class JobGenerator < Rails::Generators::NamedBase # :nodoc: + desc 'This generator creates an active job file at app/jobs' + + class_option :queue, type: :string, default: 'default', desc: 'The queue name for the generated job' + + def self.default_generator_root + File.dirname(__FILE__) + end + + check_class_collision suffix: 'Job' + + def create_job_file + template 'job.rb', File.join('app/jobs', class_path, "#{file_name}_job.rb") + end + + end + end +end \ No newline at end of file diff --git a/lib/rails/generators/active_job/templates/job.rb b/lib/rails/generators/active_job/templates/job.rb new file mode 100644 index 0000000000..6a21616d30 --- /dev/null +++ b/lib/rails/generators/active_job/templates/job.rb @@ -0,0 +1,9 @@ +<% module_namespacing do -%> +class <%= class_name %>Job < ActiveJob::Base + queue_as :<%= options[:queue] %> + + def perform + # Do something later + end +end +<% end -%> \ No newline at end of file -- cgit v1.2.3 From 261e51b0caca4e1dfa08f9a398df407933fd9650 Mon Sep 17 00:00:00 2001 From: Tatsuhiko Miyagawa Date: Wed, 2 Jul 2014 19:32:40 -0700 Subject: Explicitly call #to_s when serializing GlobalID. Addresses #95 --- lib/active_job/arguments.rb | 2 +- test/cases/parameters_test.rb | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/active_job/arguments.rb b/lib/active_job/arguments.rb index ba314ccf14..ef6a3fce1d 100644 --- a/lib/active_job/arguments.rb +++ b/lib/active_job/arguments.rb @@ -17,7 +17,7 @@ module ActiveJob def self.serialize_argument(argument) case argument when ActiveModel::GlobalIdentification - argument.global_id + argument.global_id.to_s when *TYPE_WHITELIST argument when Array diff --git a/test/cases/parameters_test.rb b/test/cases/parameters_test.rb index 737f3c26b7..76e8a8059a 100644 --- a/test/cases/parameters_test.rb +++ b/test/cases/parameters_test.rb @@ -26,8 +26,8 @@ class ParameterSerializationTest < ActiveSupport::TestCase end test 'should dive deep into arrays or hashes' do - assert_equal [ { "a" => Person.find(5).gid }.with_indifferent_access ], ActiveJob::Arguments.serialize([ { a: Person.find(5) } ]) - assert_equal [ [ Person.find(5).gid ] ], ActiveJob::Arguments.serialize([ [ Person.find(5) ] ]) + assert_equal [ { "a" => Person.find(5).gid.to_s }.with_indifferent_access ], ActiveJob::Arguments.serialize([ { a: Person.find(5) } ]) + assert_equal [ [ Person.find(5).gid.to_s ] ], ActiveJob::Arguments.serialize([ [ Person.find(5) ] ]) end test 'should dive deep into arrays or hashes and raise exception on complex objects' do @@ -45,11 +45,11 @@ class ParameterSerializationTest < ActiveSupport::TestCase end test 'should serialize records with global id' do - assert_equal [ Person.find(5).gid ], ActiveJob::Arguments.serialize([ Person.find(5) ]) + assert_equal [ Person.find(5).gid.to_s ], ActiveJob::Arguments.serialize([ Person.find(5) ]) end test 'should serialize values and records together' do - assert_equal [ 3, Person.find(5).gid ], ActiveJob::Arguments.serialize([ 3, Person.find(5) ]) + assert_equal [ 3, Person.find(5).gid.to_s ], ActiveJob::Arguments.serialize([ 3, Person.find(5) ]) end end -- cgit v1.2.3 From c5c74974926a378d3c4ae7afd0dac576dc4220ad Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Thu, 3 Jul 2014 16:26:06 +0000 Subject: require 'resque-scheduler' fix #97 --- lib/active_job/queue_adapters/resque_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index 84904f84f2..9c223db15e 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -1,7 +1,7 @@ require 'resque' require 'active_support/core_ext/enumerable' require 'active_support/core_ext/array/access' -require 'resque_scheduler' +require 'resque-scheduler' module ActiveJob module QueueAdapters -- cgit v1.2.3 From 392c48278507ef5ac43dd450725ce0ffb6b4d4ce Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Thu, 3 Jul 2014 16:44:01 +0000 Subject: require both variant of resque-scheduler --- lib/active_job/queue_adapters/resque_adapter.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index 9c223db15e..cddc4b5424 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -1,7 +1,11 @@ require 'resque' require 'active_support/core_ext/enumerable' require 'active_support/core_ext/array/access' -require 'resque-scheduler' +begin + require 'resque-scheduler' +rescue LoadError + require 'resque_scheduler' +end module ActiveJob module QueueAdapters -- cgit v1.2.3 From 780fd61c24abad4b6f4fe4996ec0504d91d35915 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Wed, 14 Aug 2013 09:35:14 -0500 Subject: Remove unused, broken, legacy ruby-prof extension from 2005 [ci skip] --- railties/lib/rails/rubyprof_ext.rb | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 railties/lib/rails/rubyprof_ext.rb diff --git a/railties/lib/rails/rubyprof_ext.rb b/railties/lib/rails/rubyprof_ext.rb deleted file mode 100644 index 017eba3a76..0000000000 --- a/railties/lib/rails/rubyprof_ext.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'prof' - -module Prof #:nodoc: - # Adapted from Shugo Maeda's unprof.rb - def self.print_profile(results, io = $stderr) - total = results.detect { |i| - i.method_class.nil? && i.method_id == :"#toplevel" - }.total_time - total = 0.001 if total < 0.001 - - io.puts " %% cumulative self self total" - io.puts " time seconds seconds calls ms/call ms/call name" - - sum = 0.0 - results.each do |r| - sum += r.self_time - - name = if r.method_class.nil? - r.method_id.to_s - elsif r.method_class.is_a?(Class) - "#{r.method_class}##{r.method_id}" - else - "#{r.method_class}.#{r.method_id}" - end - io.printf "%6.2f %8.3f %8.3f %8d %8.2f %8.2f %s\n", - r.self_time / total * 100, - sum, - r.self_time, - r.count, - r.self_time * 1000 / r.count, - r.total_time * 1000 / r.count, - name - end - end -end -- cgit v1.2.3 From 65a227beda8c32972cb6fdb17ca93a4f5e828044 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Fri, 25 Jul 2014 12:02:09 -0500 Subject: Correct tools/profile usage example [ci skip] --- tools/profile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/profile b/tools/profile index fbea67492b..d4b2fc5fbd 100755 --- a/tools/profile +++ b/tools/profile @@ -1,6 +1,6 @@ #!/usr/bin/env ruby # Example: -# tools/profile activesupport/lib/active_support.rb +# ./profile ../activesupport/lib/active_support.rb [ruby-prof mode] ENV['NO_RELOAD'] ||= '1' ENV['RAILS_ENV'] ||= 'development' -- cgit v1.2.3 From f36c46754fa420343e7b01695f09e62293a40597 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Fri, 25 Jul 2014 12:09:49 -0500 Subject: Encapsulate tools/profile functionality in CodeTools::Profiler Fix bug in path expansion of input filepath [ci skip] --- tools/profile | 128 ++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 76 insertions(+), 52 deletions(-) diff --git a/tools/profile b/tools/profile index d4b2fc5fbd..5c916aafc8 100755 --- a/tools/profile +++ b/tools/profile @@ -1,71 +1,95 @@ #!/usr/bin/env ruby # Example: -# ./profile ../activesupport/lib/active_support.rb [ruby-prof mode] +# tools/profile activesupport/lib/active_support.rb [ruby-prof mode] [ruby-prof printer] ENV['NO_RELOAD'] ||= '1' ENV['RAILS_ENV'] ||= 'development' -require 'benchmark' +module CodeTools + class Profiler -module RequireProfiler - private - def require(file, *args) RequireProfiler.profile(file) { super } end - def load(file, *args) RequireProfiler.profile(file) { super } end - - @depth, @stats = 0, [] - class << self - attr_accessor :depth - attr_accessor :stats - - def profile(file) - stats << [file, depth] - self.depth += 1 - result = nil - elapsed = Benchmark.realtime { result = yield } - self.depth -= 1 - stats.pop if stats.last.first == file - stats << [file, depth, elapsed] if result - result + attr_reader :path, :mode + def initialize(path, mode=nil) + @path, @mode = path, mode + require 'benchmark' end - end -end -GC.start -before_rss = `ps -o rss= -p #{Process.pid}`.to_i + def profile_requires + GC.start + before_rss = `ps -o rss= -p #{Process.pid}`.to_i -path = ARGV.shift -if mode = ARGV.shift - require 'ruby-prof' - RubyProf.measure_mode = RubyProf.const_get(mode.upcase) - RubyProf.start -else - Object.instance_eval { include RequireProfiler } -end + if mode + require 'ruby-prof' + RubyProf.measure_mode = RubyProf.const_get(mode.upcase) + RubyProf.start + else + Object.instance_eval { include RequireProfiler } + end + + elapsed = Benchmark.realtime { require path } + results = RubyProf.stop if mode -elapsed = Benchmark.realtime { require path } -results = RubyProf.stop if mode + GC.start + after_rss = `ps -o rss= -p #{Process.pid}`.to_i -GC.start -after_rss = `ps -o rss= -p #{Process.pid}`.to_i + if mode + if printer = ARGV.shift + puts "RubyProf outputting to stderr with printer #{printer}" + RubyProf.const_get("#{printer.to_s.classify}Printer").new(results).print($stdout) + elsif RubyProf.const_defined?(:CallStackPrinter) + filename = "#{File.basename(path, '.rb')}.#{mode}.html" + puts "RubyProf outputting to #{filename}" + File.open(filename, 'w') do |out| + RubyProf::CallStackPrinter.new(results).print(out) + end + else + filename = "#{File.basename(path, '.rb')}.#{mode}.callgrind" + puts "RubyProf outputting to #{filename}" + File.open(filename, 'w') do |out| + RubyProf::CallTreePrinter.new(results).print(out) + end + end + end -if mode - if printer = ARGV.shift - RubyProf.const_get("#{printer.to_s.classify}Printer").new(results).print($stdout) - elsif RubyProf.const_defined?(:CallStackPrinter) - File.open("#{File.basename(path, '.rb')}.#{mode}.html", 'w') do |out| - RubyProf::CallStackPrinter.new(results).print(out) + RequireProfiler.stats.each do |file, depth, sec| + if sec + puts "%8.1f ms %s%s" % [sec * 1000, ' ' * depth, file] + else + puts "#{' ' * (13 + depth)}#{file}" + end + end + puts "%8.1f ms %d KB RSS" % [elapsed * 1000, after_rss - before_rss] end - else - File.open("#{File.basename(path, '.rb')}.#{mode}.callgrind", 'w') do |out| - RubyProf::CallTreePrinter.new(results).print(out) + + module RequireProfiler + private + def require(file, *args) RequireProfiler.profile(file) { super } end + def load(file, *args) RequireProfiler.profile(file) { super } end + + @depth, @stats = 0, [] + class << self + attr_accessor :depth + attr_accessor :stats + + def profile(file) + stats << [file, depth] + self.depth += 1 + result = nil + elapsed = Benchmark.realtime { result = yield } + self.depth -= 1 + stats.pop if stats.last.first == file + stats << [file, depth, elapsed] if result + result + end + end end end end - -RequireProfiler.stats.each do |file, depth, sec| - if sec - puts "%8.1f ms %s%s" % [sec * 1000, ' ' * depth, file] +if $0 == __FILE__ + if (filename = ARGV.shift) + path = File.expand_path(filename) + mode = ARGV.shift + CodeTools::Profiler.new(path, mode).profile_requires else - puts "#{' ' * (13 + depth)}#{file}" + STDERR.puts "No file path entered. Usage is tools/profile path/to/file.rb [ruby-prof mode] [ruby-prof printer]" end end -puts "%8.1f ms %d KB RSS" % [elapsed * 1000, after_rss - before_rss] -- cgit v1.2.3 From 54a4065074485ece05c4fe598d16d108cb292d88 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Fri, 25 Jul 2014 13:06:22 -0500 Subject: Optionally add String extensions for ruby-prof printer option [ci skip] --- tools/profile | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tools/profile b/tools/profile index 5c916aafc8..62daa2f0bd 100755 --- a/tools/profile +++ b/tools/profile @@ -84,6 +84,32 @@ module CodeTools end end end +# ruby-prof printer name causes the third arg to be sent :classify +# which is probably overkill if you already know the name of the ruby-prof +# printer you want to use, e.g. Graph +begin + require 'active_support/inflector' + require 'active_support/core_ext/string/inflections' +rescue LoadError + STDERR.puts $!.message + class String + # File activesupport/lib/active_support/inflector/methods.rb, line 150 + def classify + # strip out any leading schema name + camelize(self.sub(/.*\./, '')) + end + # File activesupport/lib/active_support/inflector/methods.rb, line 68 + def camelize(uppercase_first_letter = true) + string = self + if uppercase_first_letter + string = string.sub(/^[a-z\d]*/) { $&.capitalize } + else + string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase } + end + string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub('/', '::') + end + end +end if $0 == __FILE__ if (filename = ARGV.shift) path = File.expand_path(filename) -- cgit v1.2.3 From 3cbeb8d8eaf217f8eb38f5ea26f766f5cd978ff3 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Fri, 25 Jul 2014 13:23:54 -0500 Subject: Fail profiler fast when input is not a ruby file [ci skip] --- tools/profile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/profile b/tools/profile index 62daa2f0bd..a35dd18b77 100755 --- a/tools/profile +++ b/tools/profile @@ -6,9 +6,11 @@ ENV['RAILS_ENV'] ||= 'development' module CodeTools class Profiler + Error = Class.new(StandardError) attr_reader :path, :mode def initialize(path, mode=nil) + assert_ruby_file_exists(path) @path, @mode = path, mode require 'benchmark' end @@ -60,6 +62,16 @@ module CodeTools puts "%8.1f ms %d KB RSS" % [elapsed * 1000, after_rss - before_rss] end + private + + def assert_ruby_file_exists(path) + fail Error.new("No such file") unless File.exists?(path) + fail Error.new("#{path} is a directory") if File.directory?(path) + ruby_extension = File.extname(path) == '.rb' + ruby_executable = File.open(path, 'rb') {|f| f.readline } =~ [/\A#!.*ruby/] + fail Error.new("Not a ruby file") unless ruby_extension or ruby_executable + end + module RequireProfiler private def require(file, *args) RequireProfiler.profile(file) { super } end -- cgit v1.2.3 From 91d199259bc7a85763d487a1d0fbf8cd50fe29a0 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Thu, 27 Jun 2013 23:18:30 -0500 Subject: Encapsulate rake lines from ActiveRecord/ActionPack as CodeTools::LineStatistics [ci skip] --- actionpack/Rakefile | 24 +++--------------------- activerecord/Rakefile | 24 +++--------------------- tools/line_statistics | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 42 deletions(-) create mode 100755 tools/line_statistics diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 7eab972595..d12213a2d8 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -40,27 +40,9 @@ task :release => :package do end task :lines do - lines, codelines, total_lines, total_codelines = 0, 0, 0, 0 - - FileList["lib/**/*.rb"].each do |file_name| - next if file_name =~ /vendor/ - File.open(file_name, 'r') do |f| - while line = f.gets - lines += 1 - next if line =~ /^\s*$/ - next if line =~ /^\s*#/ - codelines += 1 - end - end - puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}" - - total_lines += lines - total_codelines += codelines - - lines, codelines = 0, 0 - end - - puts "Total: Lines #{total_lines}, LOC #{total_codelines}" + load File.expand_path('..', File.dirname(__FILE__)) + '/tools/line_statistics' + files = FileList["lib/**/*.rb"] + CodeTools::LineStatistics.new(files).print_loc end rule '.rb' => '.y' do |t| diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 7769966a22..b1069e5dcc 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -146,27 +146,9 @@ task :drop_postgresql_databases => 'db:postgresql:drop' task :rebuild_postgresql_databases => 'db:postgresql:rebuild' task :lines do - lines, codelines, total_lines, total_codelines = 0, 0, 0, 0 - - FileList["lib/active_record/**/*.rb"].each do |file_name| - next if file_name =~ /vendor/ - File.open(file_name, 'r') do |f| - while line = f.gets - lines += 1 - next if line =~ /^\s*$/ - next if line =~ /^\s*#/ - codelines += 1 - end - end - puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}" - - total_lines += lines - total_codelines += codelines - - lines, codelines = 0, 0 - end - - puts "Total: Lines #{total_lines}, LOC #{total_codelines}" + load File.expand_path('..', File.dirname(__FILE__)) + '/tools/line_statistics' + files = FileList["lib/active_record/**/*.rb"] + CodeTools::LineStatistics.new(files).print_loc end spec = eval(File.read('activerecord.gemspec')) diff --git a/tools/line_statistics b/tools/line_statistics new file mode 100755 index 0000000000..5eb5cfdc3d --- /dev/null +++ b/tools/line_statistics @@ -0,0 +1,41 @@ +#!/usr/bin/env ruby +# Example: +# files = FileList["lib/active_record/**/*.rb"] +# CodeTools::LineStatistics.new(files).print_loc +module CodeTools + class LineStatistics + + # @param files [Array, FileList, Enumerable] + # e.g. FileList["lib/active_record/**/*.rb"] + def initialize(files) + @files = Array(files).compact + end + + # Calculates LOC for each file + # Outputs each file and a total LOC + def print_loc + lines, codelines, total_lines, total_codelines = 0, 0, 0, 0 + + @files.each do |file_name| + next if file_name =~ /vendor/ + File.open(file_name, 'r') do |f| + while line = f.gets + lines += 1 + next if line =~ /^\s*$/ + next if line =~ /^\s*#/ + codelines += 1 + end + end + puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}" + + total_lines += lines + total_codelines += codelines + + lines, codelines = 0, 0 + end + + puts "Total: Lines #{total_lines}, LOC #{total_codelines}" + end + + end +end -- cgit v1.2.3 From 21730dea6b5e5bb4bdb3420ae31238ee94925a0a Mon Sep 17 00:00:00 2001 From: Tim Riley Date: Tue, 29 Jul 2014 09:58:13 +1000 Subject: Fix database serialization of job class names with Que MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When passing a class constant as a job argument, Que’s stored procedures serialize it to JSON in the database as `{}`. This means that when the job (the ActiveJob “wrapper”) is deserialized from the database, it can’t find the original job class to run again. Changing the Que adapter to serialize the job class as a string fixes this behaviour. This change makes the adapter consistent with other adapters too (which constantize a class string in their JobWrapper#perform methods. --- lib/active_job/queue_adapters/que_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_job/queue_adapters/que_adapter.rb b/lib/active_job/queue_adapters/que_adapter.rb index 3d30b2bc72..4af3a0fbcf 100644 --- a/lib/active_job/queue_adapters/que_adapter.rb +++ b/lib/active_job/queue_adapters/que_adapter.rb @@ -5,7 +5,7 @@ module ActiveJob class QueAdapter class << self def enqueue(job, *args) - JobWrapper.enqueue job, *args, queue: job.queue_name + JobWrapper.enqueue job.to_s, *args, queue: job.queue_name end def enqueue_at(job, timestamp, *args) @@ -15,7 +15,7 @@ module ActiveJob class JobWrapper < Que::Job def run(job, *args) - job.new.execute *args + job.constantize.new.execute *args end end end -- cgit v1.2.3 From d1f9b0857f6afaab1b61b8c687942dd0b0f680ee Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 29 Jul 2014 11:00:26 +0200 Subject: Rename variables for consistency. --- lib/active_job/queue_adapters/que_adapter.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/active_job/queue_adapters/que_adapter.rb b/lib/active_job/queue_adapters/que_adapter.rb index 4af3a0fbcf..15a607bcb6 100644 --- a/lib/active_job/queue_adapters/que_adapter.rb +++ b/lib/active_job/queue_adapters/que_adapter.rb @@ -5,7 +5,7 @@ module ActiveJob class QueAdapter class << self def enqueue(job, *args) - JobWrapper.enqueue job.to_s, *args, queue: job.queue_name + JobWrapper.enqueue job.name, *args, queue: job.queue_name end def enqueue_at(job, timestamp, *args) @@ -14,8 +14,8 @@ module ActiveJob end class JobWrapper < Que::Job - def run(job, *args) - job.constantize.new.execute *args + def run(job_name, *args) + job_name.constantize.new.execute *args end end end -- cgit v1.2.3 From 7a9493ff55ff49979a1d155f3e4472360c1f6967 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 1 Aug 2014 09:01:52 -0700 Subject: Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 9644b37119..5adb41b0b6 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,10 @@ We currently have adapters for: * [Sucker Punch](https://github.com/brandonhilkert/sucker_punch) +## Auxiliary gems + +* [activejob-stats](https://github.com/seuros/activejob-stats) + ## Under development as a gem, targeted for Rails inclusion Active Job is currently being developed in a separate repository until it's -- cgit v1.2.3 From fcc2cd5a8faff4d56f57111687d0267545a01f05 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 1 Aug 2014 13:55:16 -0700 Subject: Improve the error message when resque-scheduler is not available --- lib/active_job/queue_adapters/resque_adapter.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index cddc4b5424..ab65bfa39c 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -1,10 +1,12 @@ require 'resque' require 'active_support/core_ext/enumerable' require 'active_support/core_ext/array/access' + begin require 'resque-scheduler' -rescue LoadError - require 'resque_scheduler' +rescue LoadError => e + $stderr.puts 'The ActiveJob resque adapter requires resque-scheduler. Please add it to your Gemfile and run bundle install' + raise e end module ActiveJob -- cgit v1.2.3 From 38ee4fd8d3c46616bc8a09f7486de4a5069df91a Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 1 Aug 2014 16:56:38 -0700 Subject: Fix the test requirement of resque_scheduler --- lib/active_job/queue_adapters/resque_adapter.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/active_job/queue_adapters/resque_adapter.rb b/lib/active_job/queue_adapters/resque_adapter.rb index ab65bfa39c..b228825f07 100644 --- a/lib/active_job/queue_adapters/resque_adapter.rb +++ b/lib/active_job/queue_adapters/resque_adapter.rb @@ -4,9 +4,13 @@ require 'active_support/core_ext/array/access' begin require 'resque-scheduler' -rescue LoadError => e - $stderr.puts 'The ActiveJob resque adapter requires resque-scheduler. Please add it to your Gemfile and run bundle install' - raise e +rescue LoadError + begin + require 'resque_scheduler' + rescue LoadError + $stderr.puts 'The ActiveJob resque adapter requires resque-scheduler. Please add it to your Gemfile and run bundle install' + raise e + end end module ActiveJob -- cgit v1.2.3 From 6ff5972c0db45c192af9c0d056d786c61821f29f Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Thu, 7 Aug 2014 00:09:28 +0300 Subject: Fixed qu, queue_classic, sneakers adapters --- lib/active_job/queue_adapters/qu_adapter.rb | 8 +++++--- lib/active_job/queue_adapters/queue_classic_adapter.rb | 6 +++--- lib/active_job/queue_adapters/sneakers_adapter.rb | 8 +++++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/active_job/queue_adapters/qu_adapter.rb b/lib/active_job/queue_adapters/qu_adapter.rb index 7e69229801..cdf4ae4ce9 100644 --- a/lib/active_job/queue_adapters/qu_adapter.rb +++ b/lib/active_job/queue_adapters/qu_adapter.rb @@ -5,7 +5,9 @@ module ActiveJob class QuAdapter class << self def enqueue(job, *args) - Qu::Payload.new(klass: JobWrapper, args: [job, *args], queue: job.queue_name).push + Qu::Payload.new(klass: JobWrapper, args: [job.name, *args]).tap do |payload| + payload.instance_variable_set(:@queue, job.queue_name) + end.push end def enqueue_at(job, timestamp, *args) @@ -14,8 +16,8 @@ module ActiveJob end class JobWrapper < Qu::Job - def initialize(job, *args) - @job = job + def initialize(job_name, *args) + @job = job_name.constantize @args = args end diff --git a/lib/active_job/queue_adapters/queue_classic_adapter.rb b/lib/active_job/queue_adapters/queue_classic_adapter.rb index db7189f076..c61e0e30db 100644 --- a/lib/active_job/queue_adapters/queue_classic_adapter.rb +++ b/lib/active_job/queue_adapters/queue_classic_adapter.rb @@ -5,7 +5,7 @@ module ActiveJob class QueueClassicAdapter class << self def enqueue(job, *args) - QC::Queue.new(job.queue_name).enqueue("#{JobWrapper.name}.perform", job, *args) + QC::Queue.new(job.queue_name).enqueue("#{JobWrapper.name}.perform", job.name, *args) end def enqueue_at(job, timestamp, *args) @@ -14,8 +14,8 @@ module ActiveJob end class JobWrapper - def self.perform(job, *args) - job.new.execute *args + def self.perform(job_name, *args) + job_name.constantize.new.execute *args end end end diff --git a/lib/active_job/queue_adapters/sneakers_adapter.rb b/lib/active_job/queue_adapters/sneakers_adapter.rb index f7da691935..094b128547 100644 --- a/lib/active_job/queue_adapters/sneakers_adapter.rb +++ b/lib/active_job/queue_adapters/sneakers_adapter.rb @@ -10,7 +10,7 @@ module ActiveJob def enqueue(job, *args) @monitor.synchronize do JobWrapper.from_queue job.queue_name - JobWrapper.enqueue [ job, *args ] + JobWrapper.enqueue ActiveSupport::JSON.encode([ job.name, *args ]) end end @@ -22,8 +22,10 @@ module ActiveJob class JobWrapper include Sneakers::Worker - def work(job, *args) - job.new.execute *args + def work(msg) + job_name, *args = ActiveSupport::JSON.decode(msg) + job_name.constantize.new.execute *args + ack! end end end -- cgit v1.2.3 From 664546c208c406bba08b8eda2e6a600154d7232e Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Tue, 5 Aug 2014 09:05:14 +0300 Subject: Integration testing --- .gitignore | 2 + .travis.yml | 10 ++ Gemfile | 9 ++ Gemfile.lock | 157 +++++++++++++++------ Rakefile | 32 ++++- lib/active_job/queue_adapters/sneakers_adapter.rb | 1 + test/dummy/Rakefile | 3 + test/dummy/app/assets/images/.keep | 0 .../app/controllers/application_controller.rb | 3 + test/dummy/app/controllers/concerns/.keep | 0 test/dummy/app/helpers/application_helper.rb | 2 + test/dummy/app/jobs/test_job.rb | 9 ++ test/dummy/app/mailers/.keep | 0 test/dummy/app/models/.keep | 0 test/dummy/app/models/concerns/.keep | 0 test/dummy/config.ru | 4 + test/dummy/config/application.rb | 9 ++ test/dummy/config/boot.rb | 5 + test/dummy/config/database.yml | 3 + test/dummy/config/environment.rb | 2 + test/dummy/config/environments/test.rb | 13 ++ test/dummy/config/initializers/activejob.rb | 65 +++++++++ test/dummy/config/initializers/session_store.rb | 1 + test/dummy/config/routes.rb | 2 + test/dummy/config/secrets.yml | 2 + .../migrate/20140804200445_create_delayed_jobs.rb | 22 +++ test/dummy/db/schema.rb | 32 +++++ test/dummy/db/test.sqlite3 | Bin 0 -> 24576 bytes test/dummy/lib/assets/.keep | 0 test/dummy/log/.keep | 0 test/dummy/tmp/.keep | 0 test/helper.rb | 12 +- test/integration/queuing_test.rb | 18 +++ test/support/integration/adapters/backburner.rb | 15 ++ test/support/integration/adapters/delayed_job.rb | 14 ++ test/support/integration/adapters/qu.rb | 14 ++ test/support/integration/adapters/que.rb | 19 +++ test/support/integration/adapters/queue_classic.rb | 21 +++ test/support/integration/adapters/resque.rb | 18 +++ test/support/integration/adapters/sidekiq.rb | 19 +++ test/support/integration/adapters/sneakers.rb | 18 +++ test/support/integration/adapters/sucker_punch.rb | 5 + test/support/integration/helper.rb | 12 ++ test/support/integration/jobs_manager.rb | 23 +++ test/support/integration/test_case_helpers.rb | 30 ++++ 45 files changed, 574 insertions(+), 52 deletions(-) create mode 100644 .gitignore create mode 100644 test/dummy/Rakefile create mode 100644 test/dummy/app/assets/images/.keep create mode 100644 test/dummy/app/controllers/application_controller.rb create mode 100644 test/dummy/app/controllers/concerns/.keep create mode 100644 test/dummy/app/helpers/application_helper.rb create mode 100644 test/dummy/app/jobs/test_job.rb create mode 100644 test/dummy/app/mailers/.keep create mode 100644 test/dummy/app/models/.keep create mode 100644 test/dummy/app/models/concerns/.keep create mode 100644 test/dummy/config.ru create mode 100644 test/dummy/config/application.rb create mode 100644 test/dummy/config/boot.rb create mode 100644 test/dummy/config/database.yml create mode 100644 test/dummy/config/environment.rb create mode 100644 test/dummy/config/environments/test.rb create mode 100644 test/dummy/config/initializers/activejob.rb create mode 100644 test/dummy/config/initializers/session_store.rb create mode 100644 test/dummy/config/routes.rb create mode 100644 test/dummy/config/secrets.yml create mode 100644 test/dummy/db/migrate/20140804200445_create_delayed_jobs.rb create mode 100644 test/dummy/db/schema.rb create mode 100644 test/dummy/db/test.sqlite3 create mode 100644 test/dummy/lib/assets/.keep create mode 100644 test/dummy/log/.keep create mode 100644 test/dummy/tmp/.keep create mode 100644 test/integration/queuing_test.rb create mode 100644 test/support/integration/adapters/backburner.rb create mode 100644 test/support/integration/adapters/delayed_job.rb create mode 100644 test/support/integration/adapters/qu.rb create mode 100644 test/support/integration/adapters/que.rb create mode 100644 test/support/integration/adapters/queue_classic.rb create mode 100644 test/support/integration/adapters/resque.rb create mode 100644 test/support/integration/adapters/sidekiq.rb create mode 100644 test/support/integration/adapters/sneakers.rb create mode 100644 test/support/integration/adapters/sucker_punch.rb create mode 100644 test/support/integration/helper.rb create mode 100644 test/support/integration/jobs_manager.rb create mode 100644 test/support/integration/test_case_helpers.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..ee45263e91 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/test/dummy/log/* +/test/dummy/tmp/* diff --git a/.travis.yml b/.travis.yml index cadc24d01f..fba12f9d0d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,10 @@ before_install: - travis_retry gem install bundler +- sudo apt-get update -qq +- sudo apt-get install beanstalkd +- echo "START=yes" | sudo tee -a /etc/default/beanstalkd +- sudo /etc/init.d/beanstalkd start + rvm: - 1.9.3 - 2.0.0 @@ -7,6 +12,8 @@ rvm: - ruby-head - rbx-2 - jruby +env: +- QC_DATABASE_URL="postgres://postgres@localhost/active_jobs_qc_int_test" QUE_DATABASE_URL="postgres://postgres@localhost/active_jobs_qc_int_test" matrix: allow_failures: - rvm: rbx-2 @@ -27,3 +34,6 @@ notifications: - secure: AgZwJA+9VdnWAw7QN9Z5s6RpQIzsEB0q7V+p3pCzXY45156WocL8iNQx+KnyOQ8jbRUt4L/XIOiZl5xHf4pHjXytHWHNhetAlVQP/hPeDcCSk/h0g5gqgf6QABdp38tBNaUq866bXHgCOZYPwwP9bypcmuv2SLyfIO+b/PBgqN0= services: - redis +- rabbitmq +addons: + postgresql: "9.3" diff --git a/Gemfile b/Gemfile index aa228d5669..12a1798fd5 100644 --- a/Gemfile +++ b/Gemfile @@ -14,3 +14,12 @@ gem 'que' gem 'backburner' gem 'qu-rails', github: "bkeepers/qu", branch: "master" gem 'qu-redis' + +#for integration testing +gem 'arel', github: 'rails/arel' +gem 'rack', github: 'rack/rack' +gem 'i18n', github: 'svenfuchs/i18n' +gem 'rails', github: 'rails/rails' +gem 'sqlite3' +gem 'delayed_job_active_record' +gem 'sequel' diff --git a/Gemfile.lock b/Gemfile.lock index 7dac1c6e88..53ba5c055b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: git://github.com/bkeepers/qu.git - revision: 2175633a834504423368d71cb10fb9f072d76cd2 + revision: 50f3788f2b55ddd4dc939767fb35aebefa260322 branch: master specs: qu (0.2.0) @@ -11,6 +11,70 @@ GIT qu (= 0.2.0) redis-namespace +GIT + remote: git://github.com/rack/rack.git + revision: e98a9f7ef0ddd9589145ea953948c73a8ce3caa9 + specs: + rack (1.6.0.alpha) + +GIT + remote: git://github.com/rails/arel.git + revision: 66cee768bc163537087037a583f60639eae49fc3 + specs: + arel (6.0.0.20140505020427) + +GIT + remote: git://github.com/rails/rails.git + revision: b2e88043b52a8f83820a0f4e8a65aa42fd40c544 + specs: + actionmailer (4.2.0.alpha) + actionpack (= 4.2.0.alpha) + actionview (= 4.2.0.alpha) + mail (~> 2.5, >= 2.5.4) + actionpack (4.2.0.alpha) + actionview (= 4.2.0.alpha) + activesupport (= 4.2.0.alpha) + rack (~> 1.6.0.alpha) + rack-test (~> 0.6.2) + actionview (4.2.0.alpha) + activesupport (= 4.2.0.alpha) + builder (~> 3.1) + erubis (~> 2.7.0) + activemodel (4.2.0.alpha) + activesupport (= 4.2.0.alpha) + builder (~> 3.1) + activerecord (4.2.0.alpha) + activemodel (= 4.2.0.alpha) + activesupport (= 4.2.0.alpha) + arel (~> 6.0.0) + activesupport (4.2.0.alpha) + i18n (>= 0.7.0.dev, < 0.8) + json (~> 1.7, >= 1.7.7) + minitest (~> 5.1) + thread_safe (~> 0.1) + tzinfo (~> 1.1) + rails (4.2.0.alpha) + actionmailer (= 4.2.0.alpha) + actionpack (= 4.2.0.alpha) + actionview (= 4.2.0.alpha) + activemodel (= 4.2.0.alpha) + activerecord (= 4.2.0.alpha) + activesupport (= 4.2.0.alpha) + bundler (>= 1.3.0, < 2.0) + railties (= 4.2.0.alpha) + sprockets-rails (~> 2.1) + railties (4.2.0.alpha) + actionpack (= 4.2.0.alpha) + activesupport (= 4.2.0.alpha) + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + +GIT + remote: git://github.com/svenfuchs/i18n.git + revision: cb679b8cdbab675703a3f88de4d48a48f7b50e06 + specs: + i18n (0.7.0.dev) + PATH remote: . specs: @@ -21,27 +85,9 @@ PATH GEM remote: https://rubygems.org/ specs: - actionpack (4.1.1) - actionview (= 4.1.1) - activesupport (= 4.1.1) - rack (~> 1.5.2) - rack-test (~> 0.6.2) - actionview (4.1.1) - activesupport (= 4.1.1) - builder (~> 3.1) - erubis (~> 2.7.0) - activemodel (4.1.1) - activesupport (= 4.1.1) - builder (~> 3.1) - activemodel-globalid (0.1.0) + activemodel-globalid (0.1.1) activemodel (>= 4.1.0) activesupport (>= 4.1.0) - activesupport (4.1.1) - i18n (~> 0.6, >= 0.6.9) - json (~> 1.7, >= 1.7.7) - minitest (~> 5.1) - thread_safe (~> 0.1) - tzinfo (~> 1.1) amq-protocol (1.9.2) backburner (0.4.5) beaneater (~> 0.3.1) @@ -54,54 +100,56 @@ GEM timers (~> 1.1.0) connection_pool (2.0.0) dante (0.1.5) - delayed_job (4.0.1) + delayed_job (4.0.2) activesupport (>= 3.0, < 4.2) + delayed_job_active_record (4.0.1) + activerecord (>= 3.0, < 4.2) + delayed_job (>= 3.0, < 4.1) erubis (2.7.0) - i18n (0.6.9) + hike (1.2.3) json (1.8.1) - minitest (5.3.4) + mail (2.6.1) + mime-types (>= 1.16, < 3) + mime-types (2.3) + minitest (5.4.0) mono_logger (1.1.0) - multi_json (1.9.3) + multi_json (1.10.1) pg (0.17.1) - que (0.7.3) + que (0.8.1) queue_classic (2.2.3) pg (~> 0.17.0) - rack (1.5.2) - rack-protection (1.5.2) + rack-protection (1.5.3) rack rack-test (0.6.2) rack (>= 1.0) - railties (4.1.1) - actionpack (= 4.1.1) - activesupport (= 4.1.1) - rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) rake (10.3.2) - redis (3.0.7) - redis-namespace (1.4.1) - redis (~> 3.0.4) - resque (1.24.1) + redis (3.1.0) + redis-namespace (1.5.1) + redis (~> 3.0, >= 3.0.4) + resque (1.25.2) mono_logger (~> 1.0) multi_json (~> 1.0) - redis-namespace (~> 1.2) + redis-namespace (~> 1.3) sinatra (>= 0.9.2) vegas (~> 0.1.2) - resque-scheduler (2.2.0) - redis (>= 3.0.0) - resque (>= 1.20.0, < 1.25) + resque-scheduler (3.0.0) + mono_logger (~> 1.0) + redis (~> 3.0) + resque (~> 1.25) rufus-scheduler (~> 2.0) rufus-scheduler (2.0.24) tzinfo (>= 0.3.22) - serverengine (1.5.7) + sequel (4.8.0) + serverengine (1.5.9) sigdump (~> 0.2.2) - sidekiq (3.0.2) + sidekiq (3.2.1) celluloid (>= 0.15.2) connection_pool (>= 2.0.0) json redis (>= 3.0.6) redis-namespace (>= 1.3.1) sigdump (0.2.2) - sinatra (1.4.4) + sinatra (1.4.5) rack (~> 1.4) rack-protection (~> 1.4) tilt (~> 1.3, >= 1.3.4) @@ -110,14 +158,24 @@ GEM serverengine thor thread - sucker_punch (1.0.5) + sprockets (2.12.1) + hike (~> 1.2) + multi_json (~> 1.0) + rack (~> 1.0) + tilt (~> 1.1, != 1.3.0) + sprockets-rails (2.1.3) + actionpack (>= 3.0) + activesupport (>= 3.0) + sprockets (~> 2.8) + sqlite3 (1.3.9) + sucker_punch (1.1) celluloid (~> 0.15.2) thor (0.19.1) thread (0.1.4) - thread_safe (0.3.3) + thread_safe (0.3.4) tilt (1.4.1) timers (1.1.0) - tzinfo (1.1.0) + tzinfo (1.2.1) thread_safe (~> 0.1) vegas (0.1.11) rack (>= 1.0.0) @@ -127,15 +185,22 @@ PLATFORMS DEPENDENCIES activejob! + arel! backburner delayed_job + delayed_job_active_record + i18n! qu-rails! qu-redis que queue_classic + rack! + rails! rake resque resque-scheduler + sequel sidekiq sneakers (= 0.1.1.pre) + sqlite3 sucker_punch diff --git a/Rakefile b/Rakefile index 5780410eda..b2910de81d 100644 --- a/Rakefile +++ b/Rakefile @@ -18,13 +18,15 @@ end task default: :test +ADAPTERS = %w(inline delayed_job qu que queue_classic resque sidekiq sneakers sucker_punch backburner) + desc 'Run all adapter tests' task :test do - tasks = %w(test_inline test_delayed_job test_qu test_que test_queue_classic test_resque test_sidekiq test_sneakers test_sucker_punch test_backburner) + tasks = ADAPTERS.map{|a| "test_#{a}" }+["integration_test"] run_without_aborting(*tasks) end -%w(inline delayed_job qu que queue_classic resque sidekiq sneakers sucker_punch backburner).each do |adapter| +ADAPTERS.each do |adapter| Rake::TestTask.new("test_#{adapter}") do |t| t.libs << 'test' t.test_files = FileList['test/cases/**/*_test.rb'] @@ -38,3 +40,29 @@ end task "test_#{adapter}" => "#{adapter}:env" end + + + +desc 'Run all adapter integration tests' +task :integration_test do + tasks = (ADAPTERS-['inline']).map{|a| "integration_test_#{a}" } + run_without_aborting(*tasks) +end + +(ADAPTERS-['inline']).each do |adapter| + Rake::TestTask.new("integration_test_#{adapter}") do |t| + t.libs << 'test' + t.test_files = FileList['test/integration/**/*_test.rb'] + t.verbose = true + end + + namespace "integration_#{adapter}" do + task test: "integration_test_#{adapter}" + task(:env) do + ENV['AJADAPTER'] = adapter + ENV['AJ_INTEGRATION_TESTS'] = "1" + end + end + + task "integration_test_#{adapter}" => "integration_#{adapter}:env" +end diff --git a/lib/active_job/queue_adapters/sneakers_adapter.rb b/lib/active_job/queue_adapters/sneakers_adapter.rb index 094b128547..051a8c3bd7 100644 --- a/lib/active_job/queue_adapters/sneakers_adapter.rb +++ b/lib/active_job/queue_adapters/sneakers_adapter.rb @@ -21,6 +21,7 @@ module ActiveJob class JobWrapper include Sneakers::Worker + from_queue 'active_jobs_default' def work(msg) job_name, *args = ActiveSupport::JSON.decode(msg) diff --git a/test/dummy/Rakefile b/test/dummy/Rakefile new file mode 100644 index 0000000000..9866295e6a --- /dev/null +++ b/test/dummy/Rakefile @@ -0,0 +1,3 @@ +require 'sneakers/tasks' +require File.expand_path('../config/application', __FILE__) +Rails.application.load_tasks diff --git a/test/dummy/app/assets/images/.keep b/test/dummy/app/assets/images/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/dummy/app/controllers/application_controller.rb b/test/dummy/app/controllers/application_controller.rb new file mode 100644 index 0000000000..1c07694e9d --- /dev/null +++ b/test/dummy/app/controllers/application_controller.rb @@ -0,0 +1,3 @@ +class ApplicationController < ActionController::Base + protect_from_forgery with: :exception +end diff --git a/test/dummy/app/controllers/concerns/.keep b/test/dummy/app/controllers/concerns/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/dummy/app/helpers/application_helper.rb b/test/dummy/app/helpers/application_helper.rb new file mode 100644 index 0000000000..de6be7945c --- /dev/null +++ b/test/dummy/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/test/dummy/app/jobs/test_job.rb b/test/dummy/app/jobs/test_job.rb new file mode 100644 index 0000000000..281771a851 --- /dev/null +++ b/test/dummy/app/jobs/test_job.rb @@ -0,0 +1,9 @@ +class TestJob < ActiveJob::Base + queue_as :default + + def perform(x) + File.open(Rails.root.join("tmp/#{x}"), "w+") do |f| + f.write x + end + end +end diff --git a/test/dummy/app/mailers/.keep b/test/dummy/app/mailers/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/dummy/app/models/.keep b/test/dummy/app/models/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/dummy/app/models/concerns/.keep b/test/dummy/app/models/concerns/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/dummy/config.ru b/test/dummy/config.ru new file mode 100644 index 0000000000..5bc2a619e8 --- /dev/null +++ b/test/dummy/config.ru @@ -0,0 +1,4 @@ +# This file is used by Rack-based servers to start the application. + +require ::File.expand_path('../config/environment', __FILE__) +run Rails.application diff --git a/test/dummy/config/application.rb b/test/dummy/config/application.rb new file mode 100644 index 0000000000..8b06039a68 --- /dev/null +++ b/test/dummy/config/application.rb @@ -0,0 +1,9 @@ +require File.expand_path('../boot', __FILE__) +require 'rails/all' +Bundler.require(*Rails.groups) + +module Dummy + class Application < Rails::Application + end +end + diff --git a/test/dummy/config/boot.rb b/test/dummy/config/boot.rb new file mode 100644 index 0000000000..6266cfc509 --- /dev/null +++ b/test/dummy/config/boot.rb @@ -0,0 +1,5 @@ +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__) + +require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) +$LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__) diff --git a/test/dummy/config/database.yml b/test/dummy/config/database.yml new file mode 100644 index 0000000000..f5f3aaf1fa --- /dev/null +++ b/test/dummy/config/database.yml @@ -0,0 +1,3 @@ +test: + adapter: sqlite3 + database: "db/test.sqlite3" diff --git a/test/dummy/config/environment.rb b/test/dummy/config/environment.rb new file mode 100644 index 0000000000..7fd2f91b8f --- /dev/null +++ b/test/dummy/config/environment.rb @@ -0,0 +1,2 @@ +require File.expand_path('../application', __FILE__) +Rails.application.initialize! diff --git a/test/dummy/config/environments/test.rb b/test/dummy/config/environments/test.rb new file mode 100644 index 0000000000..ff0318412f --- /dev/null +++ b/test/dummy/config/environments/test.rb @@ -0,0 +1,13 @@ +Rails.application.configure do + config.cache_classes = true + config.eager_load = false + config.serve_static_assets = true + config.static_cache_control = 'public, max-age=3600' + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + config.action_dispatch.show_exceptions = false + config.action_controller.allow_forgery_protection = false + config.action_mailer.delivery_method = :test + config.active_support.deprecation = :stderr + config.log_level = :debug +end diff --git a/test/dummy/config/initializers/activejob.rb b/test/dummy/config/initializers/activejob.rb new file mode 100644 index 0000000000..5fcde86c96 --- /dev/null +++ b/test/dummy/config/initializers/activejob.rb @@ -0,0 +1,65 @@ +case ENV['AJADAPTER'] +when "delayed_job" + ActiveJob::Base.queue_adapter = :delayed_job +when "sidekiq" + ActiveJob::Base.queue_adapter = :sidekiq +when "resque" + ActiveJob::Base.queue_adapter = :resque + Resque.redis = Redis::Namespace.new 'active_jobs_int_test', redis: Redis.connect(url: "tcp://127.0.0.1:6379/12", :thread_safe => true) + Resque.logger = Rails.logger +when 'qu' + ActiveJob::Base.queue_adapter = :qu + ENV['REDISTOGO_URL'] = "tcp://127.0.0.1:6379/12" + backend = Qu::Backend::Redis.new + backend.namespace = "active_jobs_int_test" + Qu.backend = backend + Qu.logger = Rails.logger + Qu.interval = 0.5 +when 'que' + ActiveJob::Base.queue_adapter = :que + QUE_URL = ENV['QUE_DATABASE_URL'] || 'postgres://localhost/active_jobs_que_int_test' + uri = URI.parse(QUE_URL) + user = uri.user||ENV['USER'] + pass = uri.password + db = uri.path[1..-1] + %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'drop database "#{db}"' -U #{user} -t template1} + %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'create database "#{db}"' -U #{user} -t template1} + Que.connection = Sequel.connect(QUE_URL) + Que.migrate! + Que.mode = :off + Que.worker_count = 1 +when 'queue_classic' + ENV['QC_DATABASE_URL'] ||= 'postgres://localhost/active_jobs_qc_int_test' + ENV['QC_LISTEN_TIME'] = "0.5" + ActiveJob::Base.queue_adapter = :queue_classic + uri = URI.parse(ENV['QC_DATABASE_URL']) + user = uri.user||ENV['USER'] + pass = uri.password + db = uri.path[1..-1] + %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'drop database "#{db}"' -U #{user} -t template1} + %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'create database "#{db}"' -U #{user} -t template1} + QC::Setup.create +when 'sidekiq' + ActiveJob::Base.queue_adapter = :sidekiq +when 'sneakers' + ActiveJob::Base.queue_adapter = :sneakers + Sneakers.configure :heartbeat => 2, + :amqp => 'amqp://guest:guest@localhost:5672', + :vhost => '/', + :exchange => 'active_jobs_sneakers_int_test', + :exchange_type => :direct, + :daemonize => true, + :threads => 1, + :workers => 1, + :pid_path => Rails.root.join("tmp/sneakers.pid").to_s, + :log => Rails.root.join("log/sneakers.log").to_s +when 'sucker_punch' + ActiveJob::Base.queue_adapter = :sucker_punch +when 'backburner' + ActiveJob::Base.queue_adapter = :backburner + Backburner.configure do |config| + config.logger = Rails.logger + end +else + ActiveJob::Base.queue_adapter = nil +end diff --git a/test/dummy/config/initializers/session_store.rb b/test/dummy/config/initializers/session_store.rb new file mode 100644 index 0000000000..70a5a506a9 --- /dev/null +++ b/test/dummy/config/initializers/session_store.rb @@ -0,0 +1 @@ +Rails.application.config.session_store :cookie_store, key: '_dummy_session' diff --git a/test/dummy/config/routes.rb b/test/dummy/config/routes.rb new file mode 100644 index 0000000000..1daf9a4121 --- /dev/null +++ b/test/dummy/config/routes.rb @@ -0,0 +1,2 @@ +Rails.application.routes.draw do +end diff --git a/test/dummy/config/secrets.yml b/test/dummy/config/secrets.yml new file mode 100644 index 0000000000..7dfacb38ea --- /dev/null +++ b/test/dummy/config/secrets.yml @@ -0,0 +1,2 @@ +test: + secret_key_base: b83ee5aeada663bc4270a1817d0ca43b2784017cc77dc8afcd60967cc968d4ce30caff9eb682766129e18a4048c4d5ebf14eabf463fc37ad67c18934f4345545 diff --git a/test/dummy/db/migrate/20140804200445_create_delayed_jobs.rb b/test/dummy/db/migrate/20140804200445_create_delayed_jobs.rb new file mode 100644 index 0000000000..ec0dd93ce1 --- /dev/null +++ b/test/dummy/db/migrate/20140804200445_create_delayed_jobs.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/test/dummy/db/schema.rb b/test/dummy/db/schema.rb new file mode 100644 index 0000000000..012a099f9c --- /dev/null +++ b/test/dummy/db/schema.rb @@ -0,0 +1,32 @@ +# 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 +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20140804200445) do + + create_table "delayed_jobs", force: true do |t| + t.integer "priority", default: 0, null: false + t.integer "attempts", default: 0, null: false + t.text "handler", null: false + t.text "last_error" + t.datetime "run_at" + t.datetime "locked_at" + t.datetime "failed_at" + t.string "locked_by" + t.string "queue" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority" + +end diff --git a/test/dummy/db/test.sqlite3 b/test/dummy/db/test.sqlite3 new file mode 100644 index 0000000000..671e20102e Binary files /dev/null and b/test/dummy/db/test.sqlite3 differ diff --git a/test/dummy/lib/assets/.keep b/test/dummy/lib/assets/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/dummy/log/.keep b/test/dummy/log/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/dummy/tmp/.keep b/test/dummy/tmp/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/helper.rb b/test/helper.rb index 7ba7607df2..104dabd861 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -6,10 +6,14 @@ $LOAD_PATH << File.dirname(__FILE__) + "/../lib" require 'active_job' adapter = ENV['AJADAPTER'] || 'inline' -require "adapters/#{adapter}" -puts "Testing using #{adapter}" +puts "Testing#{" integration" if ENV['AJ_INTEGRATION_TESTS']} using #{adapter}" -require 'active_support/testing/autorun' +if ENV['AJ_INTEGRATION_TESTS'] + require 'support/integration/helper' +else + require "adapters/#{adapter}" +end +require 'active_support/testing/autorun' -ActiveJob::Base.logger.level = Logger::ERROR +ActiveJob::Base.logger.level = Logger::DEBUG diff --git a/test/integration/queuing_test.rb b/test/integration/queuing_test.rb new file mode 100644 index 0000000000..bf6137af4c --- /dev/null +++ b/test/integration/queuing_test.rb @@ -0,0 +1,18 @@ +require 'helper' +require 'jobs/logging_job' +require 'active_support/core_ext/numeric/time' + + +class QueuingTest < ActiveSupport::TestCase + setup do + + end + + test 'run queued job' do + id = "AJ-#{SecureRandom.uuid}" + TestJob.enqueue id + sleep 2 + assert Dummy::Application.root.join("tmp/#{id}").exist? + end + +end diff --git a/test/support/integration/adapters/backburner.rb b/test/support/integration/adapters/backburner.rb new file mode 100644 index 0000000000..7271d50a45 --- /dev/null +++ b/test/support/integration/adapters/backburner.rb @@ -0,0 +1,15 @@ +module BackburnerJobsManager + def clear_jobs + Backburner::Worker.connection.tubes.all.map &:clear + end + + def start_workers + @thread = Thread.new { Backburner.work "active-jobs-default" } + end + + def stop_workers + @thread.kill + end + +end + diff --git a/test/support/integration/adapters/delayed_job.rb b/test/support/integration/adapters/delayed_job.rb new file mode 100644 index 0000000000..3e55933438 --- /dev/null +++ b/test/support/integration/adapters/delayed_job.rb @@ -0,0 +1,14 @@ +module DelayedJobJobsManager + def clear_jobs + Delayed::Job.delete_all + end + + def start_workers + @worker = Delayed::Worker.new(quiet: false, sleep_delay: 0.5) + @thread = Thread.new { @worker.start } + end + + def stop_workers + @worker.stop + end +end diff --git a/test/support/integration/adapters/qu.rb b/test/support/integration/adapters/qu.rb new file mode 100644 index 0000000000..12d063ea0d --- /dev/null +++ b/test/support/integration/adapters/qu.rb @@ -0,0 +1,14 @@ +module QuJobsManager + def clear_jobs + Qu.clear "active_jobs_default" + end + + def start_workers + @thread = Thread.new { Qu::Worker.new("active_jobs_default").start } + end + + def stop_workers + @thread.kill + end +end + diff --git a/test/support/integration/adapters/que.rb b/test/support/integration/adapters/que.rb new file mode 100644 index 0000000000..f15c9af910 --- /dev/null +++ b/test/support/integration/adapters/que.rb @@ -0,0 +1,19 @@ +module QueJobsManager + def clear_jobs + Que.clear! + end + + def start_workers + @thread = Thread.new do + loop do + Que::Job.work("active_jobs_default") + sleep 0.5 + end + end + end + + def stop_workers + @thread.kill + end +end + diff --git a/test/support/integration/adapters/queue_classic.rb b/test/support/integration/adapters/queue_classic.rb new file mode 100644 index 0000000000..3b24eca5b9 --- /dev/null +++ b/test/support/integration/adapters/queue_classic.rb @@ -0,0 +1,21 @@ +module QC; WAIT_TIME = 0.5; end + +module QueueClassicJobsManager + def clear_jobs + # disabling this as it locks + # QC::Queue.new("active_jobs_default").delete_all + end + + def start_workers + @pid = fork do + QC::Conn.connection = QC::Conn.connect + worker = QC::Worker.new(q_name: 'active_jobs_default') + worker.start + end + end + + def stop_workers + Process.kill 'HUP', @pid + end +end + diff --git a/test/support/integration/adapters/resque.rb b/test/support/integration/adapters/resque.rb new file mode 100644 index 0000000000..b9811bd3e1 --- /dev/null +++ b/test/support/integration/adapters/resque.rb @@ -0,0 +1,18 @@ +module ResqueJobsManager + def clear_jobs + Resque.queues.each { |queue_name| Resque.redis.del "queue:#{queue_name}" } + Resque.redis.keys("delayed:*").each { |key| Resque.redis.del "#{key}" } + Resque.redis.del "delayed_queue_schedule" + end + + def start_workers + @thread = Thread.new do + Resque::Worker.new("*").work(0.5) + end + end + + def stop_workers + @thread.kill + end +end + diff --git a/test/support/integration/adapters/sidekiq.rb b/test/support/integration/adapters/sidekiq.rb new file mode 100644 index 0000000000..d3b7d15614 --- /dev/null +++ b/test/support/integration/adapters/sidekiq.rb @@ -0,0 +1,19 @@ +require 'sidekiq/launcher' +require 'sidekiq/api' + +module SidekiqJobsManager + def clear_jobs + Sidekiq::Queue.new("active_jobs_default").clear + end + + def start_workers + options = {:queues=>["active_jobs_default"], :concurrency=>1, :environment=>"test", :timeout=>8, :daemon=>true, :strict=>true} + @launcher = Sidekiq::Launcher.new(options) + @launcher.run + end + + def stop_workers + @launcher.stop + end +end + diff --git a/test/support/integration/adapters/sneakers.rb b/test/support/integration/adapters/sneakers.rb new file mode 100644 index 0000000000..5dcab68515 --- /dev/null +++ b/test/support/integration/adapters/sneakers.rb @@ -0,0 +1,18 @@ +require 'sneakers/runner' + +module SneakersJobsManager + def clear_jobs + end + + def start_workers + cmd = %{cd #{Rails.root.to_s} && (RAILS_ENV=test AJADAPTER=sneakers WORKERS=ActiveJob::QueueAdapters::SneakersAdapter::JobWrapper bundle exec rake --trace sneakers:run)} + `#{cmd}` + while !Rails.root.join("tmp/sneakers.pid").exist? do + sleep 0.5 + end + end + + def stop_workers + Process.kill 'TERM', File.open(Rails.root.join("tmp/sneakers.pid").to_s).read.to_i + end +end diff --git a/test/support/integration/adapters/sucker_punch.rb b/test/support/integration/adapters/sucker_punch.rb new file mode 100644 index 0000000000..317f9c80fd --- /dev/null +++ b/test/support/integration/adapters/sucker_punch.rb @@ -0,0 +1,5 @@ +module SuckerPunchJobsManager + def clear_jobs + end +end + diff --git a/test/support/integration/helper.rb b/test/support/integration/helper.rb new file mode 100644 index 0000000000..cb94e7cfb5 --- /dev/null +++ b/test/support/integration/helper.rb @@ -0,0 +1,12 @@ +ENV["RAILS_ENV"] = "test" +require File.expand_path("../../../dummy/config/environment.rb", __FILE__) +require "rails/test_help" +Rails.backtrace_cleaner.remove_silencers! + +require_relative 'test_case_helpers' +ActiveSupport::TestCase.send(:include, TestCaseHelpers) + +JobsManager.current_manager.setup +JobsManager.current_manager.start_workers +Minitest.after_run { JobsManager.current_manager.stop_workers } + diff --git a/test/support/integration/jobs_manager.rb b/test/support/integration/jobs_manager.rb new file mode 100644 index 0000000000..1da74193b1 --- /dev/null +++ b/test/support/integration/jobs_manager.rb @@ -0,0 +1,23 @@ +class JobsManager + @@managers = {} + attr :adapter_name + + def self.current_manager + @@managers[ENV['AJADAPTER']] ||= new(ENV['AJADAPTER']) + end + + def initialize(adapter_name) + @adapter_name = adapter_name + require_relative "adapters/#{adapter_name}" + extend "#{adapter_name.camelize}JobsManager".constantize + end + + def setup + end + + def start_workers + end + + def stop_workers + end +end diff --git a/test/support/integration/test_case_helpers.rb b/test/support/integration/test_case_helpers.rb new file mode 100644 index 0000000000..9a5eea0783 --- /dev/null +++ b/test/support/integration/test_case_helpers.rb @@ -0,0 +1,30 @@ +require 'active_support/concern' +require 'support/integration/jobs_manager' + +module TestCaseHelpers + extend ActiveSupport::Concern + + included do + self.use_transactional_fixtures = false + + setup do + clear_jobs + end + + teardown do + clear_jobs + FileUtils.rm_rf Dir[Dummy::Application.root.join("tmp/AJ-*")] + end + end + + protected + + def jobs_manager + JobsManager.current_manager + end + + def clear_jobs + jobs_manager.clear_jobs + end + +end -- cgit v1.2.3 From 7a41295734e2a2332dc09db08f08eda36f927ca3 Mon Sep 17 00:00:00 2001 From: Robin Dupret Date: Sat, 26 Jul 2014 20:10:43 +0200 Subject: Avoid relying on error messages when rescuing When we are rescuing from an error, it's a brittle approach to do checks with regular expressions on the raised message because it may change in in the future and error messages are different across implementations. The NameError API could be improved at the MRI level but for now we need to rely on its #name. A #== check will only pass for top level constants or only when the last constant of the path is missing so we need to rely on #include? instead. For instance: begin Namespace::Foo rescue NameError => e e.name # => :Namespace end However, if the name-space already exists, only the name of the first missing constant in the path is returned (e.g. for Math::PHI, the name would be :PHI). JRuby will return a fully qualified name (:"Math::PHI"). We need to keep the == check for 1.9 compatibility since const_get will raise a NameError with a name attribute set to the given string if it's one of "::" or "". See http://git.io/jnSN7g for further information. --- activesupport/lib/active_support/inflector/methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index 51720d0192..c9848fcf0c 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -303,8 +303,8 @@ module ActiveSupport def safe_constantize(camel_cased_word) constantize(camel_cased_word) rescue NameError => e - raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ || - e.name.to_s == camel_cased_word.to_s + raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) || + e.name.to_s == camel_cased_word.to_s) rescue ArgumentError => e raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/ end -- cgit v1.2.3 From 2638d5c72444db1dc73c0593cb35f9916fc6284c Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Mon, 11 Aug 2014 00:00:23 -0700 Subject: Fixed issue w/custom accessors + reserved name + inheritance Fixed an issue where custom accessor methods (such as those generated by `enum`) with the same name as a global method are incorrectly overridden when subclassing. This was partially fixed in 4155431 then broken again by e5f15a8. Fixes #16288. --- activerecord/CHANGELOG.md | 8 ++++++++ activerecord/lib/active_record/attribute_methods.rb | 13 ++++++++----- activerecord/test/cases/attribute_methods_test.rb | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 4b70d883fe..cbed360fff 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,11 @@ +* Fixed an issue where custom accessor methods (such as those generated by + `enum`) with the same name as a global method are incorrectly overridden + when subclassing. + + Fixes #16288. + + *Godfrey Chan* + * Define `id_was` to get the previous value of the primary key. Currently when we call id_was and we have a custom primary key name diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index a2bb78dfcc..82f1682b33 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -57,6 +57,8 @@ module ActiveRecord end end + class GeneratedAttributeMethods < Module; end # :nodoc: + module ClassMethods def inherited(child_class) #:nodoc: child_class.initialize_generated_modules @@ -64,7 +66,7 @@ module ActiveRecord end def initialize_generated_modules # :nodoc: - @generated_attribute_methods = Module.new { extend Mutex_m } + @generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m } @attribute_methods_generated = false include @generated_attribute_methods end @@ -113,10 +115,11 @@ module ActiveRecord if superclass == Base super else - # If B < A and A defines its own attribute method, then we don't want to overwrite that. - defined = method_defined_within?(method_name, superclass, superclass.generated_attribute_methods) - base_defined = Base.method_defined?(method_name) || Base.private_method_defined?(method_name) - defined && !base_defined || super + # If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass + # defines its own attribute method, then we don't want to overwrite that. + defined = method_defined_within?(method_name, superclass, Base) && + ! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods) + defined || super end end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index ab67cf4085..2c07b5cbad 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -810,6 +810,24 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert_equal "lol", topic.author_name end + def test_inherited_custom_accessors_with_reserved_names + klass = Class.new(ActiveRecord::Base) do + self.table_name = 'computers' + self.abstract_class = true + def system; "omg"; end + def system=(val); self.developer = val; end + end + + subklass = Class.new(klass) + [klass, subklass].each(&:define_attribute_methods) + + computer = subklass.find(1) + assert_equal "omg", computer.system + + computer.developer = 99 + assert_equal 99, computer.developer + end + def test_on_the_fly_super_invokable_generated_attribute_methods_via_method_missing klass = new_topic_like_ar_class do def title -- cgit v1.2.3 From 0c232779ece1ff6bf9bce9d55c54ea19867a2170 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Tue, 12 Aug 2014 09:29:21 +0000 Subject: Remove activejob integration tests --- Gemfile | 13 ++ Rakefile | 2 +- activejob/.gitignore | 2 - activejob/.travis.yml | 39 ---- activejob/Gemfile | 25 --- activejob/Gemfile.lock | 206 --------------------- activejob/Rakefile | 76 +++++--- activejob/test/cases/logging_test.rb | 1 + activejob/test/dummy/Rakefile | 3 - activejob/test/dummy/app/assets/images/.keep | 0 .../app/controllers/application_controller.rb | 3 - .../test/dummy/app/controllers/concerns/.keep | 0 .../test/dummy/app/helpers/application_helper.rb | 2 - activejob/test/dummy/app/jobs/test_job.rb | 9 - activejob/test/dummy/app/mailers/.keep | 0 activejob/test/dummy/app/models/.keep | 0 activejob/test/dummy/app/models/concerns/.keep | 0 activejob/test/dummy/config.ru | 4 - activejob/test/dummy/config/application.rb | 9 - activejob/test/dummy/config/boot.rb | 5 - activejob/test/dummy/config/database.yml | 3 - activejob/test/dummy/config/environment.rb | 2 - activejob/test/dummy/config/environments/test.rb | 13 -- .../test/dummy/config/initializers/activejob.rb | 65 ------- .../dummy/config/initializers/session_store.rb | 1 - activejob/test/dummy/config/routes.rb | 2 - activejob/test/dummy/config/secrets.yml | 2 - .../migrate/20140804200445_create_delayed_jobs.rb | 22 --- activejob/test/dummy/db/schema.rb | 32 ---- activejob/test/dummy/db/test.sqlite3 | Bin 24576 -> 0 bytes activejob/test/dummy/lib/assets/.keep | 0 activejob/test/dummy/log/.keep | 0 activejob/test/dummy/tmp/.keep | 0 activejob/test/helper.rb | 7 +- activejob/test/integration/queuing_test.rb | 18 -- .../support/integration/adapters/backburner.rb | 15 -- .../support/integration/adapters/delayed_job.rb | 14 -- activejob/test/support/integration/adapters/qu.rb | 14 -- activejob/test/support/integration/adapters/que.rb | 19 -- .../support/integration/adapters/queue_classic.rb | 21 --- .../test/support/integration/adapters/resque.rb | 18 -- .../test/support/integration/adapters/sidekiq.rb | 19 -- .../test/support/integration/adapters/sneakers.rb | 18 -- .../support/integration/adapters/sucker_punch.rb | 5 - activejob/test/support/integration/helper.rb | 12 -- activejob/test/support/integration/jobs_manager.rb | 23 --- .../test/support/integration/test_case_helpers.rb | 30 --- rails.gemspec | 1 + railties/lib/rails/all.rb | 1 + tasks/release.rb | 2 +- 50 files changed, 65 insertions(+), 713 deletions(-) delete mode 100644 activejob/.gitignore delete mode 100644 activejob/.travis.yml delete mode 100644 activejob/Gemfile delete mode 100644 activejob/Gemfile.lock delete mode 100644 activejob/test/dummy/Rakefile delete mode 100644 activejob/test/dummy/app/assets/images/.keep delete mode 100644 activejob/test/dummy/app/controllers/application_controller.rb delete mode 100644 activejob/test/dummy/app/controllers/concerns/.keep delete mode 100644 activejob/test/dummy/app/helpers/application_helper.rb delete mode 100644 activejob/test/dummy/app/jobs/test_job.rb delete mode 100644 activejob/test/dummy/app/mailers/.keep delete mode 100644 activejob/test/dummy/app/models/.keep delete mode 100644 activejob/test/dummy/app/models/concerns/.keep delete mode 100644 activejob/test/dummy/config.ru delete mode 100644 activejob/test/dummy/config/application.rb delete mode 100644 activejob/test/dummy/config/boot.rb delete mode 100644 activejob/test/dummy/config/database.yml delete mode 100644 activejob/test/dummy/config/environment.rb delete mode 100644 activejob/test/dummy/config/environments/test.rb delete mode 100644 activejob/test/dummy/config/initializers/activejob.rb delete mode 100644 activejob/test/dummy/config/initializers/session_store.rb delete mode 100644 activejob/test/dummy/config/routes.rb delete mode 100644 activejob/test/dummy/config/secrets.yml delete mode 100644 activejob/test/dummy/db/migrate/20140804200445_create_delayed_jobs.rb delete mode 100644 activejob/test/dummy/db/schema.rb delete mode 100644 activejob/test/dummy/db/test.sqlite3 delete mode 100644 activejob/test/dummy/lib/assets/.keep delete mode 100644 activejob/test/dummy/log/.keep delete mode 100644 activejob/test/dummy/tmp/.keep delete mode 100644 activejob/test/integration/queuing_test.rb delete mode 100644 activejob/test/support/integration/adapters/backburner.rb delete mode 100644 activejob/test/support/integration/adapters/delayed_job.rb delete mode 100644 activejob/test/support/integration/adapters/qu.rb delete mode 100644 activejob/test/support/integration/adapters/que.rb delete mode 100644 activejob/test/support/integration/adapters/queue_classic.rb delete mode 100644 activejob/test/support/integration/adapters/resque.rb delete mode 100644 activejob/test/support/integration/adapters/sidekiq.rb delete mode 100644 activejob/test/support/integration/adapters/sneakers.rb delete mode 100644 activejob/test/support/integration/adapters/sucker_punch.rb delete mode 100644 activejob/test/support/integration/helper.rb delete mode 100644 activejob/test/support/integration/jobs_manager.rb delete mode 100644 activejob/test/support/integration/test_case_helpers.rb diff --git a/Gemfile b/Gemfile index 04cef17458..884572c232 100644 --- a/Gemfile +++ b/Gemfile @@ -35,6 +35,19 @@ end # AS gem 'dalli', '>= 2.2.1' +# ActiveJob +gem 'resque' +gem 'resque-scheduler' +gem 'sidekiq' +gem 'sucker_punch' +gem 'delayed_job' +gem 'queue_classic' +gem 'sneakers', '0.1.1.pre' +gem 'que' +gem 'backburner' +gem 'qu-rails', github: "bkeepers/qu", branch: "master" +gem 'qu-redis' + # Add your own local bundler stuff local_gemfile = File.dirname(__FILE__) + "/.Gemfile" instance_eval File.read local_gemfile if File.exist? local_gemfile diff --git a/Rakefile b/Rakefile index 0737afd089..db27105cc3 100644 --- a/Rakefile +++ b/Rakefile @@ -11,7 +11,7 @@ task :build => "all:build" desc "Release all gems to rubygems and create a tag" task :release => "all:release" -PROJECTS = %w(activesupport activemodel actionpack actionview actionmailer activerecord railties) +PROJECTS = %w(activesupport activemodel actionpack actionview actionmailer activerecord railties activejob) desc 'Run all tests by default' task :default => %w(test test:isolated) diff --git a/activejob/.gitignore b/activejob/.gitignore deleted file mode 100644 index ee45263e91..0000000000 --- a/activejob/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/test/dummy/log/* -/test/dummy/tmp/* diff --git a/activejob/.travis.yml b/activejob/.travis.yml deleted file mode 100644 index fba12f9d0d..0000000000 --- a/activejob/.travis.yml +++ /dev/null @@ -1,39 +0,0 @@ -before_install: -- travis_retry gem install bundler -- sudo apt-get update -qq -- sudo apt-get install beanstalkd -- echo "START=yes" | sudo tee -a /etc/default/beanstalkd -- sudo /etc/init.d/beanstalkd start - -rvm: -- 1.9.3 -- 2.0.0 -- 2.1 -- ruby-head -- rbx-2 -- jruby -env: -- QC_DATABASE_URL="postgres://postgres@localhost/active_jobs_qc_int_test" QUE_DATABASE_URL="postgres://postgres@localhost/active_jobs_qc_int_test" -matrix: - allow_failures: - - rvm: rbx-2 - - rvm: jruby - - rvm: ruby-head - fast_finish: true -notifications: - email: false - irc: - on_success: change - on_failure: always - channels: - - irc.freenode.org#rails-contrib - campfire: - on_success: change - on_failure: always - rooms: - - secure: AgZwJA+9VdnWAw7QN9Z5s6RpQIzsEB0q7V+p3pCzXY45156WocL8iNQx+KnyOQ8jbRUt4L/XIOiZl5xHf4pHjXytHWHNhetAlVQP/hPeDcCSk/h0g5gqgf6QABdp38tBNaUq866bXHgCOZYPwwP9bypcmuv2SLyfIO+b/PBgqN0= -services: -- redis -- rabbitmq -addons: - postgresql: "9.3" diff --git a/activejob/Gemfile b/activejob/Gemfile deleted file mode 100644 index 12a1798fd5..0000000000 --- a/activejob/Gemfile +++ /dev/null @@ -1,25 +0,0 @@ -source 'https://rubygems.org' - -gemspec - -gem 'rake' -gem 'resque' -gem 'resque-scheduler' -gem 'sidekiq' -gem 'sucker_punch' -gem 'delayed_job' -gem 'queue_classic' -gem 'sneakers', '0.1.1.pre' -gem 'que' -gem 'backburner' -gem 'qu-rails', github: "bkeepers/qu", branch: "master" -gem 'qu-redis' - -#for integration testing -gem 'arel', github: 'rails/arel' -gem 'rack', github: 'rack/rack' -gem 'i18n', github: 'svenfuchs/i18n' -gem 'rails', github: 'rails/rails' -gem 'sqlite3' -gem 'delayed_job_active_record' -gem 'sequel' diff --git a/activejob/Gemfile.lock b/activejob/Gemfile.lock deleted file mode 100644 index 53ba5c055b..0000000000 --- a/activejob/Gemfile.lock +++ /dev/null @@ -1,206 +0,0 @@ -GIT - remote: git://github.com/bkeepers/qu.git - revision: 50f3788f2b55ddd4dc939767fb35aebefa260322 - branch: master - specs: - qu (0.2.0) - qu-rails (0.2.0) - qu (= 0.2.0) - railties (>= 3.2, < 5) - qu-redis (0.2.0) - qu (= 0.2.0) - redis-namespace - -GIT - remote: git://github.com/rack/rack.git - revision: e98a9f7ef0ddd9589145ea953948c73a8ce3caa9 - specs: - rack (1.6.0.alpha) - -GIT - remote: git://github.com/rails/arel.git - revision: 66cee768bc163537087037a583f60639eae49fc3 - specs: - arel (6.0.0.20140505020427) - -GIT - remote: git://github.com/rails/rails.git - revision: b2e88043b52a8f83820a0f4e8a65aa42fd40c544 - specs: - actionmailer (4.2.0.alpha) - actionpack (= 4.2.0.alpha) - actionview (= 4.2.0.alpha) - mail (~> 2.5, >= 2.5.4) - actionpack (4.2.0.alpha) - actionview (= 4.2.0.alpha) - activesupport (= 4.2.0.alpha) - rack (~> 1.6.0.alpha) - rack-test (~> 0.6.2) - actionview (4.2.0.alpha) - activesupport (= 4.2.0.alpha) - builder (~> 3.1) - erubis (~> 2.7.0) - activemodel (4.2.0.alpha) - activesupport (= 4.2.0.alpha) - builder (~> 3.1) - activerecord (4.2.0.alpha) - activemodel (= 4.2.0.alpha) - activesupport (= 4.2.0.alpha) - arel (~> 6.0.0) - activesupport (4.2.0.alpha) - i18n (>= 0.7.0.dev, < 0.8) - json (~> 1.7, >= 1.7.7) - minitest (~> 5.1) - thread_safe (~> 0.1) - tzinfo (~> 1.1) - rails (4.2.0.alpha) - actionmailer (= 4.2.0.alpha) - actionpack (= 4.2.0.alpha) - actionview (= 4.2.0.alpha) - activemodel (= 4.2.0.alpha) - activerecord (= 4.2.0.alpha) - activesupport (= 4.2.0.alpha) - bundler (>= 1.3.0, < 2.0) - railties (= 4.2.0.alpha) - sprockets-rails (~> 2.1) - railties (4.2.0.alpha) - actionpack (= 4.2.0.alpha) - activesupport (= 4.2.0.alpha) - rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) - -GIT - remote: git://github.com/svenfuchs/i18n.git - revision: cb679b8cdbab675703a3f88de4d48a48f7b50e06 - specs: - i18n (0.7.0.dev) - -PATH - remote: . - specs: - activejob (4.2.0.alpha) - activemodel-globalid - activesupport (>= 4.1.0) - -GEM - remote: https://rubygems.org/ - specs: - activemodel-globalid (0.1.1) - activemodel (>= 4.1.0) - activesupport (>= 4.1.0) - amq-protocol (1.9.2) - backburner (0.4.5) - beaneater (~> 0.3.1) - dante (~> 0.1.5) - beaneater (0.3.2) - builder (3.2.2) - bunny (1.1.9) - amq-protocol (>= 1.9.2) - celluloid (0.15.2) - timers (~> 1.1.0) - connection_pool (2.0.0) - dante (0.1.5) - delayed_job (4.0.2) - activesupport (>= 3.0, < 4.2) - delayed_job_active_record (4.0.1) - activerecord (>= 3.0, < 4.2) - delayed_job (>= 3.0, < 4.1) - erubis (2.7.0) - hike (1.2.3) - json (1.8.1) - mail (2.6.1) - mime-types (>= 1.16, < 3) - mime-types (2.3) - minitest (5.4.0) - mono_logger (1.1.0) - multi_json (1.10.1) - pg (0.17.1) - que (0.8.1) - queue_classic (2.2.3) - pg (~> 0.17.0) - rack-protection (1.5.3) - rack - rack-test (0.6.2) - rack (>= 1.0) - rake (10.3.2) - redis (3.1.0) - redis-namespace (1.5.1) - redis (~> 3.0, >= 3.0.4) - resque (1.25.2) - mono_logger (~> 1.0) - multi_json (~> 1.0) - redis-namespace (~> 1.3) - sinatra (>= 0.9.2) - vegas (~> 0.1.2) - resque-scheduler (3.0.0) - mono_logger (~> 1.0) - redis (~> 3.0) - resque (~> 1.25) - rufus-scheduler (~> 2.0) - rufus-scheduler (2.0.24) - tzinfo (>= 0.3.22) - sequel (4.8.0) - serverengine (1.5.9) - sigdump (~> 0.2.2) - sidekiq (3.2.1) - celluloid (>= 0.15.2) - connection_pool (>= 2.0.0) - json - redis (>= 3.0.6) - redis-namespace (>= 1.3.1) - sigdump (0.2.2) - sinatra (1.4.5) - rack (~> 1.4) - rack-protection (~> 1.4) - tilt (~> 1.3, >= 1.3.4) - sneakers (0.1.1.pre) - bunny (~> 1.1.3) - serverengine - thor - thread - sprockets (2.12.1) - hike (~> 1.2) - multi_json (~> 1.0) - rack (~> 1.0) - tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.1.3) - actionpack (>= 3.0) - activesupport (>= 3.0) - sprockets (~> 2.8) - sqlite3 (1.3.9) - sucker_punch (1.1) - celluloid (~> 0.15.2) - thor (0.19.1) - thread (0.1.4) - thread_safe (0.3.4) - tilt (1.4.1) - timers (1.1.0) - tzinfo (1.2.1) - thread_safe (~> 0.1) - vegas (0.1.11) - rack (>= 1.0.0) - -PLATFORMS - ruby - -DEPENDENCIES - activejob! - arel! - backburner - delayed_job - delayed_job_active_record - i18n! - qu-rails! - qu-redis - que - queue_classic - rack! - rails! - rake - resque - resque-scheduler - sequel - sidekiq - sneakers (= 0.1.1.pre) - sqlite3 - sucker_punch diff --git a/activejob/Rakefile b/activejob/Rakefile index b2910de81d..e661622165 100644 --- a/activejob/Rakefile +++ b/activejob/Rakefile @@ -1,6 +1,7 @@ -require 'bundler/gem_tasks' - require 'rake/testtask' +require 'rubygems/package_task' + +dir = File.dirname(__FILE__) def run_without_aborting(*tasks) errors = [] @@ -18,51 +19,66 @@ end task default: :test -ADAPTERS = %w(inline delayed_job qu que queue_classic resque sidekiq sneakers sucker_punch backburner) +ACTIVEJOB_ADAPTERS = %w(inline delayed_job qu que queue_classic resque sidekiq sneakers sucker_punch backburner) desc 'Run all adapter tests' task :test do - tasks = ADAPTERS.map{|a| "test_#{a}" }+["integration_test"] + tasks = ACTIVEJOB_ADAPTERS.map{|a| "test_#{a}" } run_without_aborting(*tasks) end -ADAPTERS.each do |adapter| - Rake::TestTask.new("test_#{adapter}") do |t| - t.libs << 'test' - t.test_files = FileList['test/cases/**/*_test.rb'] - t.verbose = true +namespace :test do + desc 'Run all adapter tests in isolation' + task :isolated do + tasks = ACTIVEJOB_ADAPTERS.map{|a| "isolated_test_#{a}" } + run_without_aborting(*tasks) + end +end + + +ACTIVEJOB_ADAPTERS.each do |adapter| + namespace :test do + Rake::TestTask.new(adapter => "#{adapter}:env") do |t| + t.description = "" + t.libs << 'test' + t.test_files = FileList['test/cases/**/*_test.rb'] + t.verbose = true + end + + namespace :isolated do + task adapter => "#{adapter}:env" do + Dir.glob("#{dir}/test/cases/**/*_test.rb").all? do |file| + sh(Gem.ruby, '-w', "-I#{dir}/lib", "-I#{dir}/test", file) + end or raise 'Failures' + end + end end namespace adapter do task test: "test_#{adapter}" + task isolated_test: "isolated_test_#{adapter}" + task(:env) { ENV['AJADAPTER'] = adapter } end - task "test_#{adapter}" => "#{adapter}:env" -end - + desc "Run #{adapter} tests" + task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"] -desc 'Run all adapter integration tests' -task :integration_test do - tasks = (ADAPTERS-['inline']).map{|a| "integration_test_#{a}" } - run_without_aborting(*tasks) + desc "Run #{adapter} tests in isolation" + task "isolated_test_#{adapter}" => ["#{adapter}:env", "test:isolated:#{adapter}"] end -(ADAPTERS-['inline']).each do |adapter| - Rake::TestTask.new("integration_test_#{adapter}") do |t| - t.libs << 'test' - t.test_files = FileList['test/integration/**/*_test.rb'] - t.verbose = true - end - namespace "integration_#{adapter}" do - task test: "integration_test_#{adapter}" - task(:env) do - ENV['AJADAPTER'] = adapter - ENV['AJ_INTEGRATION_TESTS'] = "1" - end - end +spec = eval(File.read('activejob.gemspec')) + +Gem::PackageTask.new(spec) do |p| + p.gem_spec = spec +end - task "integration_test_#{adapter}" => "integration_#{adapter}:env" +desc 'Release to rubygems' +task release: :package do + require 'rake/gemcutter' + Rake::Gemcutter::Tasks.new(spec).define + Rake::Task['gem:push'].invoke end diff --git a/activejob/test/cases/logging_test.rb b/activejob/test/cases/logging_test.rb index 537702edd4..a4c010c7bd 100644 --- a/activejob/test/cases/logging_test.rb +++ b/activejob/test/cases/logging_test.rb @@ -1,5 +1,6 @@ require 'helper' require "active_support/log_subscriber/test_helper" +require 'jobs/hello_job' require 'jobs/logging_job' require 'jobs/nested_job' diff --git a/activejob/test/dummy/Rakefile b/activejob/test/dummy/Rakefile deleted file mode 100644 index 9866295e6a..0000000000 --- a/activejob/test/dummy/Rakefile +++ /dev/null @@ -1,3 +0,0 @@ -require 'sneakers/tasks' -require File.expand_path('../config/application', __FILE__) -Rails.application.load_tasks diff --git a/activejob/test/dummy/app/assets/images/.keep b/activejob/test/dummy/app/assets/images/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/activejob/test/dummy/app/controllers/application_controller.rb b/activejob/test/dummy/app/controllers/application_controller.rb deleted file mode 100644 index 1c07694e9d..0000000000 --- a/activejob/test/dummy/app/controllers/application_controller.rb +++ /dev/null @@ -1,3 +0,0 @@ -class ApplicationController < ActionController::Base - protect_from_forgery with: :exception -end diff --git a/activejob/test/dummy/app/controllers/concerns/.keep b/activejob/test/dummy/app/controllers/concerns/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/activejob/test/dummy/app/helpers/application_helper.rb b/activejob/test/dummy/app/helpers/application_helper.rb deleted file mode 100644 index de6be7945c..0000000000 --- a/activejob/test/dummy/app/helpers/application_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module ApplicationHelper -end diff --git a/activejob/test/dummy/app/jobs/test_job.rb b/activejob/test/dummy/app/jobs/test_job.rb deleted file mode 100644 index 281771a851..0000000000 --- a/activejob/test/dummy/app/jobs/test_job.rb +++ /dev/null @@ -1,9 +0,0 @@ -class TestJob < ActiveJob::Base - queue_as :default - - def perform(x) - File.open(Rails.root.join("tmp/#{x}"), "w+") do |f| - f.write x - end - end -end diff --git a/activejob/test/dummy/app/mailers/.keep b/activejob/test/dummy/app/mailers/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/activejob/test/dummy/app/models/.keep b/activejob/test/dummy/app/models/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/activejob/test/dummy/app/models/concerns/.keep b/activejob/test/dummy/app/models/concerns/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/activejob/test/dummy/config.ru b/activejob/test/dummy/config.ru deleted file mode 100644 index 5bc2a619e8..0000000000 --- a/activejob/test/dummy/config.ru +++ /dev/null @@ -1,4 +0,0 @@ -# This file is used by Rack-based servers to start the application. - -require ::File.expand_path('../config/environment', __FILE__) -run Rails.application diff --git a/activejob/test/dummy/config/application.rb b/activejob/test/dummy/config/application.rb deleted file mode 100644 index 8b06039a68..0000000000 --- a/activejob/test/dummy/config/application.rb +++ /dev/null @@ -1,9 +0,0 @@ -require File.expand_path('../boot', __FILE__) -require 'rails/all' -Bundler.require(*Rails.groups) - -module Dummy - class Application < Rails::Application - end -end - diff --git a/activejob/test/dummy/config/boot.rb b/activejob/test/dummy/config/boot.rb deleted file mode 100644 index 6266cfc509..0000000000 --- a/activejob/test/dummy/config/boot.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Set up gems listed in the Gemfile. -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__) - -require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) -$LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__) diff --git a/activejob/test/dummy/config/database.yml b/activejob/test/dummy/config/database.yml deleted file mode 100644 index f5f3aaf1fa..0000000000 --- a/activejob/test/dummy/config/database.yml +++ /dev/null @@ -1,3 +0,0 @@ -test: - adapter: sqlite3 - database: "db/test.sqlite3" diff --git a/activejob/test/dummy/config/environment.rb b/activejob/test/dummy/config/environment.rb deleted file mode 100644 index 7fd2f91b8f..0000000000 --- a/activejob/test/dummy/config/environment.rb +++ /dev/null @@ -1,2 +0,0 @@ -require File.expand_path('../application', __FILE__) -Rails.application.initialize! diff --git a/activejob/test/dummy/config/environments/test.rb b/activejob/test/dummy/config/environments/test.rb deleted file mode 100644 index ff0318412f..0000000000 --- a/activejob/test/dummy/config/environments/test.rb +++ /dev/null @@ -1,13 +0,0 @@ -Rails.application.configure do - config.cache_classes = true - config.eager_load = false - config.serve_static_assets = true - config.static_cache_control = 'public, max-age=3600' - config.consider_all_requests_local = true - config.action_controller.perform_caching = false - config.action_dispatch.show_exceptions = false - config.action_controller.allow_forgery_protection = false - config.action_mailer.delivery_method = :test - config.active_support.deprecation = :stderr - config.log_level = :debug -end diff --git a/activejob/test/dummy/config/initializers/activejob.rb b/activejob/test/dummy/config/initializers/activejob.rb deleted file mode 100644 index 5fcde86c96..0000000000 --- a/activejob/test/dummy/config/initializers/activejob.rb +++ /dev/null @@ -1,65 +0,0 @@ -case ENV['AJADAPTER'] -when "delayed_job" - ActiveJob::Base.queue_adapter = :delayed_job -when "sidekiq" - ActiveJob::Base.queue_adapter = :sidekiq -when "resque" - ActiveJob::Base.queue_adapter = :resque - Resque.redis = Redis::Namespace.new 'active_jobs_int_test', redis: Redis.connect(url: "tcp://127.0.0.1:6379/12", :thread_safe => true) - Resque.logger = Rails.logger -when 'qu' - ActiveJob::Base.queue_adapter = :qu - ENV['REDISTOGO_URL'] = "tcp://127.0.0.1:6379/12" - backend = Qu::Backend::Redis.new - backend.namespace = "active_jobs_int_test" - Qu.backend = backend - Qu.logger = Rails.logger - Qu.interval = 0.5 -when 'que' - ActiveJob::Base.queue_adapter = :que - QUE_URL = ENV['QUE_DATABASE_URL'] || 'postgres://localhost/active_jobs_que_int_test' - uri = URI.parse(QUE_URL) - user = uri.user||ENV['USER'] - pass = uri.password - db = uri.path[1..-1] - %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'drop database "#{db}"' -U #{user} -t template1} - %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'create database "#{db}"' -U #{user} -t template1} - Que.connection = Sequel.connect(QUE_URL) - Que.migrate! - Que.mode = :off - Que.worker_count = 1 -when 'queue_classic' - ENV['QC_DATABASE_URL'] ||= 'postgres://localhost/active_jobs_qc_int_test' - ENV['QC_LISTEN_TIME'] = "0.5" - ActiveJob::Base.queue_adapter = :queue_classic - uri = URI.parse(ENV['QC_DATABASE_URL']) - user = uri.user||ENV['USER'] - pass = uri.password - db = uri.path[1..-1] - %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'drop database "#{db}"' -U #{user} -t template1} - %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'create database "#{db}"' -U #{user} -t template1} - QC::Setup.create -when 'sidekiq' - ActiveJob::Base.queue_adapter = :sidekiq -when 'sneakers' - ActiveJob::Base.queue_adapter = :sneakers - Sneakers.configure :heartbeat => 2, - :amqp => 'amqp://guest:guest@localhost:5672', - :vhost => '/', - :exchange => 'active_jobs_sneakers_int_test', - :exchange_type => :direct, - :daemonize => true, - :threads => 1, - :workers => 1, - :pid_path => Rails.root.join("tmp/sneakers.pid").to_s, - :log => Rails.root.join("log/sneakers.log").to_s -when 'sucker_punch' - ActiveJob::Base.queue_adapter = :sucker_punch -when 'backburner' - ActiveJob::Base.queue_adapter = :backburner - Backburner.configure do |config| - config.logger = Rails.logger - end -else - ActiveJob::Base.queue_adapter = nil -end diff --git a/activejob/test/dummy/config/initializers/session_store.rb b/activejob/test/dummy/config/initializers/session_store.rb deleted file mode 100644 index 70a5a506a9..0000000000 --- a/activejob/test/dummy/config/initializers/session_store.rb +++ /dev/null @@ -1 +0,0 @@ -Rails.application.config.session_store :cookie_store, key: '_dummy_session' diff --git a/activejob/test/dummy/config/routes.rb b/activejob/test/dummy/config/routes.rb deleted file mode 100644 index 1daf9a4121..0000000000 --- a/activejob/test/dummy/config/routes.rb +++ /dev/null @@ -1,2 +0,0 @@ -Rails.application.routes.draw do -end diff --git a/activejob/test/dummy/config/secrets.yml b/activejob/test/dummy/config/secrets.yml deleted file mode 100644 index 7dfacb38ea..0000000000 --- a/activejob/test/dummy/config/secrets.yml +++ /dev/null @@ -1,2 +0,0 @@ -test: - secret_key_base: b83ee5aeada663bc4270a1817d0ca43b2784017cc77dc8afcd60967cc968d4ce30caff9eb682766129e18a4048c4d5ebf14eabf463fc37ad67c18934f4345545 diff --git a/activejob/test/dummy/db/migrate/20140804200445_create_delayed_jobs.rb b/activejob/test/dummy/db/migrate/20140804200445_create_delayed_jobs.rb deleted file mode 100644 index ec0dd93ce1..0000000000 --- a/activejob/test/dummy/db/migrate/20140804200445_create_delayed_jobs.rb +++ /dev/null @@ -1,22 +0,0 @@ -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/activejob/test/dummy/db/schema.rb b/activejob/test/dummy/db/schema.rb deleted file mode 100644 index 012a099f9c..0000000000 --- a/activejob/test/dummy/db/schema.rb +++ /dev/null @@ -1,32 +0,0 @@ -# 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 -# you'll amass, the slower it'll run and the greater likelihood for issues). -# -# It's strongly recommended that you check this file into your version control system. - -ActiveRecord::Schema.define(version: 20140804200445) do - - create_table "delayed_jobs", force: true do |t| - t.integer "priority", default: 0, null: false - t.integer "attempts", default: 0, null: false - t.text "handler", null: false - t.text "last_error" - t.datetime "run_at" - t.datetime "locked_at" - t.datetime "failed_at" - t.string "locked_by" - t.string "queue" - t.datetime "created_at" - t.datetime "updated_at" - end - - add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority" - -end diff --git a/activejob/test/dummy/db/test.sqlite3 b/activejob/test/dummy/db/test.sqlite3 deleted file mode 100644 index 671e20102e..0000000000 Binary files a/activejob/test/dummy/db/test.sqlite3 and /dev/null differ diff --git a/activejob/test/dummy/lib/assets/.keep b/activejob/test/dummy/lib/assets/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/activejob/test/dummy/log/.keep b/activejob/test/dummy/log/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/activejob/test/dummy/tmp/.keep b/activejob/test/dummy/tmp/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb index 104dabd861..ad263c05cd 100644 --- a/activejob/test/helper.rb +++ b/activejob/test/helper.rb @@ -6,13 +6,8 @@ $LOAD_PATH << File.dirname(__FILE__) + "/../lib" require 'active_job' adapter = ENV['AJADAPTER'] || 'inline' -puts "Testing#{" integration" if ENV['AJ_INTEGRATION_TESTS']} using #{adapter}" -if ENV['AJ_INTEGRATION_TESTS'] - require 'support/integration/helper' -else - require "adapters/#{adapter}" -end +require "adapters/#{adapter}" require 'active_support/testing/autorun' diff --git a/activejob/test/integration/queuing_test.rb b/activejob/test/integration/queuing_test.rb deleted file mode 100644 index bf6137af4c..0000000000 --- a/activejob/test/integration/queuing_test.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'helper' -require 'jobs/logging_job' -require 'active_support/core_ext/numeric/time' - - -class QueuingTest < ActiveSupport::TestCase - setup do - - end - - test 'run queued job' do - id = "AJ-#{SecureRandom.uuid}" - TestJob.enqueue id - sleep 2 - assert Dummy::Application.root.join("tmp/#{id}").exist? - end - -end diff --git a/activejob/test/support/integration/adapters/backburner.rb b/activejob/test/support/integration/adapters/backburner.rb deleted file mode 100644 index 7271d50a45..0000000000 --- a/activejob/test/support/integration/adapters/backburner.rb +++ /dev/null @@ -1,15 +0,0 @@ -module BackburnerJobsManager - def clear_jobs - Backburner::Worker.connection.tubes.all.map &:clear - end - - def start_workers - @thread = Thread.new { Backburner.work "active-jobs-default" } - end - - def stop_workers - @thread.kill - end - -end - diff --git a/activejob/test/support/integration/adapters/delayed_job.rb b/activejob/test/support/integration/adapters/delayed_job.rb deleted file mode 100644 index 3e55933438..0000000000 --- a/activejob/test/support/integration/adapters/delayed_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -module DelayedJobJobsManager - def clear_jobs - Delayed::Job.delete_all - end - - def start_workers - @worker = Delayed::Worker.new(quiet: false, sleep_delay: 0.5) - @thread = Thread.new { @worker.start } - end - - def stop_workers - @worker.stop - end -end diff --git a/activejob/test/support/integration/adapters/qu.rb b/activejob/test/support/integration/adapters/qu.rb deleted file mode 100644 index 12d063ea0d..0000000000 --- a/activejob/test/support/integration/adapters/qu.rb +++ /dev/null @@ -1,14 +0,0 @@ -module QuJobsManager - def clear_jobs - Qu.clear "active_jobs_default" - end - - def start_workers - @thread = Thread.new { Qu::Worker.new("active_jobs_default").start } - end - - def stop_workers - @thread.kill - end -end - diff --git a/activejob/test/support/integration/adapters/que.rb b/activejob/test/support/integration/adapters/que.rb deleted file mode 100644 index f15c9af910..0000000000 --- a/activejob/test/support/integration/adapters/que.rb +++ /dev/null @@ -1,19 +0,0 @@ -module QueJobsManager - def clear_jobs - Que.clear! - end - - def start_workers - @thread = Thread.new do - loop do - Que::Job.work("active_jobs_default") - sleep 0.5 - end - end - end - - def stop_workers - @thread.kill - end -end - diff --git a/activejob/test/support/integration/adapters/queue_classic.rb b/activejob/test/support/integration/adapters/queue_classic.rb deleted file mode 100644 index 3b24eca5b9..0000000000 --- a/activejob/test/support/integration/adapters/queue_classic.rb +++ /dev/null @@ -1,21 +0,0 @@ -module QC; WAIT_TIME = 0.5; end - -module QueueClassicJobsManager - def clear_jobs - # disabling this as it locks - # QC::Queue.new("active_jobs_default").delete_all - end - - def start_workers - @pid = fork do - QC::Conn.connection = QC::Conn.connect - worker = QC::Worker.new(q_name: 'active_jobs_default') - worker.start - end - end - - def stop_workers - Process.kill 'HUP', @pid - end -end - diff --git a/activejob/test/support/integration/adapters/resque.rb b/activejob/test/support/integration/adapters/resque.rb deleted file mode 100644 index b9811bd3e1..0000000000 --- a/activejob/test/support/integration/adapters/resque.rb +++ /dev/null @@ -1,18 +0,0 @@ -module ResqueJobsManager - def clear_jobs - Resque.queues.each { |queue_name| Resque.redis.del "queue:#{queue_name}" } - Resque.redis.keys("delayed:*").each { |key| Resque.redis.del "#{key}" } - Resque.redis.del "delayed_queue_schedule" - end - - def start_workers - @thread = Thread.new do - Resque::Worker.new("*").work(0.5) - end - end - - def stop_workers - @thread.kill - end -end - diff --git a/activejob/test/support/integration/adapters/sidekiq.rb b/activejob/test/support/integration/adapters/sidekiq.rb deleted file mode 100644 index d3b7d15614..0000000000 --- a/activejob/test/support/integration/adapters/sidekiq.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'sidekiq/launcher' -require 'sidekiq/api' - -module SidekiqJobsManager - def clear_jobs - Sidekiq::Queue.new("active_jobs_default").clear - end - - def start_workers - options = {:queues=>["active_jobs_default"], :concurrency=>1, :environment=>"test", :timeout=>8, :daemon=>true, :strict=>true} - @launcher = Sidekiq::Launcher.new(options) - @launcher.run - end - - def stop_workers - @launcher.stop - end -end - diff --git a/activejob/test/support/integration/adapters/sneakers.rb b/activejob/test/support/integration/adapters/sneakers.rb deleted file mode 100644 index 5dcab68515..0000000000 --- a/activejob/test/support/integration/adapters/sneakers.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'sneakers/runner' - -module SneakersJobsManager - def clear_jobs - end - - def start_workers - cmd = %{cd #{Rails.root.to_s} && (RAILS_ENV=test AJADAPTER=sneakers WORKERS=ActiveJob::QueueAdapters::SneakersAdapter::JobWrapper bundle exec rake --trace sneakers:run)} - `#{cmd}` - while !Rails.root.join("tmp/sneakers.pid").exist? do - sleep 0.5 - end - end - - def stop_workers - Process.kill 'TERM', File.open(Rails.root.join("tmp/sneakers.pid").to_s).read.to_i - end -end diff --git a/activejob/test/support/integration/adapters/sucker_punch.rb b/activejob/test/support/integration/adapters/sucker_punch.rb deleted file mode 100644 index 317f9c80fd..0000000000 --- a/activejob/test/support/integration/adapters/sucker_punch.rb +++ /dev/null @@ -1,5 +0,0 @@ -module SuckerPunchJobsManager - def clear_jobs - end -end - diff --git a/activejob/test/support/integration/helper.rb b/activejob/test/support/integration/helper.rb deleted file mode 100644 index cb94e7cfb5..0000000000 --- a/activejob/test/support/integration/helper.rb +++ /dev/null @@ -1,12 +0,0 @@ -ENV["RAILS_ENV"] = "test" -require File.expand_path("../../../dummy/config/environment.rb", __FILE__) -require "rails/test_help" -Rails.backtrace_cleaner.remove_silencers! - -require_relative 'test_case_helpers' -ActiveSupport::TestCase.send(:include, TestCaseHelpers) - -JobsManager.current_manager.setup -JobsManager.current_manager.start_workers -Minitest.after_run { JobsManager.current_manager.stop_workers } - diff --git a/activejob/test/support/integration/jobs_manager.rb b/activejob/test/support/integration/jobs_manager.rb deleted file mode 100644 index 1da74193b1..0000000000 --- a/activejob/test/support/integration/jobs_manager.rb +++ /dev/null @@ -1,23 +0,0 @@ -class JobsManager - @@managers = {} - attr :adapter_name - - def self.current_manager - @@managers[ENV['AJADAPTER']] ||= new(ENV['AJADAPTER']) - end - - def initialize(adapter_name) - @adapter_name = adapter_name - require_relative "adapters/#{adapter_name}" - extend "#{adapter_name.camelize}JobsManager".constantize - end - - def setup - end - - def start_workers - end - - def stop_workers - end -end diff --git a/activejob/test/support/integration/test_case_helpers.rb b/activejob/test/support/integration/test_case_helpers.rb deleted file mode 100644 index 9a5eea0783..0000000000 --- a/activejob/test/support/integration/test_case_helpers.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'active_support/concern' -require 'support/integration/jobs_manager' - -module TestCaseHelpers - extend ActiveSupport::Concern - - included do - self.use_transactional_fixtures = false - - setup do - clear_jobs - end - - teardown do - clear_jobs - FileUtils.rm_rf Dir[Dummy::Application.root.join("tmp/AJ-*")] - end - end - - protected - - def jobs_manager - JobsManager.current_manager - end - - def clear_jobs - jobs_manager.clear_jobs - end - -end diff --git a/rails.gemspec b/rails.gemspec index 4800df0df4..b1a7d16722 100644 --- a/rails.gemspec +++ b/rails.gemspec @@ -24,6 +24,7 @@ Gem::Specification.new do |s| s.add_dependency 'activemodel', version s.add_dependency 'activerecord', version s.add_dependency 'actionmailer', version + s.add_dependency 'activejob', version s.add_dependency 'railties', version s.add_dependency 'bundler', '>= 1.3.0', '< 2.0' diff --git a/railties/lib/rails/all.rb b/railties/lib/rails/all.rb index 2e83c0fe14..45361fca83 100644 --- a/railties/lib/rails/all.rb +++ b/railties/lib/rails/all.rb @@ -5,6 +5,7 @@ require "rails" action_controller action_view action_mailer + active_job rails/test_unit sprockets ).each do |framework| diff --git a/tasks/release.rb b/tasks/release.rb index 767feaf236..de05dfad99 100644 --- a/tasks/release.rb +++ b/tasks/release.rb @@ -1,4 +1,4 @@ -FRAMEWORKS = %w( activesupport activemodel activerecord actionview actionpack actionmailer railties ) +FRAMEWORKS = %w( activesupport activemodel activerecord actionview actionpack actionmailer railties activejob ) root = File.expand_path('../../', __FILE__) version = File.read("#{root}/RAILS_VERSION").strip -- cgit v1.2.3 From 3ed69cd5f54c6eda7ee8664aa14c53205ecbb6c5 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Tue, 12 Aug 2014 13:53:46 +0300 Subject: Fixed failing tests; Load active_job in railtie; Renamed generator to job --- activejob/lib/active_job/railtie.rb | 5 +++-- .../rails/generators/active_job/job_generator.rb | 22 ---------------------- .../rails/generators/active_job/templates/job.rb | 9 --------- .../lib/rails/generators/job/job_generator.rb | 22 ++++++++++++++++++++++ .../lib/rails/generators/job/templates/job.rb | 9 +++++++++ activejob/test/cases/logging_test.rb | 1 + activejob/test/cases/queue_naming_test.rb | 4 +++- 7 files changed, 38 insertions(+), 34 deletions(-) delete mode 100644 activejob/lib/rails/generators/active_job/job_generator.rb delete mode 100644 activejob/lib/rails/generators/active_job/templates/job.rb create mode 100644 activejob/lib/rails/generators/job/job_generator.rb create mode 100644 activejob/lib/rails/generators/job/templates/job.rb diff --git a/activejob/lib/active_job/railtie.rb b/activejob/lib/active_job/railtie.rb index 08555d1d77..16736374af 100644 --- a/activejob/lib/active_job/railtie.rb +++ b/activejob/lib/active_job/railtie.rb @@ -1,4 +1,5 @@ -require 'active_model/railtie' +require 'active_job' +require 'rails' module ActiveJob # = Active Job Railtie @@ -7,4 +8,4 @@ module ActiveJob ActiveSupport.on_load(:active_job) { self.logger = ::Rails.logger } end end -end \ No newline at end of file +end diff --git a/activejob/lib/rails/generators/active_job/job_generator.rb b/activejob/lib/rails/generators/active_job/job_generator.rb deleted file mode 100644 index fe4af0d2cc..0000000000 --- a/activejob/lib/rails/generators/active_job/job_generator.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'rails/generators/named_base' - -module ActiveJob - module Generators # :nodoc: - class JobGenerator < Rails::Generators::NamedBase # :nodoc: - desc 'This generator creates an active job file at app/jobs' - - class_option :queue, type: :string, default: 'default', desc: 'The queue name for the generated job' - - def self.default_generator_root - File.dirname(__FILE__) - end - - check_class_collision suffix: 'Job' - - def create_job_file - template 'job.rb', File.join('app/jobs', class_path, "#{file_name}_job.rb") - end - - end - end -end \ No newline at end of file diff --git a/activejob/lib/rails/generators/active_job/templates/job.rb b/activejob/lib/rails/generators/active_job/templates/job.rb deleted file mode 100644 index 6a21616d30..0000000000 --- a/activejob/lib/rails/generators/active_job/templates/job.rb +++ /dev/null @@ -1,9 +0,0 @@ -<% module_namespacing do -%> -class <%= class_name %>Job < ActiveJob::Base - queue_as :<%= options[:queue] %> - - def perform - # Do something later - end -end -<% end -%> \ No newline at end of file diff --git a/activejob/lib/rails/generators/job/job_generator.rb b/activejob/lib/rails/generators/job/job_generator.rb new file mode 100644 index 0000000000..78a9c27606 --- /dev/null +++ b/activejob/lib/rails/generators/job/job_generator.rb @@ -0,0 +1,22 @@ +require 'rails/generators/named_base' + +module Rails + module Generators # :nodoc: + class JobGenerator < Rails::Generators::NamedBase # :nodoc: + desc 'This generator creates an active job file at app/jobs' + + class_option :queue, type: :string, default: 'default', desc: 'The queue name for the generated job' + + def self.default_generator_root + File.dirname(__FILE__) + end + + check_class_collision suffix: 'Job' + + def create_job_file + template 'job.rb', File.join('app/jobs', class_path, "#{file_name}_job.rb") + end + + end + end +end diff --git a/activejob/lib/rails/generators/job/templates/job.rb b/activejob/lib/rails/generators/job/templates/job.rb new file mode 100644 index 0000000000..6a21616d30 --- /dev/null +++ b/activejob/lib/rails/generators/job/templates/job.rb @@ -0,0 +1,9 @@ +<% module_namespacing do -%> +class <%= class_name %>Job < ActiveJob::Base + queue_as :<%= options[:queue] %> + + def perform + # Do something later + end +end +<% end -%> \ No newline at end of file diff --git a/activejob/test/cases/logging_test.rb b/activejob/test/cases/logging_test.rb index a4c010c7bd..3f21fa644c 100644 --- a/activejob/test/cases/logging_test.rb +++ b/activejob/test/cases/logging_test.rb @@ -1,5 +1,6 @@ require 'helper' require "active_support/log_subscriber/test_helper" +require 'active_support/core_ext/numeric/time' require 'jobs/hello_job' require 'jobs/logging_job' require 'jobs/nested_job' diff --git a/activejob/test/cases/queue_naming_test.rb b/activejob/test/cases/queue_naming_test.rb index 852643b9f6..7cb516bbb4 100644 --- a/activejob/test/cases/queue_naming_test.rb +++ b/activejob/test/cases/queue_naming_test.rb @@ -1,11 +1,13 @@ require 'helper' require 'jobs/hello_job' +require 'jobs/logging_job' +require 'jobs/nested_job' class QueueNamingTest < ActiveSupport::TestCase test 'name derived from base' do assert_equal "active_jobs", HelloJob.queue_name end - + test 'name appended in job' do begin HelloJob.queue_as :greetings -- cgit v1.2.3 From 2e2d9f9d4635d9a6eadf192c96de99c65c04388c Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Tue, 12 Aug 2014 15:37:18 +0300 Subject: Added activejob to install.rb --- install.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.rb b/install.rb index bff8fee934..96e4153165 100644 --- a/install.rb +++ b/install.rb @@ -5,7 +5,7 @@ if version.nil? exit(64) end -%w( activesupport activemodel activerecord actionpack actionview actionmailer railties ).each do |framework| +%w( activesupport activemodel activerecord actionpack actionview actionmailer railties activejob ).each do |framework| puts "Installing #{framework}..." `cd #{framework} && gem build #{framework}.gemspec && gem install #{framework}-#{version}.gem --no-ri --no-rdoc && rm #{framework}-#{version}.gem` end -- cgit v1.2.3 From 7c84dfb510a573fbe80a9de7064b59c381987fbb Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Tue, 12 Aug 2014 15:43:43 +0300 Subject: Update activejob's README --- activejob/README.md | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/activejob/README.md b/activejob/README.md index 5adb41b0b6..ebb7876c81 100644 --- a/activejob/README.md +++ b/activejob/README.md @@ -5,7 +5,7 @@ of queueing backends. These jobs can be everything from regularly scheduled clean-ups, billing charges, or mailings. Anything that can be chopped up into small units of work and run in parallel, really. -It also serves as the backend for [ActionMailer's #deliver_later functionality](https://github.com/rails/activejob/issues/13) +It also serves as the backend for ActionMailer's #deliver_later functionality that makes it easy to turn any mailing into a job for running later. That's one of the most common jobs in a modern web application: Sending emails outside of the request-response cycle, so the user doesn't have to wait on it. @@ -105,16 +105,37 @@ We currently have adapters for: * [activejob-stats](https://github.com/seuros/activejob-stats) -## Under development as a gem, targeted for Rails inclusion +## Download and installation -Active Job is currently being developed in a separate repository until it's -ready to be merged in with Rails. The current plan is to have Active Job -be part of the Rails 4.2 release, but plans may change depending on when -this framework stabilizes and feels ready. +The latest version of Active Job can be installed with RubyGems: +``` + % [sudo] gem install activejob +``` + +Source code can be downloaded as part of the Rails project on GitHub + +* https://github.com/rails/rails/tree/master/activejob ## License -Active Job is released under the MIT license: +ActiveJob is released under the MIT license: * http://www.opensource.org/licenses/MIT + + +## Support + +API documentation is at + +* http://api.rubyonrails.org + +Bug reports can be filed for the Ruby on Rails project here: + +* https://github.com/rails/rails/issues + +Feature requests should be discussed on the rails-core mailing list here: + +* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core + + -- cgit v1.2.3 From 22ee993825897fe2adafebeed57158f0699b071b Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Tue, 12 Aug 2014 15:57:57 +0300 Subject: Modified activejob.gemspec to match other rails gems format --- activejob/activejob.gemspec | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/activejob/activejob.gemspec b/activejob/activejob.gemspec index 23fd25c6c8..fd551e540e 100644 --- a/activejob/activejob.gemspec +++ b/activejob/activejob.gemspec @@ -1,8 +1,10 @@ +version = File.read(File.expand_path('../../RAILS_VERSION', __FILE__)).strip + Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY s.name = 'activejob' - s.version = '4.2.0.alpha' - s.summary = 'Job framework with pluggable queues (will be part of Rails).' + s.version = version + s.summary = 'Job framework with pluggable queues.' s.description = 'Declare job classes that can be run by a variety of queueing backends.' s.required_ruby_version = '>= 1.9.3' @@ -13,9 +15,9 @@ Gem::Specification.new do |s| s.email = 'david@loudthinking.com' s.homepage = 'http://www.rubyonrails.org' - s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.rdoc', 'lib/**/*'] + s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.md', 'lib/**/*'] s.require_path = 'lib' - s.add_dependency 'activesupport', '>= 4.1.0' + s.add_dependency 'activesupport', version s.add_dependency 'activemodel-globalid' end -- cgit v1.2.3 From 29be3f5d8386fc9a8a67844fa9b7d6860574e715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Tue, 12 Aug 2014 21:57:51 +0200 Subject: Add config option for cookies digest You can now configure custom digest for cookies in the same way as `serializer`: config.action_dispatch.cookies_digest = 'SHA256' --- actionpack/CHANGELOG.md | 5 +++ .../lib/action_dispatch/middleware/cookies.rb | 12 ++++-- actionpack/test/dispatch/cookies_test.rb | 50 ++++++++++++++++++++++ .../lib/active_support/message_encryptor.rb | 3 +- railties/lib/rails/application.rb | 3 +- 5 files changed, 68 insertions(+), 5 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 44b8fa843d..253b647aca 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,8 @@ +* Add `config.action_dispatch.cookies_digest` option for setting custom + digest. The default remains the same - 'SHA1'. + + *Łukasz Strzałkowski* + * Extract source code for the entire exception stack trace for better debugging and diagnosis. diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index ac9e5effe2..0f088c5539 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -90,6 +90,7 @@ module ActionDispatch SECRET_TOKEN = "action_dispatch.secret_token".freeze SECRET_KEY_BASE = "action_dispatch.secret_key_base".freeze COOKIES_SERIALIZER = "action_dispatch.cookies_serializer".freeze + COOKIES_DIGEST = "action_dispatch.cookies_digest".freeze # Cookies can typically store 4096 bytes. MAX_COOKIE_SIZE = 4096 @@ -212,7 +213,8 @@ module ActionDispatch secret_token: env[SECRET_TOKEN], secret_key_base: env[SECRET_KEY_BASE], upgrade_legacy_signed_cookies: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present?, - serializer: env[COOKIES_SERIALIZER] + serializer: env[COOKIES_SERIALIZER], + digest: env[COOKIES_DIGEST] } end @@ -441,6 +443,10 @@ module ActionDispatch serializer end end + + def digest + @options[:digest] || 'SHA1' + end end class SignedCookieJar #:nodoc: @@ -451,7 +457,7 @@ module ActionDispatch @parent_jar = parent_jar @options = options secret = key_generator.generate_key(@options[:signed_cookie_salt]) - @verifier = ActiveSupport::MessageVerifier.new(secret, serializer: NullSerializer) + @verifier = ActiveSupport::MessageVerifier.new(secret, digest: digest, serializer: NullSerializer) end def [](name) @@ -508,7 +514,7 @@ module ActionDispatch @options = options secret = key_generator.generate_key(@options[:encrypted_cookie_salt]) sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt]) - @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: NullSerializer) + @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: NullSerializer) end def [](name) diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 0f145666d1..b40c21c067 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -369,6 +369,35 @@ class CookiesTest < ActionController::TestCase assert_equal 'Jamie', @controller.send(:cookies).permanent[:user_name] end + def test_signed_cookie_using_default_digest + get :set_signed_cookie + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"] + secret = key_generator.generate_key(signed_cookie_salt) + + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal, digest: 'SHA1') + assert_equal verifier.generate(45), cookies[:user_id] + end + + def test_signed_cookie_using_custom_digest + @request.env["action_dispatch.cookies_digest"] = 'SHA256' + get :set_signed_cookie + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"] + secret = key_generator.generate_key(signed_cookie_salt) + + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal, digest: 'SHA256') + assert_equal verifier.generate(45), cookies[:user_id] + end + def test_signed_cookie_using_default_serializer get :set_signed_cookie cookies = @controller.send :cookies @@ -481,6 +510,27 @@ class CookiesTest < ActionController::TestCase assert_equal 'bar was dumped and loaded', cookies.encrypted[:foo] end + def test_encrypted_cookie_using_custom_digest + @request.env["action_dispatch.cookies_digest"] = 'SHA256' + get :set_encrypted_cookie + cookies = @controller.send :cookies + assert_not_equal 'bar', cookies[:foo] + assert_equal 'bar', cookies.encrypted[:foo] + + sign_secret = @request.env["action_dispatch.key_generator"].generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"]) + + sha1_verifier = ActiveSupport::MessageVerifier.new(sign_secret, serializer: ActionDispatch::Cookies::NullSerializer, digest: 'SHA1') + sha256_verifier = ActiveSupport::MessageVerifier.new(sign_secret, serializer: ActionDispatch::Cookies::NullSerializer, digest: 'SHA256') + + assert_raises(ActiveSupport::MessageVerifier::InvalidSignature) do + sha1_verifier.verify(cookies[:foo]) + end + + assert_nothing_raised do + sha256_verifier.verify(cookies[:foo]) + end + end + def test_encrypted_cookie_using_hybrid_serializer_can_migrate_marshal_dumped_value_to_json @request.env["action_dispatch.cookies_serializer"] = :hybrid diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index b019ad0dec..92ab6fe648 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -40,6 +40,7 @@ module ActiveSupport # Options: # * :cipher - Cipher to use. Can be any cipher returned by # OpenSSL::Cipher.ciphers. Default is 'aes-256-cbc'. + # * :digest - String of digest to use for signing. Default is +SHA1+. # * :serializer - Object serializer to use. Default is +Marshal+. def initialize(secret, *signature_key_or_options) options = signature_key_or_options.extract_options! @@ -47,7 +48,7 @@ module ActiveSupport @secret = secret @sign_secret = sign_secret @cipher = options[:cipher] || 'aes-256-cbc' - @verifier = MessageVerifier.new(@sign_secret || @secret, :serializer => NullSerializer) + @verifier = MessageVerifier.new(@sign_secret || @secret, digest: options[:digest] || 'SHA1', serializer: NullSerializer) @serializer = options[:serializer] || Marshal end diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 61639be7c6..6a4660bad0 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -256,7 +256,8 @@ module Rails "action_dispatch.signed_cookie_salt" => config.action_dispatch.signed_cookie_salt, "action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt, "action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt, - "action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer + "action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer, + "action_dispatch.cookies_digest" => config.action_dispatch.cookies_digest }) end end -- cgit v1.2.3 From ea3ba34506c72d636096245016b5ef9cfe27c566 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 12 Aug 2014 14:23:14 -0600 Subject: Change the default `null` value for timestamps As per discussion, this changes the model generators to specify `null: false` for timestamp columns. A warning is now emitted if `timestamps` is called without a `null` option specified, so we can safely change the behavior when no option is specified in Rails 5. --- .../abstract/schema_definitions.rb | 24 +++++++-- .../abstract/schema_statements.rb | 6 +-- .../connection_adapters/abstract_mysql_adapter.rb | 4 +- .../migration/templates/create_table_migration.rb | 2 +- .../cases/adapters/mysql/active_schema_test.rb | 2 +- .../cases/adapters/mysql2/active_schema_test.rb | 2 +- .../cases/adapters/postgresql/timestamp_test.rb | 8 +-- activerecord/test/cases/ar_schema_test.rb | 57 ++++++++++++++++++++++ .../test/cases/migration/change_schema_test.rb | 7 ++- .../test/cases/migration/change_table_test.rb | 4 +- activerecord/test/cases/migration/helper.rb | 2 +- activerecord/test/cases/migration_test.rb | 2 +- activerecord/test/schema/schema.rb | 10 ++-- railties/test/generators/model_generator_test.rb | 2 +- 14 files changed, 105 insertions(+), 27 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index e44ccb7d81..9e07e9a5c4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -56,6 +56,19 @@ module ActiveRecord end end + module TimestampDefaultDeprecation # :nodoc: + def emit_warning_if_null_unspecified(options) + return if options.key?(:null) + + ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc) + `timestamp` was called without specifying an option for `null`. In Rails + 5.0, this behavior will change to `null: false`. You should manually + specify `null: true` to prevent the behavior of your existing migrations + from changing. + MESSAGE + end + end + # Represents the schema of an SQL table in an abstract way. This class # provides methods for manipulating the schema representation. # @@ -77,6 +90,8 @@ module ActiveRecord # The table definitions # The Columns are stored as a ColumnDefinition in the +columns+ attribute. class TableDefinition + include TimestampDefaultDeprecation + # An array of ColumnDefinition objects, representing the column changes # that have been defined. attr_accessor :indexes @@ -276,6 +291,7 @@ module ActiveRecord # :updated_at to the table. def timestamps(*args) options = args.extract_options! + emit_warning_if_null_unspecified(options) column(:created_at, :datetime, options) column(:updated_at, :datetime, options) end @@ -405,6 +421,8 @@ module ActiveRecord # end # class Table + include TimestampDefaultDeprecation + def initialize(table_name, base) @table_name = table_name @base = base @@ -452,8 +470,9 @@ module ActiveRecord # Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps # # t.timestamps - def timestamps - @base.add_timestamps(@table_name) + def timestamps(options = {}) + emit_warning_if_null_unspecified(options) + @base.add_timestamps(@table_name, options) end # Changes the column's definition according to the new options. @@ -559,6 +578,5 @@ module ActiveRecord @base.native_database_types end end - end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 10753defc2..84e6a27cd1 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -838,9 +838,9 @@ module ActiveRecord # # add_timestamps(:suppliers) # - def add_timestamps(table_name) - add_column table_name, :created_at, :datetime - add_column table_name, :updated_at, :datetime + def add_timestamps(table_name, options = {}) + add_column table_name, :created_at, :datetime, options + add_column table_name, :updated_at, :datetime, options end # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition. diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index e5417a9556..a1c370b05d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -764,8 +764,8 @@ module ActiveRecord "DROP INDEX #{index_name}" end - def add_timestamps_sql(table_name) - [add_column_sql(table_name, :created_at, :datetime), add_column_sql(table_name, :updated_at, :datetime)] + def add_timestamps_sql(table_name, options = {}) + [add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)] end def remove_timestamps_sql(table_name) diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb index fd94a2d038..f7bf6987c4 100644 --- a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +++ b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb @@ -9,7 +9,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration <% end -%> <% end -%> <% if options[:timestamps] %> - t.timestamps + t.timestamps null: false <% end -%> end <% attributes_with_index.each do |attribute| -%> diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb index 7c0f11b033..a84673e452 100644 --- a/activerecord/test/cases/adapters/mysql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb @@ -105,7 +105,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase with_real_execute do begin ActiveRecord::Base.connection.create_table :delete_me do |t| - t.timestamps + t.timestamps null: true end ActiveRecord::Base.connection.remove_timestamps :delete_me assert !column_present?('delete_me', 'updated_at', 'datetime') diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb index cefc3e3c7e..49cfafd7a5 100644 --- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb @@ -105,7 +105,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase with_real_execute do begin ActiveRecord::Base.connection.create_table :delete_me do |t| - t.timestamps + t.timestamps null: true end ActiveRecord::Base.connection.remove_timestamps :delete_me assert !column_present?('delete_me', 'updated_at', 'datetime') diff --git a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb index 3614b29190..eb32c4d2c2 100644 --- a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb +++ b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb @@ -87,7 +87,7 @@ class TimestampTest < ActiveRecord::TestCase def test_timestamps_helper_with_custom_precision ActiveRecord::Base.connection.create_table(:foos) do |t| - t.timestamps :precision => 4 + t.timestamps :null => true, :precision => 4 end assert_equal 4, activerecord_column_option('foos', 'created_at', 'precision') assert_equal 4, activerecord_column_option('foos', 'updated_at', 'precision') @@ -95,7 +95,7 @@ class TimestampTest < ActiveRecord::TestCase def test_passing_precision_to_timestamp_does_not_set_limit ActiveRecord::Base.connection.create_table(:foos) do |t| - t.timestamps :precision => 4 + t.timestamps :null => true, :precision => 4 end assert_nil activerecord_column_option("foos", "created_at", "limit") assert_nil activerecord_column_option("foos", "updated_at", "limit") @@ -104,14 +104,14 @@ class TimestampTest < ActiveRecord::TestCase def test_invalid_timestamp_precision_raises_error assert_raises ActiveRecord::ActiveRecordError do ActiveRecord::Base.connection.create_table(:foos) do |t| - t.timestamps :precision => 7 + t.timestamps :null => true, :precision => 7 end end end def test_postgres_agrees_with_activerecord_about_precision ActiveRecord::Base.connection.create_table(:foos) do |t| - t.timestamps :precision => 4 + t.timestamps :null => true, :precision => 4 end assert_equal '4', pg_datetime_precision('foos', 'created_at') assert_equal '4', pg_datetime_precision('foos', 'updated_at') diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index 8700b20dee..3f5858714a 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -14,6 +14,7 @@ if ActiveRecord::Base.connection.supports_migrations? @connection.drop_table :fruits rescue nil @connection.drop_table :nep_fruits rescue nil @connection.drop_table :nep_schema_migrations rescue nil + @connection.drop_table :has_timestamps rescue nil ActiveRecord::SchemaMigration.delete_all rescue nil end @@ -88,5 +89,61 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal "017", ActiveRecord::SchemaMigration.normalize_migration_number("0017") assert_equal "20131219224947", ActiveRecord::SchemaMigration.normalize_migration_number("20131219224947") end + + def test_timestamps_without_null_is_deprecated_on_create_table + assert_deprecated do + ActiveRecord::Schema.define do + create_table :has_timestamps do |t| + t.timestamps + end + end + end + end + + def test_timestamps_without_null_is_deprecated_on_change_table + assert_deprecated do + ActiveRecord::Schema.define do + create_table :has_timestamps + + change_table :has_timestamps do |t| + t.timestamps + end + end + end + end + + def test_no_deprecation_warning_from_timestamps_on_create_table + assert_not_deprecated do + ActiveRecord::Schema.define do + create_table :has_timestamps do |t| + t.timestamps null: true + end + + drop_table :has_timestamps + + create_table :has_timestamps do |t| + t.timestamps null: false + end + end + end + end + + def test_no_deprecation_warning_from_timestamps_on_change_table + assert_not_deprecated do + ActiveRecord::Schema.define do + create_table :has_timestamps + change_table :has_timestamps do |t| + t.timestamps null: true + end + + drop_table :has_timestamps + + create_table :has_timestamps + change_table :has_timestamps do |t| + t.timestamps null: false, default: Time.now + end + end + end + end end end diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index c66eaf1ee1..bd3dd29f4d 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -176,8 +176,11 @@ module ActiveRecord end def test_create_table_with_timestamps_should_create_datetime_columns - connection.create_table table_name do |t| - t.timestamps + # FIXME: Remove the silence when we change the default `null` behavior + ActiveSupport::Deprecation.silence do + connection.create_table table_name do |t| + t.timestamps + end end created_columns = connection.columns(table_name) diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb index 3e9d957ed3..777a48ad14 100644 --- a/activerecord/test/cases/migration/change_table_test.rb +++ b/activerecord/test/cases/migration/change_table_test.rb @@ -88,8 +88,8 @@ module ActiveRecord def test_timestamps_creates_updated_at_and_created_at with_change_table do |t| - @connection.expect :add_timestamps, nil, [:delete_me] - t.timestamps + @connection.expect :add_timestamps, nil, [:delete_me, null: true] + t.timestamps null: true end end diff --git a/activerecord/test/cases/migration/helper.rb b/activerecord/test/cases/migration/helper.rb index e28feedcf9..4dad77e8fd 100644 --- a/activerecord/test/cases/migration/helper.rb +++ b/activerecord/test/cases/migration/helper.rb @@ -22,7 +22,7 @@ module ActiveRecord super @connection = ActiveRecord::Base.connection connection.create_table :test_models do |t| - t.timestamps + t.timestamps null: true end TestModel.reset_column_information diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index ef3f073472..11338e1fb6 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -561,7 +561,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter? t.string :qualification, :experience t.integer :age, :default => 0 t.date :birthdate - t.timestamps + t.timestamps null: true end end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index a8b21904ac..98f2492ef8 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -138,7 +138,7 @@ ActiveRecord::Schema.define do t.integer :engines_count t.integer :wheels_count t.column :lock_version, :integer, null: false, default: 0 - t.timestamps + t.timestamps null: false end create_table :categories, force: true do |t| @@ -537,7 +537,7 @@ ActiveRecord::Schema.define do t.references :best_friend_of t.integer :insures, null: false, default: 0 t.timestamp :born_at - t.timestamps + t.timestamps null: false end create_table :peoples_treasures, id: false, force: true do |t| @@ -548,7 +548,7 @@ ActiveRecord::Schema.define do create_table :pets, primary_key: :pet_id, force: true do |t| t.string :name t.integer :owner_id, :integer - t.timestamps + t.timestamps null: false end create_table :pirates, force: true do |t| @@ -726,13 +726,13 @@ ActiveRecord::Schema.define do t.string :parent_title t.string :type t.string :group - t.timestamps + t.timestamps null: true end create_table :toys, primary_key: :toy_id, force: true do |t| t.string :name t.integer :pet_id, :integer - t.timestamps + t.timestamps null: false end create_table :traffic_lights, force: true do |t| diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index b67cf02d7b..6daedc0c29 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -222,7 +222,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_migration_with_timestamps run_generator - assert_migration "db/migrate/create_accounts.rb", /t.timestamps/ + assert_migration "db/migrate/create_accounts.rb", /t.timestamps null: false/ end def test_migration_timestamps_are_skipped -- cgit v1.2.3 From 8b028325003e91155d228882444199c1aedf31f7 Mon Sep 17 00:00:00 2001 From: Alex Robbin Date: Sat, 9 Aug 2014 00:10:54 -0400 Subject: add I18n support for `:placeholder` HTML option is passed to form fields --- actionview/CHANGELOG.md | 6 + .../action_view/helpers/tags/placeholderable.rb | 32 ++++ .../lib/action_view/helpers/tags/text_area.rb | 4 + .../lib/action_view/helpers/tags/text_field.rb | 4 + actionview/test/template/form_helper_test.rb | 168 +++++++++++++++++++++ 5 files changed, 214 insertions(+) create mode 100644 actionview/lib/action_view/helpers/tags/placeholderable.rb diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 3fc2ab178c..552a902349 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,9 @@ +* Add I18n support for input/textarea placeholder text. + + Placeholder I18n follows the same convention as `label` I18n. + + *Alex Robbin* + * Fix that render layout: 'messages/layout' should also be added to the dependency tracker tree. *DHH* diff --git a/actionview/lib/action_view/helpers/tags/placeholderable.rb b/actionview/lib/action_view/helpers/tags/placeholderable.rb new file mode 100644 index 0000000000..313aa725c9 --- /dev/null +++ b/actionview/lib/action_view/helpers/tags/placeholderable.rb @@ -0,0 +1,32 @@ +module ActionView + module Helpers + module Tags # :nodoc: + module Placeholderable # :nodoc: + def initialize(*) + super + + if tag_value = @options[:placeholder] + object_name = @object_name.gsub(/\[(.*)_attributes\]\[\d+\]/, '.\1') + method_and_value = tag_value.is_a?(TrueClass) ? @method_name : "#{@method_name}.#{tag_value}" + + if object.respond_to?(:to_model) + key = object.class.model_name.i18n_key + i18n_default = ["#{key}.#{method_and_value}".to_sym, ""] + end + + i18n_default ||= "" + placeholder = I18n.t("#{object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.placeholder").presence + + placeholder ||= if object && object.class.respond_to?(:human_attribute_name) + object.class.human_attribute_name(method_and_value) + end + + placeholder ||= @method_name.humanize + + @options[:placeholder] = placeholder + end + end + end + end + end +end diff --git a/actionview/lib/action_view/helpers/tags/text_area.rb b/actionview/lib/action_view/helpers/tags/text_area.rb index 9ee83ee7c2..69038c1498 100644 --- a/actionview/lib/action_view/helpers/tags/text_area.rb +++ b/actionview/lib/action_view/helpers/tags/text_area.rb @@ -1,7 +1,11 @@ +require 'action_view/helpers/tags/placeholderable' + module ActionView module Helpers module Tags # :nodoc: class TextArea < Base # :nodoc: + include Placeholderable + def render options = @options.stringify_keys add_default_name_and_id(options) diff --git a/actionview/lib/action_view/helpers/tags/text_field.rb b/actionview/lib/action_view/helpers/tags/text_field.rb index e0b80d81c2..5c576a20ca 100644 --- a/actionview/lib/action_view/helpers/tags/text_field.rb +++ b/actionview/lib/action_view/helpers/tags/text_field.rb @@ -1,7 +1,11 @@ +require 'action_view/helpers/tags/placeholderable' + module ActionView module Helpers module Tags # :nodoc: class TextField < Base # :nodoc: + include Placeholderable + def render options = @options.stringify_keys options["size"] = options["maxlength"] unless options.key?("size") diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb index a9f137aec6..d944214961 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -59,6 +59,35 @@ class FormHelperTest < ActionView::TestCase } } + I18n.backend.store_translations 'placeholder', { + activemodel: { + attributes: { + post: { + cost: "Total cost" + }, + :"post/cost" => { + uk: "Pounds" + } + } + }, + helpers: { + placeholder: { + post: { + title: "What is this about?", + written_on: { + spanish: "Escrito en" + }, + comments: { + body: "Write body here" + } + }, + tag: { + value: "Tag" + } + } + } + } + @post = Post.new @comment = Comment.new def @post.errors() @@ -297,6 +326,68 @@ class FormHelperTest < ActionView::TestCase ) end + def test_text_field_placeholder_without_locales + with_locale :placeholder do + assert_dom_equal('', text_field(:post, :body, placeholder: true)) + end + end + + def test_text_field_placeholder_with_locales + with_locale :placeholder do + assert_dom_equal('', text_field(:post, :title, placeholder: true)) + end + end + + def test_text_field_placeholder_with_human_attribute_name + with_locale :placeholder do + assert_dom_equal('', text_field(:post, :cost, placeholder: true)) + end + end + + def test_text_field_placeholder_with_human_attribute_name_and_value + with_locale :placeholder do + assert_dom_equal('', text_field(:post, :cost, placeholder: "uk")) + end + end + + def test_text_field_placeholder_with_locales_and_value + with_locale :placeholder do + assert_dom_equal('', text_field(:post, :written_on, placeholder: "spanish")) + end + end + + def test_text_field_placeholder_with_locales_and_nested_attributes + with_locale :placeholder do + form_for(@post, html: { id: 'create-post' }) do |f| + f.fields_for(:comments) do |cf| + concat cf.text_field(:body, placeholder: true) + end + end + + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + '' + end + + assert_dom_equal expected, output_buffer + end + end + + def test_text_field_placeholder_with_locales_fallback_and_nested_attributes + with_locale :placeholder do + form_for(@post, html: { id: 'create-post' }) do |f| + f.fields_for(:tags) do |cf| + concat cf.text_field(:value, placeholder: true) + end + end + + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + '' + end + + assert_dom_equal expected, output_buffer + end + end + def test_text_field assert_dom_equal( '', @@ -665,6 +756,83 @@ class FormHelperTest < ActionView::TestCase ) end + def test_text_area_placeholder_without_locales + with_locale :placeholder do + assert_dom_equal( + %{}, + text_area(:post, :body, placeholder: true) + ) + end + end + + def test_text_area_placeholder_with_locales + with_locale :placeholder do + assert_dom_equal( + %{}, + text_area(:post, :title, placeholder: true) + ) + end + end + + def test_text_area_placeholder_with_human_attribute_name + with_locale :placeholder do + assert_dom_equal( + %{}, + text_area(:post, :cost, placeholder: true) + ) + end + end + + def test_text_area_placeholder_with_human_attribute_name_and_value + with_locale :placeholder do + assert_dom_equal( + %{}, + text_area(:post, :cost, placeholder: "uk") + ) + end + end + + def test_text_area_placeholder_with_locales_and_value + with_locale :placeholder do + assert_dom_equal( + %{}, + text_area(:post, :written_on, placeholder: "spanish") + ) + end + end + + def test_text_area_placeholder_with_locales_and_nested_attributes + with_locale :placeholder do + form_for(@post, html: { id: 'create-post' }) do |f| + f.fields_for(:comments) do |cf| + concat cf.text_area(:body, placeholder: true) + end + end + + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + %{} + end + + assert_dom_equal expected, output_buffer + end + end + + def test_text_area_placeholder_with_locales_fallback_and_nested_attributes + with_locale :placeholder do + form_for(@post, html: { id: 'create-post' }) do |f| + f.fields_for(:tags) do |cf| + concat cf.text_area(:value, placeholder: true) + end + end + + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + %{} + end + + assert_dom_equal expected, output_buffer + end + end + def test_text_area assert_dom_equal( %{}, -- cgit v1.2.3 From 8f4e24c1ba247087ae33871e48eae635c272d19d Mon Sep 17 00:00:00 2001 From: Tom Kadwill Date: Wed, 13 Aug 2014 07:36:43 +0100 Subject: [ci skip] Updated where scope to conform to new style --- activerecord/lib/active_record/associations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 1c5a737696..830c633c36 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1435,7 +1435,7 @@ module ActiveRecord # belongs_to :firm, foreign_key: "client_of" # belongs_to :person, primary_key: "name", foreign_key: "person_name" # belongs_to :author, class_name: "Person", foreign_key: "author_id" - # belongs_to :valid_coupon, ->(o) { where "discounts > #{o.payments_count}" }, + # belongs_to :valid_coupon, ->(o) { where "discounts > ?", o.payments_count }, # class_name: "Coupon", foreign_key: "coupon_id" # belongs_to :attachable, polymorphic: true # belongs_to :project, readonly: true -- cgit v1.2.3 From ecfce561e415d99df48eebefc1b444dd53580d1a Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Wed, 13 Aug 2014 11:44:58 +0200 Subject: `index_exists?` with `:name` checks specified columns. [Yves Senn & Matthew Draper] The column check was embodied in the defaul index name. If the :name option was used, the specified columns were not verified at all. Given: ``` assert connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_yo_momma) ``` That index could have been defined on any field, not necessarily on `:foo_id`. --- activerecord/CHANGELOG.md | 16 ++++++++++++++++ .../connection_adapters/abstract/schema_statements.rb | 15 ++++++++------- activerecord/test/cases/migration/index_test.rb | 6 ++++++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index ea951fdfd1..b70d16f97b 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,19 @@ +* `index_exists?` with `:name` option does verify specified columns. + + Example: + + add_index :articles, :title, name: "idx_title" + + # Before: + index_exists? :articles, :title, name: "idx_title" # => `true` + index_exists? :articles, :body, name: "idx_title" # => `true` + + # After: + index_exists? :articles, :title, name: "idx_title" # => `true` + index_exists? :articles, :body, name: "idx_title" # => `false` + + *Yves Senn*, *Matthew Draper* + * When calling `update_columns` on a record that is not persisted, the error message now reflects whether that object is a new record or has been destroyed. diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 10753defc2..4957e1ac80 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -43,13 +43,14 @@ module ActiveRecord # index_exists?(:suppliers, :company_id, name: "idx_company_id") # def index_exists?(table_name, column_name, options = {}) - column_names = Array(column_name) - index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names) - if options[:unique] - indexes(table_name).any?{ |i| i.unique && i.name == index_name } - else - indexes(table_name).any?{ |i| i.name == index_name } - end + column_names = Array(column_name).map(&:to_s) + index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, column: column_names) + checks = [] + checks << lambda { |i| i.name == index_name } + checks << lambda { |i| i.columns == column_names } + checks << lambda { |i| i.unique } if options[:unique] + + indexes(table_name).any? { |i| checks.all? { |check| check[i] } } end # Returns an array of Column objects for the table specified by +table_name+. diff --git a/activerecord/test/cases/migration/index_test.rb b/activerecord/test/cases/migration/index_test.rb index 93c3bfae7a..ac932378fd 100644 --- a/activerecord/test/cases/migration/index_test.rb +++ b/activerecord/test/cases/migration/index_test.rb @@ -95,6 +95,12 @@ module ActiveRecord assert connection.index_exists?(:testings, [:foo, :bar]) end + def test_index_exists_with_custom_name_checks_columns + connection.add_index :testings, [:foo, :bar], name: "my_index" + assert connection.index_exists?(:testings, [:foo, :bar], name: "my_index") + assert_not connection.index_exists?(:testings, [:foo], name: "my_index") + end + def test_valid_index_options assert_raise ArgumentError do connection.add_index :testings, :foo, unqiue: true -- cgit v1.2.3 From 12ce5b1cf6be926d6ca22474d1d77ca83b9da2bf Mon Sep 17 00:00:00 2001 From: Akshay Vishnoi Date: Wed, 13 Aug 2014 16:47:28 +0530 Subject: [ci skip] fix spelling of override --- actionpack/test/dispatch/request_test.rb | 2 +- activerecord/lib/active_record/type/value.rb | 4 ++-- activerecord/test/cases/adapters/mysql/connection_test.rb | 2 +- activerecord/test/cases/adapters/mysql2/connection_test.rb | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 6737609567..fe9ee6f73d 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -640,7 +640,7 @@ end class RequestMethod < BaseRequestTest test "method returns environment's request method when it has not been - overriden by middleware".squish do + overridden by middleware".squish do ActionDispatch::Request::HTTP_METHODS.each do |method| request = stub_request('REQUEST_METHOD' => method) diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb index e0a783fb45..475e130013 100644 --- a/activerecord/lib/active_record/type/value.rb +++ b/activerecord/lib/active_record/type/value.rb @@ -69,8 +69,8 @@ module ActiveRecord end # Determines whether the mutable value has been modified since it was - # read. Returns +false+ by default. This method should not need to be - # overriden directly. Types which return a mutable value should include + # read. Returns +false+ by default. This method should not be overridden + # directly. Types which return a mutable value should include # +Type::Mutable+, which will define this method. def changed_in_place?(*) false diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb index b0759dffde..a7b0addc1b 100644 --- a/activerecord/test/cases/adapters/mysql/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -150,7 +150,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase end end - def test_mysql_sql_mode_variable_overides_strict_mode + def test_mysql_sql_mode_variable_overrides_strict_mode run_without_connection do |orig_connection| ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { 'sql_mode' => 'ansi' })) result = ActiveRecord::Base.connection.exec_query 'SELECT @@SESSION.sql_mode' diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index 3b35e69e0d..beedb4f3a1 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -76,7 +76,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase end end - def test_mysql_sql_mode_variable_overides_strict_mode + def test_mysql_sql_mode_variable_overrides_strict_mode run_without_connection do |orig_connection| ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { 'sql_mode' => 'ansi' })) result = ActiveRecord::Base.connection.exec_query 'SELECT @@SESSION.sql_mode' -- cgit v1.2.3 From 67f8b6b2bc7e7eca8723996b1303c3fafa5ed39b Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Tue, 12 Aug 2014 18:24:19 +0000 Subject: Added ActionMailer::DeliverLater --- actionmailer/lib/action_mailer.rb | 1 + actionmailer/lib/action_mailer/base.rb | 6 +- actionmailer/lib/action_mailer/deliver_later.rb | 9 +++ .../lib/action_mailer/deliver_later/job.rb | 11 +++ .../deliver_later/mail_message_wrapper.rb | 46 ++++++++++++ actionmailer/test/deliver_later_test.rb | 82 ++++++++++++++++++++++ actionmailer/test/mailers/delayed_mailer.rb | 6 ++ activejob/lib/active_job.rb | 2 - activejob/lib/active_job/logging.rb | 4 +- activejob/lib/activejob.rb | 1 - activejob/test/helper.rb | 5 +- 11 files changed, 163 insertions(+), 10 deletions(-) create mode 100644 actionmailer/lib/action_mailer/deliver_later.rb create mode 100644 actionmailer/lib/action_mailer/deliver_later/job.rb create mode 100644 actionmailer/lib/action_mailer/deliver_later/mail_message_wrapper.rb create mode 100644 actionmailer/test/deliver_later_test.rb create mode 100644 actionmailer/test/mailers/delayed_mailer.rb delete mode 100644 activejob/lib/activejob.rb diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 83969d4074..49a380419f 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -45,4 +45,5 @@ module ActionMailer autoload :Previews, 'action_mailer/preview' autoload :TestCase autoload :TestHelper + autoload :DeliverLater end diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index bc540aece0..df0c945434 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -549,7 +549,11 @@ module ActionMailer def method_missing(method_name, *args) # :nodoc: if respond_to?(method_name) - new(method_name, *args).message + if defined?(::ActiveJob) && action_methods.include?(method_name.to_s) + DeliverLater::MailMessageWrapper.new(self, method_name, *args) + else + new(method_name, *args).message + end else super end diff --git a/actionmailer/lib/action_mailer/deliver_later.rb b/actionmailer/lib/action_mailer/deliver_later.rb new file mode 100644 index 0000000000..5609e35d01 --- /dev/null +++ b/actionmailer/lib/action_mailer/deliver_later.rb @@ -0,0 +1,9 @@ +require 'active_job' + +module ActionMailer + module DeliverLater + extend ActiveSupport::Autoload + autoload :Job + autoload :MailMessageWrapper + end +end \ No newline at end of file diff --git a/actionmailer/lib/action_mailer/deliver_later/job.rb b/actionmailer/lib/action_mailer/deliver_later/job.rb new file mode 100644 index 0000000000..fda3b626b2 --- /dev/null +++ b/actionmailer/lib/action_mailer/deliver_later/job.rb @@ -0,0 +1,11 @@ +module ActionMailer + module DeliverLater + class Job < ActiveJob::Base + queue_as :mailers + + def perform(mailer, mail_method, delivery_method, *args) + mailer.constantize.send(mail_method, *args).send(delivery_method) + end + end + end +end diff --git a/actionmailer/lib/action_mailer/deliver_later/mail_message_wrapper.rb b/actionmailer/lib/action_mailer/deliver_later/mail_message_wrapper.rb new file mode 100644 index 0000000000..5c0a5304d4 --- /dev/null +++ b/actionmailer/lib/action_mailer/deliver_later/mail_message_wrapper.rb @@ -0,0 +1,46 @@ +module ActionMailer + module DeliverLater + class MailMessageWrapper < Delegator + def initialize(mailer, mail_method, *args) + @mailer = mailer + @mail_method = mail_method + @args = args + __getobj__ + end + + def __getobj__ + @obj ||= @mailer.send(:new, @mail_method, *@args).message + end + + def __setobj__(obj) + @obj = obj + end + + def deliver_later!(options={}) + enqueue_delivery :deliver!, options + end + + def deliver_later(options={}) + enqueue_delivery :deliver, options + end + + def method_missing(m, *args, &block) + __getobj__.__send__(m, *args, &block) + end + + private + def enqueue_delivery(delivery_method, options={}) + args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args + enqueue_method = :enqueue + if options[:at] + enqueue_method = :enqueue_at + args.unshift options[:at] + elsif options[:in] + enqueue_method = :enqueue_in + args.unshift options[:in] + end + ActionMailer::DeliverLater::Job.send enqueue_method, *args + end + end + end +end diff --git a/actionmailer/test/deliver_later_test.rb b/actionmailer/test/deliver_later_test.rb new file mode 100644 index 0000000000..829c96872f --- /dev/null +++ b/actionmailer/test/deliver_later_test.rb @@ -0,0 +1,82 @@ +# encoding: utf-8 +gem 'activejob' +require 'action_mailer/deliver_later' +require 'abstract_unit' +require 'minitest/mock' +require_relative 'mailers/delayed_mailer' + +class MailerTest < ActiveSupport::TestCase + + setup do + @previous_logger = ActiveJob::Base.logger + @previous_delivery_method = ActionMailer::Base.delivery_method + ActionMailer::Base.delivery_method = :test + ActiveJob::Base.logger = Logger.new('/dev/null') + @mail = DelayedMailer.test_message(1, 2, 3) + ActionMailer::Base.deliveries.clear + end + + teardown do + ActiveJob::Base.logger = @previous_logger + ActionMailer::Base.delivery_method = @previous_delivery_method + end + + test 'should be a MailMessageWrapper' do + assert_equal @mail.class, ActionMailer::DeliverLater::MailMessageWrapper + end + + test 'its object should be a Mail::Message' do + assert_equal @mail.__getobj__.class, Mail::Message + end + + test 'should respond to .deliver' do + assert_respond_to @mail, :deliver + end + + test 'should respond to .deliver!' do + assert_respond_to @mail, :deliver! + end + + test 'should respond to .deliver_later' do + assert_respond_to @mail, :deliver_later + end + + test 'should respond to .deliver_later!' do + assert_respond_to @mail, :deliver_later! + end + + test 'should enqueue and run correctly in activejob' do + @mail.deliver_later! + assert_equal ActionMailer::Base.deliveries.size, 1 + end + + test 'should enqueue the email with :deliver delivery method' do + ret = ActionMailer::DeliverLater::Job.stub :enqueue, ->(*args){ args } do + @mail.deliver_later + end + assert_equal ret, ["DelayedMailer", "test_message", "deliver", 1, 2, 3] + end + + test 'should enqueue the email with :deliver! delivery method' do + ret = ActionMailer::DeliverLater::Job.stub :enqueue, ->(*args){ args } do + @mail.deliver_later! + end + assert_equal ret, ["DelayedMailer", "test_message", "deliver!", 1, 2, 3] + end + + test 'should enqueue a delivery with a delay' do + ret = ActionMailer::DeliverLater::Job.stub :enqueue_in, ->(*args){ args } do + @mail.deliver_later in: 600 + end + assert_equal ret, [600, "DelayedMailer", "test_message", "deliver", 1, 2, 3] + end + + test 'should enqueue a delivery at a specific time' do + later_time = Time.now.to_i + 3600 + ret = ActionMailer::DeliverLater::Job.stub :enqueue_at, ->(*args){ args } do + @mail.deliver_later at: later_time + end + assert_equal ret, [later_time, "DelayedMailer", "test_message", "deliver", 1, 2, 3] + end + +end diff --git a/actionmailer/test/mailers/delayed_mailer.rb b/actionmailer/test/mailers/delayed_mailer.rb new file mode 100644 index 0000000000..62d4baa434 --- /dev/null +++ b/actionmailer/test/mailers/delayed_mailer.rb @@ -0,0 +1,6 @@ +class DelayedMailer < ActionMailer::Base + + def test_message(*) + mail(from: 'test-sender@test.com', to: 'test-receiver@test.com', subject: 'Test Subject', body: 'Test Body') + end +end diff --git a/activejob/lib/active_job.rb b/activejob/lib/active_job.rb index ddfdda4fb4..f0a34ffb44 100644 --- a/activejob/lib/active_job.rb +++ b/activejob/lib/active_job.rb @@ -23,8 +23,6 @@ require 'active_support' require 'active_support/rails' - -require 'active_job/railtie' if defined?(Rails) require 'active_job/version' module ActiveJob diff --git a/activejob/lib/active_job/logging.rb b/activejob/lib/active_job/logging.rb index d913aee03d..d20bc3efce 100644 --- a/activejob/lib/active_job/logging.rb +++ b/activejob/lib/active_job/logging.rb @@ -7,7 +7,7 @@ module ActiveJob included do cattr_accessor(:logger) { ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT)) } - around_enqueue do |job, block, _| + around_enqueue do |_, block, _| tag_logger do block.call end @@ -17,7 +17,7 @@ module ActiveJob tag_logger(job.class.name, job.job_id) do payload = {adapter: job.class.queue_adapter, job: job.class, args: job.arguments} ActiveSupport::Notifications.instrument("perform_start.active_job", payload.dup) - ActiveSupport::Notifications.instrument("perform.active_job", payload) do |payload| + ActiveSupport::Notifications.instrument("perform.active_job", payload) do block.call end end diff --git a/activejob/lib/activejob.rb b/activejob/lib/activejob.rb deleted file mode 100644 index fea38731af..0000000000 --- a/activejob/lib/activejob.rb +++ /dev/null @@ -1 +0,0 @@ -require 'active_job' diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb index ad263c05cd..8e255c1696 100644 --- a/activejob/test/helper.rb +++ b/activejob/test/helper.rb @@ -1,7 +1,4 @@ -require 'bundler' -Bundler.setup - -$LOAD_PATH << File.dirname(__FILE__) + "/../lib" +require File.expand_path('../../../load_paths', __FILE__) require 'active_job' -- cgit v1.2.3 From bc116a55ca3dd9f63a1f1ca7ade3623885adcc57 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 13 Aug 2014 18:26:41 +0900 Subject: AM, AP, AV, and AMo tests are already order_independent! --- actionmailer/test/abstract_unit.rb | 5 ----- actionpack/test/abstract_unit.rb | 5 ----- actionview/test/abstract_unit.rb | 5 ----- activemodel/test/cases/helper.rb | 5 ----- 4 files changed, 20 deletions(-) diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 1281b1bcfc..98d266bd73 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -51,8 +51,3 @@ def jruby_skip(message = '') end require 'mocha/setup' # FIXME: stop using mocha - -# FIXME: we have tests that depend on run order, we should fix that and -# remove this method call. -require 'active_support/test_case' -ActiveSupport::TestCase.my_tests_are_order_dependent! diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 376add06ca..4e17d57dad 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -497,8 +497,3 @@ if ActiveSupport::Testing::Isolation.forking_env? && PROCESS_COUNT > 0 # Use N processes (N defaults to 4) Minitest.parallel_executor = ForkingExecutor.new(PROCESS_COUNT) end - -# FIXME: we have tests that depend on run order, we should fix that and -# remove this method call. -require 'active_support/test_case' -ActiveSupport::TestCase.my_tests_are_order_dependent! diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb index 923e637f11..d60712255b 100644 --- a/actionview/test/abstract_unit.rb +++ b/actionview/test/abstract_unit.rb @@ -340,8 +340,3 @@ def jruby_skip(message = '') end require 'mocha/setup' # FIXME: stop using mocha - -# FIXME: we have tests that depend on run order, we should fix that and -# remove this method call. -require 'active_support/test_case' -ActiveSupport::TestCase.my_tests_are_order_dependent! diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb index 5e80353836..804e0c24f6 100644 --- a/activemodel/test/cases/helper.rb +++ b/activemodel/test/cases/helper.rb @@ -13,8 +13,3 @@ I18n.enforce_available_locales = false require 'active_support/testing/autorun' require 'mocha/setup' # FIXME: stop using mocha - -# FIXME: we have tests that depend on run order, we should fix that and -# remove this method call. -require 'active_support/test_case' -ActiveSupport::TestCase.my_tests_are_order_dependent! -- cgit v1.2.3 From fe873dfae2882ad8ae20100af497b9e87202070c Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 13 Aug 2014 19:08:50 +0900 Subject: Duplicated method in the test helper --- activesupport/test/dependencies_test.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index a013aadd67..03e79fa25e 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -990,11 +990,4 @@ class DependenciesTest < ActiveSupport::TestCase ensure ActiveSupport::Dependencies.hook! end - -private - def remove_constants(*constants) - constants.each do |constant| - Object.send(:remove_const, constant) if Object.const_defined?(constant) - end - end end -- cgit v1.2.3 From 6e440a014709f8f5ffd2fb2c72ddf7de9f3f456a Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 13 Aug 2014 19:37:12 +0900 Subject: Reset ActiveSupport::Dependencies.mechanism to make tests order independent --- activesupport/test/dependencies_test.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 03e79fa25e..5fc3de651a 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -47,18 +47,22 @@ class DependenciesTest < ActiveSupport::TestCase end def test_tracking_loaded_files - require_dependency 'dependencies/service_one' - require_dependency 'dependencies/service_two' - assert_equal 2, ActiveSupport::Dependencies.loaded.size + with_loading do + require_dependency 'dependencies/service_one' + require_dependency 'dependencies/service_two' + assert_equal 2, ActiveSupport::Dependencies.loaded.size + end ensure Object.send(:remove_const, :ServiceOne) if Object.const_defined?(:ServiceOne) Object.send(:remove_const, :ServiceTwo) if Object.const_defined?(:ServiceTwo) end def test_tracking_identical_loaded_files - require_dependency 'dependencies/service_one' - require_dependency 'dependencies/service_one' - assert_equal 1, ActiveSupport::Dependencies.loaded.size + with_loading do + require_dependency 'dependencies/service_one' + require_dependency 'dependencies/service_one' + assert_equal 1, ActiveSupport::Dependencies.loaded.size + end ensure Object.send(:remove_const, :ServiceOne) if Object.const_defined?(:ServiceOne) end -- cgit v1.2.3 From cbde413df3839e06dd14e3c220e9800af91e83ab Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 13 Aug 2014 19:44:50 +0900 Subject: AS tests are now order_independent! --- activesupport/test/abstract_unit.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index 52fbaf8a85..7ffcae6007 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -38,8 +38,3 @@ def jruby_skip(message = '') end require 'mocha/setup' # FIXME: stop using mocha - -# FIXME: we have tests that depend on run order, we should fix that and -# remove this method call. -require 'active_support/test_case' -ActiveSupport::TestCase.my_tests_are_order_dependent! -- cgit v1.2.3 From 655c2c8b50aa113ffbe5a8eaf1296b52d111ee4a Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 13 Aug 2014 20:59:31 +0900 Subject: Fix Railties tests that were order dependent --- railties/test/generators_test.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb index b5765c391e..127e059bf1 100644 --- a/railties/test/generators_test.rb +++ b/railties/test/generators_test.rb @@ -152,6 +152,8 @@ class GeneratorsTest < Rails::Generators::TestCase klass = Rails::Generators.find_by_namespace(:plugin, :remarkable) assert klass assert_equal "test_unit:plugin", klass.namespace + ensure + Rails::Generators.fallbacks.delete(:remarkable) end def test_fallbacks_for_generators_on_find_by_namespace_with_context @@ -159,18 +161,26 @@ class GeneratorsTest < Rails::Generators::TestCase klass = Rails::Generators.find_by_namespace(:remarkable, :rails, :plugin) assert klass assert_equal "test_unit:plugin", klass.namespace + ensure + Rails::Generators.fallbacks.delete(:remarkable) end def test_fallbacks_for_generators_on_invoke Rails::Generators.fallbacks[:shoulda] = :test_unit TestUnit::Generators::ModelGenerator.expects(:start).with(["Account"], {}) Rails::Generators.invoke "shoulda:model", ["Account"] + ensure + Rails::Generators.fallbacks.delete(:shoulda) end def test_nested_fallbacks_for_generators + Rails::Generators.fallbacks[:shoulda] = :test_unit Rails::Generators.fallbacks[:super_shoulda] = :shoulda TestUnit::Generators::ModelGenerator.expects(:start).with(["Account"], {}) Rails::Generators.invoke "super_shoulda:model", ["Account"] + ensure + Rails::Generators.fallbacks.delete(:shoulda) + Rails::Generators.fallbacks.delete(:super_shoulda) end def test_developer_options_are_overwritten_by_user_options -- cgit v1.2.3 From bf0a67931dd8e58f6f878b9510ae818ae1f29a3a Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 13 Aug 2014 21:21:22 +0900 Subject: Railties tests are order_independent! Hopefully. --- railties/test/abstract_unit.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb index d8800eaa0f..b6533a5fb2 100644 --- a/railties/test/abstract_unit.rb +++ b/railties/test/abstract_unit.rb @@ -28,10 +28,6 @@ def jruby_skip(message = '') end class ActiveSupport::TestCase - # FIXME: we have tests that depend on run order, we should fix that and - # remove this method call. - self.my_tests_are_order_dependent! - private unless defined?(:capture) -- cgit v1.2.3 From 2c8714568b8ef43523505cfd897ca6c569cac4ac Mon Sep 17 00:00:00 2001 From: Aditya Kapoor Date: Wed, 13 Aug 2014 18:56:03 +0530 Subject: [ci skip] correct default cache store class --- actionpack/lib/action_controller/caching.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index 12d798d0c1..de85e0c1a7 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -16,7 +16,7 @@ module ActionController # All the caching stores from ActiveSupport::Cache are available to be used as backends # for Action Controller caching. # - # Configuration examples (MemoryStore is the default): + # Configuration examples (FileStore is the default): # # config.action_controller.cache_store = :memory_store # config.action_controller.cache_store = :file_store, '/path/to/cache/directory' -- cgit v1.2.3 From 2ef0d3bea622ad8d61b2b2f47451ff0c9604637d Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 13 Aug 2014 22:29:30 +0900 Subject: actionmailer_tests_are_order_dependent! --- actionmailer/test/abstract_unit.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 98d266bd73..1281b1bcfc 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -51,3 +51,8 @@ def jruby_skip(message = '') end require 'mocha/setup' # FIXME: stop using mocha + +# FIXME: we have tests that depend on run order, we should fix that and +# remove this method call. +require 'active_support/test_case' +ActiveSupport::TestCase.my_tests_are_order_dependent! -- cgit v1.2.3 From 9b209603d07cad48b47d554407d6319c9fcced18 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Wed, 13 Aug 2014 12:57:23 +0000 Subject: Clean adapter_test.rb and skip test for sidekiq in unsupported rubies --- Gemfile | 22 +++++++-------- activejob/test/cases/adapter_test.rb | 54 ++---------------------------------- activejob/test/helper.rb | 21 ++++++++++++-- 3 files changed, 33 insertions(+), 64 deletions(-) diff --git a/Gemfile b/Gemfile index 884572c232..d059926fdb 100644 --- a/Gemfile +++ b/Gemfile @@ -36,17 +36,17 @@ end gem 'dalli', '>= 2.2.1' # ActiveJob -gem 'resque' -gem 'resque-scheduler' -gem 'sidekiq' -gem 'sucker_punch' -gem 'delayed_job' -gem 'queue_classic' -gem 'sneakers', '0.1.1.pre' -gem 'que' -gem 'backburner' -gem 'qu-rails', github: "bkeepers/qu", branch: "master" -gem 'qu-redis' +gem 'resque', require: false +gem 'resque-scheduler', require: false +gem 'sidekiq', require: false +gem 'sucker_punch', require: false +gem 'delayed_job', require: false +gem 'queue_classic', require: false +gem 'sneakers', '0.1.1.pre', require: false +gem 'que', require: false +gem 'backburner', require: false +gem 'qu-rails', github: "bkeepers/qu", branch: "master", require: false +gem 'qu-redis', require: false # Add your own local bundler stuff local_gemfile = File.dirname(__FILE__) + "/.Gemfile" diff --git a/activejob/test/cases/adapter_test.rb b/activejob/test/cases/adapter_test.rb index 7f6f4c1159..4fc235ae40 100644 --- a/activejob/test/cases/adapter_test.rb +++ b/activejob/test/cases/adapter_test.rb @@ -1,56 +1,8 @@ require 'helper' class AdapterTest < ActiveSupport::TestCase - setup { @old_adapter = ActiveJob::Base.queue_adapter } - teardown { ActiveJob::Base.queue_adapter = @old_adapter } - - test 'should load inline adapter' do - ActiveJob::Base.queue_adapter = :inline - assert_equal ActiveJob::QueueAdapters::InlineAdapter, ActiveJob::Base.queue_adapter - end - - test 'should load Delayed Job adapter' do - ActiveJob::Base.queue_adapter = :delayed_job - assert_equal ActiveJob::QueueAdapters::DelayedJobAdapter, ActiveJob::Base.queue_adapter - end - - test 'should load Qu adapter' do - ActiveJob::Base.queue_adapter = :qu - assert_equal ActiveJob::QueueAdapters::QuAdapter, ActiveJob::Base.queue_adapter - end - - test 'should load Que adapter' do - ActiveJob::Base.queue_adapter = :que - assert_equal ActiveJob::QueueAdapters::QueAdapter, ActiveJob::Base.queue_adapter - end - - test 'should load Queue Classic adapter' do - ActiveJob::Base.queue_adapter = :queue_classic - assert_equal ActiveJob::QueueAdapters::QueueClassicAdapter, ActiveJob::Base.queue_adapter - end - - test 'should load Resque adapter' do - ActiveJob::Base.queue_adapter = :resque - assert_equal ActiveJob::QueueAdapters::ResqueAdapter, ActiveJob::Base.queue_adapter - end - - test 'should load Sidekiq adapter' do - ActiveJob::Base.queue_adapter = :sidekiq - assert_equal ActiveJob::QueueAdapters::SidekiqAdapter, ActiveJob::Base.queue_adapter - end - - test 'should load Sucker Punch adapter' do - ActiveJob::Base.queue_adapter = :sucker_punch - assert_equal ActiveJob::QueueAdapters::SuckerPunchAdapter, ActiveJob::Base.queue_adapter - end - - test 'should load Sneakers adapter' do - ActiveJob::Base.queue_adapter = :sneakers - assert_equal ActiveJob::QueueAdapters::SneakersAdapter, ActiveJob::Base.queue_adapter - end - - test 'should load Backburner adapter' do - ActiveJob::Base.queue_adapter = :backburner - assert_equal ActiveJob::QueueAdapters::BackburnerAdapter, ActiveJob::Base.queue_adapter + test "should load #{ENV['AJADAPTER']} adapter" do + ActiveJob::Base.queue_adapter = ENV['AJADAPTER'].to_sym + assert_equal ActiveJob::Base.queue_adapter, "active_job/queue_adapters/#{ENV['AJADAPTER']}_adapter".classify.constantize end end diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb index 8e255c1696..5e491332ee 100644 --- a/activejob/test/helper.rb +++ b/activejob/test/helper.rb @@ -2,9 +2,26 @@ require File.expand_path('../../../load_paths', __FILE__) require 'active_job' -adapter = ENV['AJADAPTER'] || 'inline' +@adapter = ENV['AJADAPTER'] || 'inline' -require "adapters/#{adapter}" +def sidekiq? + @adapter == 'sidekiq' +end + +def rubinius? + RUBY_ENGINE == 'rbx' +end + +def ruby_193? + RUBY_VERSION == '1.9.3' && RUBY_ENGINE != 'java' +end + +#Sidekiq don't work with MRI 1.9.3 +#Travis uses rbx 2.6 which don't support unicode characters in methods. +#Remove the check when Travis change to rbx 2.7+ +exit if sidekiq? && (ruby_193? || rubinius?) + +require "adapters/#{@adapter}" require 'active_support/testing/autorun' -- cgit v1.2.3 From e9ce987a9a6fdad436b5511874963e8485b66f84 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Wed, 13 Aug 2014 16:00:19 +0200 Subject: use :test delivery method for `base_test.rb` This solves errors like: ``` BaseTest#test_you_can_register_multiple_observers_to_the_mail_object_that_both_get_informed_on_email_delivery: Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 25 /Users/senny/.rbenv/versions/2.1.2/lib/ruby/2.1.0/net/smtp.rb:541:in `initialize' /Users/senny/.rbenv/versions/2.1.2/lib/ruby/2.1.0/net/smtp.rb:541:in `open' /Users/senny/.rbenv/versions/2.1.2/lib/ruby/2.1.0/net/smtp.rb:541:in `tcp_socket' /Users/senny/.rbenv/versions/2.1.2/lib/ruby/2.1.0/net/smtp.rb:551:in `block in do_start' /Users/senny/.rbenv/versions/2.1.2/lib/ruby/2.1.0/timeout.rb:91:in `block in timeout' /Users/senny/.rbenv/versions/2.1.2/lib/ruby/2.1.0/timeout.rb:101:in `call' /Users/senny/.rbenv/versions/2.1.2/lib/ruby/2.1.0/timeout.rb:101:in `timeout' /Users/senny/.rbenv/versions/2.1.2/lib/ruby/2.1.0/net/smtp.rb:550:in `do_start' /Users/senny/.rbenv/versions/2.1.2/lib/ruby/2.1.0/net/smtp.rb:520:in `start' /Users/senny/Projects/rails/.bundle/gems/mail-2.6.1/lib/mail/network/delivery_methods/smtp.rb:112:in `deliver!' /Users/senny/Projects/rails/.bundle/gems/mail-2.6.1/lib/mail/message.rb:2136:in `do_delivery' /Users/senny/Projects/rails/.bundle/gems/mail-2.6.1/lib/mail/message.rb:232:in `block in deliver' /Users/senny/Projects/rails/actionmailer/lib/action_mailer/base.rb:528:in `block in deliver_mail' /Users/senny/Projects/rails/activesupport/lib/active_support/notifications.rb:164:in `block in instrument' /Users/senny/Projects/rails/activesupport/lib/active_support/notifications/instrumenter.rb:20:in `instrument' /Users/senny/Projects/rails/activesupport/lib/active_support/notifications.rb:164:in `instrument' /Users/senny/Projects/rails/actionmailer/lib/action_mailer/base.rb:526:in `deliver_mail' /Users/senny/Projects/rails/.bundle/gems/mail-2.6.1/lib/mail/message.rb:232:in `deliver' /Users/senny/Projects/rails/actionmailer/test/base_test.rb:598:in `block (2 levels) in ' /Users/senny/Projects/rails/actionmailer/test/base_test.rb:801:in `mail_side_effects' /Users/senny/Projects/rails/actionmailer/test/base_test.rb:593:in `block in ' ``` where the `:smtp` delivery method was leaked over to other test cases. --- actionmailer/test/abstract_unit.rb | 5 ----- actionmailer/test/base_test.rb | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 1281b1bcfc..98d266bd73 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -51,8 +51,3 @@ def jruby_skip(message = '') end require 'mocha/setup' # FIXME: stop using mocha - -# FIXME: we have tests that depend on run order, we should fix that and -# remove this method call. -require 'active_support/test_case' -ActiveSupport::TestCase.my_tests_are_order_dependent! diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 6116d1e29f..fd5f4e2831 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -11,6 +11,8 @@ require 'mailers/asset_mailer' class BaseTest < ActiveSupport::TestCase setup do + @original_delivery_method = ActionMailer::Base.delivery_method + ActionMailer::Base.delivery_method = :test @original_asset_host = ActionMailer::Base.asset_host @original_assets_dir = ActionMailer::Base.assets_dir end @@ -19,6 +21,7 @@ class BaseTest < ActiveSupport::TestCase ActionMailer::Base.asset_host = @original_asset_host ActionMailer::Base.assets_dir = @original_assets_dir BaseMailer.deliveries.clear + ActionMailer::Base.delivery_method = @original_delivery_method end test "method call to mail does not raise error" do @@ -468,7 +471,6 @@ class BaseTest < ActiveSupport::TestCase end test "calling deliver on the action should increment the deliveries collection if using the test mailer" do - BaseMailer.delivery_method = :test BaseMailer.welcome.deliver assert_equal(1, BaseMailer.deliveries.length) end -- cgit v1.2.3 From b1ba333ea72b8aa8fc5ffeda4067c128afb026e4 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Wed, 2 Jul 2014 15:36:23 -0700 Subject: Fix assert_template for files. The test was not failing for `assert_template file: nil` when a file has been rendered. --- actionpack/CHANGELOG.md | 4 ++++ actionpack/lib/action_controller/test_case.rb | 9 +++++++++ .../test/controller/action_pack_assertions_test.rb | 23 ++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 44b8fa843d..6ea7276ac6 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,7 @@ +* Fix `assert_template` not being able to assert that no files were rendered. + + *Guo Xiang Tan* + * Extract source code for the entire exception stack trace for better debugging and diagnosis. diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index eb5d824cbc..152420a54c 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -91,6 +91,13 @@ module ActionController # # assert that no partials were rendered # assert_template partial: false # + # # assert that a file was rendered + # assert_template file: "README.rdoc" + # + # # assert that no file was rendered + # assert_template file: nil + # assert_template file: false + # # In a view test case, you can also assert that specific locals are passed # to partials: # @@ -140,6 +147,8 @@ module ActionController if options[:file] assert_includes @_files.keys, options[:file] + elsif options.key?(:file) + assert @_files.blank?, "expected no files but #{@_files.keys} was rendered" end if expected_partial = options[:partial] diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index b6b5a218cc..311302819e 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -488,6 +488,11 @@ class AssertTemplateTest < ActionController::TestCase assert_raise(ActiveSupport::TestCase::Assertion) do assert_template :file => 'test/hello_world' end + + get :render_file_absolute_path + assert_raise(ActiveSupport::TestCase::Assertion) do + assert_template file: nil + end end def test_with_nil_passes_when_no_template_rendered @@ -612,6 +617,24 @@ class AssertTemplateTest < ActionController::TestCase get :nothing assert_template nil + + get :partial + assert_template partial: 'test/_partial' + + get :nothing + assert_template partial: nil + + get :render_with_layout + assert_template layout: 'layouts/standard' + + get :nothing + assert_template layout: nil + + get :render_file_relative_path + assert_template file: 'README.rdoc' + + get :nothing + assert_template file: nil end end -- cgit v1.2.3 From 1a299b6ca6c6be6a22b86acac7cefd59c56b5b8e Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 14 Aug 2014 01:06:34 +0900 Subject: Missing ActiveSupport require for calling String#first --- actionview/lib/action_view/helpers/translation_helper.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb index 17ec6a40bf..1d50ea2ff5 100644 --- a/actionview/lib/action_view/helpers/translation_helper.rb +++ b/actionview/lib/action_view/helpers/translation_helper.rb @@ -1,4 +1,5 @@ require 'action_view/helpers/tag_helper' +require 'active_support/core_ext/string/access' require 'i18n/exceptions' module ActionView -- cgit v1.2.3 From 95b79c1ff4d253d1554615baa5a02b0910d5a6dc Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Wed, 13 Aug 2014 13:10:59 +0300 Subject: [ci skip] First version of the Active Job guide --- guides/source/active_job_basics.md | 323 +++++++++++++++++++++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 guides/source/active_job_basics.md diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md new file mode 100644 index 0000000000..7b281bfab2 --- /dev/null +++ b/guides/source/active_job_basics.md @@ -0,0 +1,323 @@ +Active Job Basics +================= + +This guide provides you with all you need to get started in creating, +enqueueing and executing background jobs. + +After reading this guide, you will know: + +* How to create jobs. +* How to enqueue jobs. +* How to run jobs in the background. +* How to send emails from your application async. + +-------------------------------------------------------------------------------- + +Introduction +------------ + +Active Job is a framework for declaring jobs and making them run on a variety +of queueing backends. These jobs can be everything from regularly scheduled +clean-ups, billing charges, or mailings. Anything that can be chopped up +into small units of work and run in parallel, really. + + +The Purpose of the Active Job +----------------------------- +The main point is to ensure that all Rails apps will have a job infrastructure +in place, even if it's in the form of an "immediate runner". We can then have +framework features and other gems build on top of that, without having to +worry about API differences between Delayed Job and Resque. Picking your +queuing backend becomes more of an operational concern, then. And you'll +be able to switch between them without having to rewrite your jobs. + + +Creating a Job +-------------- + +This section will provide a step-by-step guide to creating a job and enqueue it. + +### Create the Job + +```bash +$ bin/rails generate job guests_cleanup +create app/jobs/guests_cleanup_job.rb +``` + +As you can see, you can generate jobs just like you use other generators with +Rails. + +If you didn't want to use a generator, you could create your own file inside of +app/jobs, just make sure that it inherits from `ActiveJob::Base`. + +Here's how a job looks like: + +```ruby +class GuestsCleanupJob < ActiveJob::Base + queue_as :default + + def perform + # Do something later + end +end +``` + +### Enqueue the Job + +Enqueue a job like so: + +```ruby +MyJob.enqueue record # Enqueue a job to be performed as soon the queueing system is free. +``` + +```ruby +MyJob.enqueue_at Date.tomorrow.noon, record # Enqueue a job to be performed tomorrow at noon. +``` + +```ruby +MyJob.enqueue_in 1.week, record # Enqueue a job to be performed 1 week from now. +``` + +That's it! + + +Job Execution +------------- + +If not adapter is set, the job is immediately executed. + +### Backends + +Active Job has adapters for the following queueing backends: + +* [Backburner](https://github.com/nesquena/backburner) +* [Delayed Job](https://github.com/collectiveidea/delayed_job) +* [Qu](https://github.com/bkeepers/qu) +* [Que](https://github.com/chanks/que) +* [QueueClassic](https://github.com/ryandotsmith/queue_classic) +* [Resque 1.x](https://github.com/resque/resque) +* [Sidekiq](https://github.com/mperham/sidekiq) +* [Sneakers](https://github.com/jondot/sneakers) +* [Sucker Punch](https://github.com/brandonhilkert/sucker_punch) + +#### Backends Features + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 AsyncQueuesDelayedPrioritiesTimeoutRetries
BackburnerYesYesYesYesJobGlobal
Delayed JobYesYesYesJobGlobalGlobal
QueYesYesYesJobNoJob
Queue ClassicYesYesGemNoNoNo
ResqueYesYesGemQueueGlobal?
SidekiqYesYesYesQueueNoJob
SneakersYesYesNoQueueQueueNo
Sucker PunchYesYesYesNoNoNo
Active JobYesYesWIPNoNoNo
Active Job InlineNoYesN/AN/AN/AN/A
+ + +### Change Backends + +You can easy change your adapter in your application.rb or development.rb or production.rb +or in an initializer: + +```ruby +# be sure to have the adapter gem in your Gemfile and follow the adapter specific +# installation and deployment instructions +YourApp::Application.config.active_job.adapter = :sidekiq +``` + +Queues +------ + +Most of the adapters supports multiple queues. With Active Job you can schedule the job +to run on a specific queue: + +```ruby +class GuestsCleanupJob < ActiveJob::Base + queue_as :low_prio + #.... +end +``` + +NOTE: Make sure your queueing backend "listens" on your queue name. For some backends +you need to specify the queues to listen to. + + +Callbacks +--------- + +Active Job provides hooks during the lifecycle of a job. Callbacks allows you to trigger +logic during the lifecycle of a job. + +### Available callbacks + +* before_enqueue +* around_enqueue +* after_enqueue +* before_perform +* around_perform +* after_perform + +### Usage + +```ruby +class GuestsCleanupJob < ActiveJob::Base + queue_as :default + + before_enqueue do |job| + # do somthing with the job instance + end + + around_perform do |job, block| + # do something before perform + block.call + # do something after perform + end + + def perform + # Do something later + end +end + +``` + +GlobalID +-------- +Active Job supports GlobalID for parameters. This makes it possible +to pass live Active Record objects to your job instead of class/id pairs, which +you then have to manually deserialize. Before, jobs would look like this: + +```ruby +class TrashableCleanupJob + def perform(trashable_class, trashable_id, depth) + trashable = trashable_class.constantize.find(trashable_id) + trashable.cleanup(depth) + end +end +``` + +Now you can simply do: + +```ruby +class TrashableCleanupJob + def perform(trashable, depth) + trashable.cleanup(depth) + end +end +``` + +This works with any class that mixes in ActiveModel::GlobalIdentification, which +by default has been mixed into Active Model classes. + + +Exceptions +---------- +Active Job provides a way to catch exceptions raised during the execution of the +job: + +```ruby + +class GuestsCleanupJob < ActiveJob::Base + queue_as :default + + rescue_from(ActiveRecord:NotFound) do |exception| + # do something with the exception + end + + def perform + # Do something later + end +end +``` -- cgit v1.2.3 From b6496713e9fb2e5e34716662087418931564646b Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Wed, 13 Aug 2014 23:05:21 +0300 Subject: Update ActiveJob guide [ci skip] --- guides/source/active_job_basics.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index 7b281bfab2..4442753370 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -39,15 +39,25 @@ This section will provide a step-by-step guide to creating a job and enqueue it. ### Create the Job +Active Job provides a Rails generator to create jobs. The following will create a +job in app/jobs: + ```bash $ bin/rails generate job guests_cleanup create app/jobs/guests_cleanup_job.rb ``` +You can also create a job that will run on a specific queue: + +```bash +$ bin/rails generate job guests_cleanup --queue urgent +create app/jobs/guests_cleanup_job.rb +``` + As you can see, you can generate jobs just like you use other generators with Rails. -If you didn't want to use a generator, you could create your own file inside of +If you don't want to use a generator, you could create your own file inside of app/jobs, just make sure that it inherits from `ActiveJob::Base`. Here's how a job looks like: @@ -209,8 +219,7 @@ Active Job has adapters for the following queueing backends: ### Change Backends -You can easy change your adapter in your application.rb or development.rb or production.rb -or in an initializer: +You can easy change your adapter: ```ruby # be sure to have the adapter gem in your Gemfile and follow the adapter specific -- cgit v1.2.3 From 32e184864f6b69f74b90112cd1755ea7a33e5656 Mon Sep 17 00:00:00 2001 From: Aditya Kapoor Date: Thu, 14 Aug 2014 02:33:35 +0530 Subject: [ci skip] add note about the ERB escape in generator docs --- guides/source/generators.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/guides/source/generators.md b/guides/source/generators.md index 5e88fa0c70..2349908979 100644 --- a/guides/source/generators.md +++ b/guides/source/generators.md @@ -8,6 +8,7 @@ After reading this guide, you will know: * How to see which generators are available in your application. * How to create a generator using templates. * How Rails searches for generators before invoking them. +* How Rails internally generates Rails code from the templates. * How to customize your scaffold by creating new generators. * How to customize your scaffold by changing generator templates. * How to use fallbacks to avoid overwriting a huge set of generators. @@ -340,6 +341,20 @@ end If you generate another resource, you can see that we get exactly the same result! This is useful if you want to customize your scaffold templates and/or layout by just creating `edit.html.erb`, `index.html.erb` and so on inside `lib/templates/erb/scaffold`. +Many scaffold templates in Rails are written in ERB tags which needs to be escaped, so that the output is a valid ERB code, that can be used correctly in Rails app. + +The following code in one of the generator file, + +```ruby +<%%= stylesheet_include_tag :application %> +``` + +when passed through the generator, would generate the following output. + +```ruby +<%= stylesheet_include_tag :application %> +``` + Adding Generators Fallbacks --------------------------- -- cgit v1.2.3 From 1a6ca03aecccea7ad73fff8b4429f578c6d68290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Wed, 13 Aug 2014 22:51:53 +0200 Subject: Remove redundant NullSerializer Use one from ActiveSupport::MessageEncryptor module. --- .../lib/action_dispatch/middleware/cookies.rb | 23 +++++++--------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index ac9e5effe2..d1cc881a62 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -173,10 +173,14 @@ module ActionDispatch end end + # Passing the ActiveSupport::MessageEncryptor::NullSerializer downstream + # to the Message{Encryptor,Verifier} allows us to handle the + # (de)serialization step within the cookie jar, which gives us the + # opportunity to detect and migrate legacy cookies. module VerifyAndUpgradeLegacySignedMessage def initialize(*args) super - @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: NullSerializer) + @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: ActiveSupport::MessageEncryptor::NullSerializer) end def verify_and_upgrade_legacy_signed_message(name, signed_message) @@ -393,19 +397,6 @@ module ActionDispatch end end - # Passing the NullSerializer downstream to the Message{Encryptor,Verifier} - # allows us to handle the (de)serialization step within the cookie jar, - # which gives us the opportunity to detect and migrate legacy cookies. - class NullSerializer - def self.load(value) - value - end - - def self.dump(value) - value - end - end - module SerializedCookieJars MARSHAL_SIGNATURE = "\x04\x08".freeze @@ -451,7 +442,7 @@ module ActionDispatch @parent_jar = parent_jar @options = options secret = key_generator.generate_key(@options[:signed_cookie_salt]) - @verifier = ActiveSupport::MessageVerifier.new(secret, serializer: NullSerializer) + @verifier = ActiveSupport::MessageVerifier.new(secret, serializer: ActiveSupport::MessageEncryptor::NullSerializer) end def [](name) @@ -508,7 +499,7 @@ module ActionDispatch @options = options secret = key_generator.generate_key(@options[:encrypted_cookie_salt]) sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt]) - @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: NullSerializer) + @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: ActiveSupport::MessageEncryptor::NullSerializer) end def [](name) -- cgit v1.2.3 From efb835c9c0aea9d2a6487a6fdd31d459f1771879 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 14:22:29 -0700 Subject: UnexpectedErrors may reference exceptions that can't be dumped UnexpectedError exceptions wrap the original exception, and the original exception may contain a reference to something that can't be marshal dumped which will cause the process to die. --- actionpack/test/abstract_unit.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 4e17d57dad..674fb253f4 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -484,6 +484,9 @@ class ForkingExecutor method = job[1] reporter = job[2] result = Minitest.run_one_method klass, method + if result.error? + translate_exceptions result + end queue.record reporter, result end } @@ -491,6 +494,20 @@ class ForkingExecutor @size.times { @queue << nil } pool.each { |pid| Process.waitpid pid } end + + private + def translate_exceptions(result) + result.failures.map! { |e| + begin + Marshal.dump e + e + rescue TypeError + ex = Exception.new e.message + ex.set_backtrace e.backtrace + Minitest::UnexpectedError.new ex + end + } + end end if ActiveSupport::Testing::Isolation.forking_env? && PROCESS_COUNT > 0 -- cgit v1.2.3 From d4981c393bb086eb7d8a6696528891927398b073 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 14:31:55 -0700 Subject: this should be accessing the hash, not calling a method --- .../lib/action_dispatch/middleware/templates/rescues/_trace.text.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb index 36b01bf952..c0b53068f7 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb @@ -3,7 +3,7 @@ Rails.root: <%= defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : "unse <% @traces.each do |name, trace| %> <% if trace.any? %> <%= name %> -<%= trace.map(&:trace).join("\n") %> +<%= trace.map { |t| t[:trace] }.join("\n") %> <% end %> <% end %> -- cgit v1.2.3 From 3b908cba28552604f8d85a236f2d1c82f7589d80 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 18:34:21 -0700 Subject: fewer operations on the options hash since we pass `as` down, then we won't have to do an insert / delete dance with the options hash --- actionpack/lib/action_dispatch/routing/mapper.rb | 20 ++++++++++---------- actionpack/test/dispatch/mapper_test.rb | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index cd94f35e8f..e977d769e0 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -63,7 +63,7 @@ module ActionDispatch attr_reader :requirements, :conditions, :defaults attr_reader :to, :default_controller, :default_action, :as, :anchor - def self.build(scope, set, path, options) + def self.build(scope, set, path, as, options) options = scope[:options].merge(options) if scope[:options] options.delete :only @@ -74,10 +74,10 @@ module ActionDispatch defaults = (scope[:defaults] || {}).merge options.delete(:defaults) || {} - new scope, set, path, defaults, options + new scope, set, path, defaults, as, options end - def initialize(scope, set, path, defaults, options) + def initialize(scope, set, path, defaults, as, options) @requirements, @conditions = {}, {} @defaults = defaults @set = set @@ -85,7 +85,7 @@ module ActionDispatch @to = options.delete :to @default_controller = options.delete(:controller) || scope[:controller] @default_action = options.delete(:action) || scope[:action] - @as = options.delete :as + @as = as @anchor = options.delete :anchor formatted = options.delete :format @@ -1544,13 +1544,13 @@ module ActionDispatch action = nil end - if !options.fetch(:as, true) # if it's set to nil or false - options.delete(:as) - else - options[:as] = name_for_action(options[:as], action) - end + as = if !options.fetch(:as, true) # if it's set to nil or false + options.delete(:as) + else + name_for_action(options.delete(:as), action) + end - mapping = Mapping.build(@scope, @set, URI.parser.escape(path), options) + mapping = Mapping.build(@scope, @set, URI.parser.escape(path), as, options) app, conditions, requirements, defaults, as, anchor = mapping.to_route @set.add_route(app, conditions, requirements, defaults, as, anchor) end diff --git a/actionpack/test/dispatch/mapper_test.rb b/actionpack/test/dispatch/mapper_test.rb index 3e554a9cf6..889f9a4736 100644 --- a/actionpack/test/dispatch/mapper_test.rb +++ b/actionpack/test/dispatch/mapper_test.rb @@ -38,7 +38,7 @@ module ActionDispatch def test_mapping_requirements options = { :controller => 'foo', :action => 'bar', :via => :get } - m = Mapper::Mapping.build({}, FakeSet.new, '/store/:name(*rest)', options) + m = Mapper::Mapping.build({}, FakeSet.new, '/store/:name(*rest)', nil, options) _, _, requirements, _ = m.to_route assert_equal(/.+?/, requirements[:rest]) end -- cgit v1.2.3 From 318eea062ec3aa95929cfb516a273e732e22a9d1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 18:42:26 -0700 Subject: pass consistent parameters to canonical_action? now we only have to look up @scope[:scope_level] once per call to canonical_action? and we don't have a variable named "flag" --- actionpack/lib/action_dispatch/routing/mapper.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index e977d769e0..a3a22bceb1 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1634,8 +1634,8 @@ module ActionDispatch RESOURCE_SCOPES.include? @scope[:scope_level] end - def resource_method_scope? #:nodoc: - RESOURCE_METHOD_SCOPES.include? @scope[:scope_level] + def resource_method_scope?(scope_level) #:nodoc: + RESOURCE_METHOD_SCOPES.include? scope_level end def nested_scope? #:nodoc: @@ -1699,8 +1699,8 @@ module ActionDispatch @scope[:constraints][parent_resource.param] end - def canonical_action?(action, flag) #:nodoc: - flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s) + def canonical_action?(action, scope_level) #:nodoc: + scope_level && resource_method_scope?(scope_level) && CANONICAL_ACTIONS.include?(action.to_s) end def shallow_scope(path, options = {}) #:nodoc: @@ -1714,7 +1714,7 @@ module ActionDispatch end def path_for_action(action, path) #:nodoc: - if canonical_action?(action, path.blank?) + if path.blank? && canonical_action?(action, @scope[:scope_level]) @scope[:path].to_s else "#{@scope[:path]}/#{action_path(action, path)}" -- cgit v1.2.3 From 91608dc342237372548ccbe403ef06c56c2755f2 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 18:47:16 -0700 Subject: only test `prefix` once we don't need to repeat if statements --- actionpack/lib/action_dispatch/routing/mapper.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index a3a22bceb1..37f781a7dd 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1732,12 +1732,14 @@ module ActionDispatch elsif !canonical_action?(action, @scope[:scope_level]) prefix = action end - prefix.to_s.tr('-', '_') if prefix + + if prefix + Mapper.normalize_name prefix.to_s.tr('-', '_') + end end def name_for_action(as, action) #:nodoc: prefix = prefix_name_for_action(as, action) - prefix = Mapper.normalize_name(prefix) if prefix name_prefix = @scope[:as] if parent_resource -- cgit v1.2.3 From 0127f02826fec6641c21779c4109a1d2ccb700fe Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 19:13:58 -0700 Subject: only look up scope level once avoid hash lookups and remove depency on the instance --- actionpack/lib/action_dispatch/routing/mapper.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 37f781a7dd..2f03e22be3 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1726,20 +1726,21 @@ module ActionDispatch path || @scope[:path_names][name] || name.to_s end - def prefix_name_for_action(as, action) #:nodoc: + def prefix_name_for_action(as, action, scope_level) #:nodoc: if as prefix = as - elsif !canonical_action?(action, @scope[:scope_level]) + elsif !canonical_action?(action, scope_level) prefix = action end - if prefix + if prefix && prefix != '/' && !prefix.empty? Mapper.normalize_name prefix.to_s.tr('-', '_') end end def name_for_action(as, action) #:nodoc: - prefix = prefix_name_for_action(as, action) + scope_level = @scope[:scope_level] + prefix = prefix_name_for_action(as, action, scope_level) name_prefix = @scope[:as] if parent_resource @@ -1749,7 +1750,7 @@ module ActionDispatch member_name = parent_resource.member_name end - name = case @scope[:scope_level] + name = case scope_level when :nested [name_prefix, prefix] when :collection @@ -1764,7 +1765,7 @@ module ActionDispatch [name_prefix, member_name, prefix] end - if candidate = name.select(&:present?).join("_").presence + if candidate = name.compact.join("_").presence # If a name was not explicitly given, we check if it is valid # and return nil in case it isn't. Otherwise, we pass the invalid name # forward so the underlying router engine treats it and raises an exception. -- cgit v1.2.3 From 2440933fe2c27b27bcafcd9019717800db2641aa Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 14 Aug 2014 11:21:15 +0900 Subject: Finally! None of our tests are order_dependent! --- activerecord/test/cases/helper.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index a35ddf5632..6a8aff4b69 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -201,8 +201,3 @@ module InTimeZone end require 'mocha/setup' # FIXME: stop using mocha - -# FIXME: we have tests that depend on run order, we should fix that and -# remove this method call. -require 'active_support/test_case' -ActiveSupport::TestCase.my_tests_are_order_dependent! -- cgit v1.2.3 From 911ef972a540686fd7fe7a3dee659ce3bb1fd12d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 19:22:30 -0700 Subject: move scope_level to a method on the scope object now we don't have to have a hard coded key --- actionpack/lib/action_dispatch/routing/mapper.rb | 27 ++++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 2f03e22be3..77d9889f28 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1047,7 +1047,6 @@ module ActionDispatch RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns] CANONICAL_ACTIONS = %w(index create new show update destroy) RESOURCE_METHOD_SCOPES = [:collection, :member, :new] - RESOURCE_SCOPES = [:resource, :resources] class Resource #:nodoc: attr_reader :controller, :path, :options, :param @@ -1521,7 +1520,7 @@ module ActionDispatch if on = options.delete(:on) send(on) { decomposed_match(path, options) } else - case @scope[:scope_level] + case @scope.scope_level when :resources nested { decomposed_match(path, options) } when :resource @@ -1564,7 +1563,7 @@ module ActionDispatch raise ArgumentError, "must be called with a path and/or options" end - if @scope[:scope_level] == :resources + if @scope.scope_level == :resources with_scope_level(:root) do scope(parent_resource.path) do super(options) @@ -1631,7 +1630,7 @@ module ActionDispatch end def resource_scope? #:nodoc: - RESOURCE_SCOPES.include? @scope[:scope_level] + @scope.resource_scope? end def resource_method_scope?(scope_level) #:nodoc: @@ -1639,7 +1638,7 @@ module ActionDispatch end def nested_scope? #:nodoc: - @scope[:scope_level] == :nested + @scope.nested? end def with_exclusive_scope @@ -1714,7 +1713,7 @@ module ActionDispatch end def path_for_action(action, path) #:nodoc: - if path.blank? && canonical_action?(action, @scope[:scope_level]) + if path.blank? && canonical_action?(action, @scope.scope_level) @scope[:path].to_s else "#{@scope[:path]}/#{action_path(action, path)}" @@ -1739,7 +1738,7 @@ module ActionDispatch end def name_for_action(as, action) #:nodoc: - scope_level = @scope[:scope_level] + scope_level = @scope.scope_level prefix = prefix_name_for_action(as, action, scope_level) name_prefix = @scope[:as] @@ -1900,6 +1899,8 @@ module ActionDispatch :controller, :action, :path_names, :constraints, :shallow, :blocks, :defaults, :options] + RESOURCE_SCOPES = [:resource, :resources] + attr_reader :parent def initialize(hash, parent = {}) @@ -1907,6 +1908,18 @@ module ActionDispatch @parent = parent end + def scope_level + self[:scope_level] + end + + def nested? + scope_level == :nested + end + + def resource_scope? + RESOURCE_SCOPES.include? scope_level + end + def options OPTIONS end -- cgit v1.2.3 From 19bb6770c0b4a7970a8c6daa2a1ed0f926e721f2 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 19:24:40 -0700 Subject: move the scope level key fully inside the scope object --- actionpack/lib/action_dispatch/routing/mapper.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 77d9889f28..9c9c56fbce 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1654,7 +1654,7 @@ module ActionDispatch end def with_scope_level(kind) - @scope = @scope.new(:scope_level => kind) + @scope = @scope.new_level(kind) yield ensure @scope = @scope.parent @@ -1928,6 +1928,10 @@ module ActionDispatch self.class.new hash, self end + def new_level(level) + new(:scope_level => level) + end + def [](key) @hash.fetch(key) { @parent[key] } end -- cgit v1.2.3 From 677bc212eb7f35ad0f8808f56450a4b6b2340023 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 19:29:24 -0700 Subject: scope_level is no longer a hash key, just use the ivar --- actionpack/lib/action_dispatch/routing/mapper.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 9c9c56fbce..fa273ac201 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1903,13 +1903,14 @@ module ActionDispatch attr_reader :parent - def initialize(hash, parent = {}) + def initialize(hash, parent = {}, scope_level = nil) @hash = hash @parent = parent + @scope_level = scope_level end def scope_level - self[:scope_level] + @scope_level end def nested? @@ -1925,11 +1926,15 @@ module ActionDispatch end def new(hash) - self.class.new hash, self + self.class.new hash, self, scope_level end def new_level(level) - new(:scope_level => level) + self.class.new(self, self, level) + end + + def fetch(key, &block) + @hash.fetch(key, &block) end def [](key) -- cgit v1.2.3 From 047af8dd3c304fbfe625be8b7c600005c69fa593 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 19:29:39 -0700 Subject: change to attr_reader --- actionpack/lib/action_dispatch/routing/mapper.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index fa273ac201..209af7a16d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1901,7 +1901,7 @@ module ActionDispatch RESOURCE_SCOPES = [:resource, :resources] - attr_reader :parent + attr_reader :parent, :scope_level def initialize(hash, parent = {}, scope_level = nil) @hash = hash @@ -1909,10 +1909,6 @@ module ActionDispatch @scope_level = scope_level end - def scope_level - @scope_level - end - def nested? scope_level == :nested end -- cgit v1.2.3 From 374d66be3e9e994bbe8ad2702ee2209c24b581b0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 19:40:57 -0700 Subject: reduce calls to scope_level this will help us to encapsulate magical symbols so hopefully we can eliminate hardcoded magic symbols --- actionpack/lib/action_dispatch/routing/mapper.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 209af7a16d..46d83f7fd7 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1563,7 +1563,7 @@ module ActionDispatch raise ArgumentError, "must be called with a path and/or options" end - if @scope.scope_level == :resources + if @scope.resources? with_scope_level(:root) do scope(parent_resource.path) do super(options) @@ -1913,6 +1913,10 @@ module ActionDispatch scope_level == :nested end + def resources? + scope_level == :resources + end + def resource_scope? RESOURCE_SCOPES.include? scope_level end -- cgit v1.2.3 From e4cb3819dfe36cc9a8396fb207b74980d7bd0cd5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 19:44:05 -0700 Subject: ask the scope for the action name --- actionpack/lib/action_dispatch/routing/mapper.rb | 32 +++++++++++++----------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 46d83f7fd7..1a7e9d32e6 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1749,20 +1749,7 @@ module ActionDispatch member_name = parent_resource.member_name end - name = case scope_level - when :nested - [name_prefix, prefix] - when :collection - [prefix, name_prefix, collection_name] - when :new - [prefix, :new, name_prefix, member_name] - when :member - [prefix, name_prefix, member_name] - when :root - [name_prefix, collection_name, prefix] - else - [name_prefix, member_name, prefix] - end + name = @scope.action_name(name_prefix, prefix, collection_name, member_name) if candidate = name.compact.join("_").presence # If a name was not explicitly given, we check if it is valid @@ -1917,6 +1904,23 @@ module ActionDispatch scope_level == :resources end + def action_name(name_prefix, prefix, collection_name, member_name) + case scope_level + when :nested + [name_prefix, prefix] + when :collection + [prefix, name_prefix, collection_name] + when :new + [prefix, :new, name_prefix, member_name] + when :member + [prefix, name_prefix, member_name] + when :root + [name_prefix, collection_name, prefix] + else + [name_prefix, member_name, prefix] + end + end + def resource_scope? RESOURCE_SCOPES.include? scope_level end -- cgit v1.2.3 From 43ce6e22b19245318ff154f859c82272130ac238 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Aug 2014 19:47:26 -0700 Subject: ask the scope object if it is a resource_method_scope --- actionpack/lib/action_dispatch/routing/mapper.rb | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 1a7e9d32e6..e92baa5aa7 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1046,7 +1046,6 @@ module ActionDispatch VALID_ON_OPTIONS = [:new, :collection, :member] RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns] CANONICAL_ACTIONS = %w(index create new show update destroy) - RESOURCE_METHOD_SCOPES = [:collection, :member, :new] class Resource #:nodoc: attr_reader :controller, :path, :options, :param @@ -1633,8 +1632,8 @@ module ActionDispatch @scope.resource_scope? end - def resource_method_scope?(scope_level) #:nodoc: - RESOURCE_METHOD_SCOPES.include? scope_level + def resource_method_scope? #:nodoc: + @scope.resource_method_scope? end def nested_scope? #:nodoc: @@ -1698,8 +1697,8 @@ module ActionDispatch @scope[:constraints][parent_resource.param] end - def canonical_action?(action, scope_level) #:nodoc: - scope_level && resource_method_scope?(scope_level) && CANONICAL_ACTIONS.include?(action.to_s) + def canonical_action?(action) #:nodoc: + resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s) end def shallow_scope(path, options = {}) #:nodoc: @@ -1713,7 +1712,7 @@ module ActionDispatch end def path_for_action(action, path) #:nodoc: - if path.blank? && canonical_action?(action, @scope.scope_level) + if path.blank? && canonical_action?(action) @scope[:path].to_s else "#{@scope[:path]}/#{action_path(action, path)}" @@ -1725,10 +1724,10 @@ module ActionDispatch path || @scope[:path_names][name] || name.to_s end - def prefix_name_for_action(as, action, scope_level) #:nodoc: + def prefix_name_for_action(as, action) #:nodoc: if as prefix = as - elsif !canonical_action?(action, scope_level) + elsif !canonical_action?(action) prefix = action end @@ -1738,8 +1737,7 @@ module ActionDispatch end def name_for_action(as, action) #:nodoc: - scope_level = @scope.scope_level - prefix = prefix_name_for_action(as, action, scope_level) + prefix = prefix_name_for_action(as, action) name_prefix = @scope[:as] if parent_resource @@ -1887,6 +1885,7 @@ module ActionDispatch :shallow, :blocks, :defaults, :options] RESOURCE_SCOPES = [:resource, :resources] + RESOURCE_METHOD_SCOPES = [:collection, :member, :new] attr_reader :parent, :scope_level @@ -1904,6 +1903,10 @@ module ActionDispatch scope_level == :resources end + def resource_method_scope? + RESOURCE_METHOD_SCOPES.include? scope_level + end + def action_name(name_prefix, prefix, collection_name, member_name) case scope_level when :nested -- cgit v1.2.3 From e76379b04a0934c5d65d3b04fb00fa3b8221c70f Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 14 Aug 2014 16:10:29 +0900 Subject: Clear validators before and after each test Or some tests fail when run in random order --- activerecord/test/cases/validations/i18n_validation_test.rb | 1 + activerecord/test/cases/validations_repair_helper.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb index 3db742c15b..268d7914b5 100644 --- a/activerecord/test/cases/validations/i18n_validation_test.rb +++ b/activerecord/test/cases/validations/i18n_validation_test.rb @@ -6,6 +6,7 @@ class I18nValidationTest < ActiveRecord::TestCase repair_validations(Topic, Reply) def setup + repair_validations(Topic, Reply) Reply.validates_presence_of(:title) @topic = Topic.new @old_load_path, @old_backend = I18n.load_path.dup, I18n.backend diff --git a/activerecord/test/cases/validations_repair_helper.rb b/activerecord/test/cases/validations_repair_helper.rb index c02b3241cd..2bbf0f23b3 100644 --- a/activerecord/test/cases/validations_repair_helper.rb +++ b/activerecord/test/cases/validations_repair_helper.rb @@ -13,7 +13,7 @@ module ActiveRecord end def repair_validations(*model_classes) - yield + yield if block_given? ensure model_classes.each do |k| k.clear_validators! -- cgit v1.2.3 From a1ddde15ae0d612ff2973de9cf768ed701b594e8 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Thu, 14 Aug 2014 09:35:32 +0200 Subject: remove deprecated `MissingHelperError` proxy. The error was moved outside of the `ClassMethods` module. --- actionpack/CHANGELOG.md | 5 +++++ actionpack/lib/abstract_controller/helpers.rb | 3 --- guides/source/4_2_release_notes.md | 5 +++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 6ea7276ac6..7c460fbaef 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,8 @@ +* Remove deprecated `AbstractController::Helpers::ClassMethods::MissingHelperError` + in favor of `AbstractController::Helpers::MissingHelperError`. + + *Yves Senn* + * Fix `assert_template` not being able to assert that no files were rendered. *Guo Xiang Tan* diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb index e77e4e01e9..95c67d482b 100644 --- a/actionpack/lib/abstract_controller/helpers.rb +++ b/actionpack/lib/abstract_controller/helpers.rb @@ -27,9 +27,6 @@ module AbstractController end module ClassMethods - MissingHelperError = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('AbstractController::Helpers::ClassMethods::MissingHelperError', - 'AbstractController::Helpers::MissingHelperError') - # When a class is inherited, wrap its helper module in a new module. # This ensures that the parent class's module can be changed # independently of the child class's. diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index a39dd9ace0..cb451a66c4 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -94,6 +94,11 @@ Action Pack Please refer to the [Changelog][action-pack] for detailed changes. +### Removals + +* Removed deprecated `AbstractController::Helpers::ClassMethods::MissingHelperError` + in favor of `AbstractController::Helpers::MissingHelperError`. + ### Deprecations * Deprecated support for setting the `:to` option of a router to a symbol or a -- cgit v1.2.3 From 37cae67e4ef50c8e763eaa7f059048732aa02e7b Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Thu, 14 Aug 2014 10:00:22 +0200 Subject: =?UTF-8?q?=E0=B2=A0=5F=E0=B2=A0=20now=20that=20the=20commit=20sha?= =?UTF-8?q?=20is=20known=20I=20can=20add=20it=20to=20the=20relese=20guide.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- guides/source/4_2_release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index cb451a66c4..e8d1dc361a 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -98,6 +98,7 @@ Please refer to the [Changelog][action-pack] for detailed changes. * Removed deprecated `AbstractController::Helpers::ClassMethods::MissingHelperError` in favor of `AbstractController::Helpers::MissingHelperError`. + ([Commit](https://github.com/rails/rails/commit/a1ddde15ae0d612ff2973de9cf768ed701b594e8)) ### Deprecations -- cgit v1.2.3 From 4bb823bd1a02e37c105ab21420d37f93df692f28 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 14 Aug 2014 16:37:08 +0900 Subject: Format --- activerecord/test/cases/scoping/relation_scoping_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb index d8a467ec4d..d6a1cd273d 100644 --- a/activerecord/test/cases/scoping/relation_scoping_test.rb +++ b/activerecord/test/cases/scoping/relation_scoping_test.rb @@ -260,7 +260,7 @@ class NestedRelationScopingTest < ActiveRecord::TestCase end end -class HasManyScopingTest< ActiveRecord::TestCase +class HasManyScopingTest < ActiveRecord::TestCase fixtures :comments, :posts, :people, :references def setup @@ -306,7 +306,7 @@ class HasManyScopingTest< ActiveRecord::TestCase end end -class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase +class HasAndBelongsToManyScopingTest < ActiveRecord::TestCase fixtures :posts, :categories, :categories_posts def setup -- cgit v1.2.3 From 76ea9f9714ff08cfffec16fc3672480a9f46f5d9 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 14 Aug 2014 17:08:20 +0900 Subject: Make sure that fixtures are loaded before finding --- activerecord/test/cases/scoping/relation_scoping_test.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb index d6a1cd273d..8e512e118a 100644 --- a/activerecord/test/cases/scoping/relation_scoping_test.rb +++ b/activerecord/test/cases/scoping/relation_scoping_test.rb @@ -11,6 +11,10 @@ require 'models/reference' class RelationScopingTest < ActiveRecord::TestCase fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects + setup do + developers(:david) + end + def test_reverse_order assert_equal Developer.order("id DESC").to_a.reverse, Developer.order("id DESC").reverse_order end -- cgit v1.2.3 From 006225b01517628421964a038bbded3dc7372a30 Mon Sep 17 00:00:00 2001 From: Tom Kadwill Date: Thu, 14 Aug 2014 09:05:10 +0100 Subject: [ci skip] updated 'where' in association documention to new style syntax --- activerecord/lib/active_record/associations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 1c5a737696..735084424e 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1209,7 +1209,7 @@ module ActiveRecord # Option examples: # has_many :comments, -> { order "posted_on" } # has_many :comments, -> { includes :author } - # has_many :people, -> { where("deleted = 0").order("name") }, class_name: "Person" + # has_many :people, -> { where(deleted: false).order(:name) }, class_name: "Person" # has_many :tracks, -> { order "position" }, dependent: :destroy # has_many :comments, dependent: :nullify # has_many :tags, as: :taggable -- cgit v1.2.3 From b5c4c50aa9f93a5602b895a9931ad529cccf2048 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 14 Aug 2014 19:15:58 +0900 Subject: Ignore SCHEMA queries in some habtm tests --- .../associations/has_and_belongs_to_many_associations_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index cc58a4a1a2..859310575e 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -254,7 +254,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase def test_build devel = Developer.find(1) - proj = assert_no_queries { devel.projects.build("name" => "Projekt") } + proj = assert_no_queries(ignore_none: false) { devel.projects.build("name" => "Projekt") } assert !devel.projects.loaded? assert_equal devel.projects.last, proj @@ -269,7 +269,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase def test_new_aliased_to_build devel = Developer.find(1) - proj = assert_no_queries { devel.projects.new("name" => "Projekt") } + proj = assert_no_queries(ignore_none: false) { devel.projects.new("name" => "Projekt") } assert !devel.projects.loaded? assert_equal devel.projects.last, proj @@ -503,7 +503,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase developer = project.developers.first - assert_no_queries do + assert_no_queries(ignore_none: false) do assert project.developers.loaded? assert project.developers.include?(developer) end @@ -824,7 +824,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase def test_has_and_belongs_to_many_associations_on_new_records_use_null_relations projects = Developer.new.projects - assert_no_queries do + assert_no_queries(ignore_none: false) do assert_equal [], projects assert_equal [], projects.where(title: 'omg') assert_equal [], projects.pluck(:title) -- cgit v1.2.3 From fe67dfbbeea092f0f42e81e4901fe9a949cf9484 Mon Sep 17 00:00:00 2001 From: Bogdan Gusiev Date: Thu, 14 Aug 2014 13:44:29 +0300 Subject: Fixed AR::Relation#where edge case with Hash and other Relation Example: Author.where(posts: { author_id: Author.where(country_id: 1) }).joins(:posts) --- .../lib/active_record/relation/query_methods.rb | 18 +++++++++++++++--- activerecord/test/cases/relation/where_test.rb | 9 +++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 1262b2c291..c8f382ad78 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -952,9 +952,7 @@ WARNING self.bind_values += bind_values attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts) - attributes.values.grep(ActiveRecord::Relation) do |rel| - self.bind_values += rel.bind_values - end + add_relations_to_bind_values(attributes) PredicateBuilder.build_from_hash(klass, attributes, table) else @@ -1137,5 +1135,19 @@ WARNING raise ArgumentError, "The method .#{method_name}() must contain arguments." end end + + # This function is recursive just for better readablity. + # #where argument doesn't support more than one level nested hash in real world. + def add_relations_to_bind_values(attributes) + if attributes.is_a?(Hash) + attributes.each_value do |value| + if value.is_a?(ActiveRecord::Relation) + self.bind_values += value.bind_values + else + add_relations_to_bind_values(value) + end + end + end + end end end diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index a6a36a6fd9..b4804aa9d7 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -61,6 +61,15 @@ module ActiveRecord assert_equal expected.to_sql, actual.to_sql end + def test_belongs_to_nested_where_with_relation + author = authors(:david) + + expected = Author.where(id: author ).joins(:posts) + actual = Author.where(posts: { author_id: Author.where(id: author.id) }).joins(:posts) + + assert_equal expected.to_a, actual.to_a + end + def test_polymorphic_shallow_where treasure = Treasure.new treasure.id = 1 -- cgit v1.2.3 From b937ea893e5ad22f53a244ac17d085c1d40fdcc2 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Thu, 14 Aug 2014 10:50:46 +0000 Subject: Make ActionMailer::Previews methods class methods. --- actionmailer/CHANGELOG.md | 6 ++++++ actionmailer/lib/action_mailer/preview.rb | 2 ++ 2 files changed, 8 insertions(+) diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index ab93745f60..451270bae3 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,3 +1,9 @@ +* Make ActionMailer::Previews methods class methods. Previously they were + instance methods and ActionMailer tries to render a message when they + are called. + + *Cristian Bica* + * Deprecate `*_path` helpers in email views. When used they generate non-working links and are not the intention of most developers. Instead we recommend to use `*_url` helper. diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb index 33de1dc049..44cf6665ba 100644 --- a/actionmailer/lib/action_mailer/preview.rb +++ b/actionmailer/lib/action_mailer/preview.rb @@ -22,7 +22,9 @@ module ActionMailer # :nodoc: mattr_accessor :preview_interceptors, instance_writer: false self.preview_interceptors = [] + end + module ClassMethods # Register one or more Interceptors which will be called before mail is previewed. def register_preview_interceptors(*interceptors) interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) } -- cgit v1.2.3 From 18303f6e82258c1d6175f1faf1becd260397fb57 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Wed, 13 Aug 2014 21:50:38 +0000 Subject: Refactor DeliverLater into MessageDelivery --- actionmailer/lib/action_mailer.rb | 3 +- actionmailer/lib/action_mailer/base.rb | 8 +-- .../lib/action_mailer/delayed_delivery_job.rb | 11 +++ actionmailer/lib/action_mailer/deliver_later.rb | 9 --- .../lib/action_mailer/deliver_later/job.rb | 11 --- .../deliver_later/mail_message_wrapper.rb | 46 ------------ actionmailer/lib/action_mailer/message_delivery.rb | 47 +++++++++++++ actionmailer/test/base_test.rb | 5 +- actionmailer/test/deliver_later_test.rb | 82 ---------------------- actionmailer/test/message_delivery_test.rb | 82 ++++++++++++++++++++++ 10 files changed, 147 insertions(+), 157 deletions(-) create mode 100644 actionmailer/lib/action_mailer/delayed_delivery_job.rb delete mode 100644 actionmailer/lib/action_mailer/deliver_later.rb delete mode 100644 actionmailer/lib/action_mailer/deliver_later/job.rb delete mode 100644 actionmailer/lib/action_mailer/deliver_later/mail_message_wrapper.rb create mode 100644 actionmailer/lib/action_mailer/message_delivery.rb delete mode 100644 actionmailer/test/deliver_later_test.rb create mode 100644 actionmailer/test/message_delivery_test.rb diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 49a380419f..1add881054 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -45,5 +45,6 @@ module ActionMailer autoload :Previews, 'action_mailer/preview' autoload :TestCase autoload :TestHelper - autoload :DeliverLater + autoload :MessageDelivery + autoload :DelayedDeliveryJob end diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index df0c945434..3c144c8e5c 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -548,12 +548,8 @@ module ActionMailer end def method_missing(method_name, *args) # :nodoc: - if respond_to?(method_name) - if defined?(::ActiveJob) && action_methods.include?(method_name.to_s) - DeliverLater::MailMessageWrapper.new(self, method_name, *args) - else - new(method_name, *args).message - end + if action_methods.include?(method_name.to_s) + MessageDelivery.new(self, method_name, *args) else super end diff --git a/actionmailer/lib/action_mailer/delayed_delivery_job.rb b/actionmailer/lib/action_mailer/delayed_delivery_job.rb new file mode 100644 index 0000000000..159198829f --- /dev/null +++ b/actionmailer/lib/action_mailer/delayed_delivery_job.rb @@ -0,0 +1,11 @@ +require 'active_job' + +module ActionMailer + class DelayedDeliveryJob < ActiveJob::Base + queue_as :mailers + + def perform(mailer, mail_method, delivery_method, *args) + mailer.constantize.send(mail_method, *args).send(delivery_method) + end + end +end diff --git a/actionmailer/lib/action_mailer/deliver_later.rb b/actionmailer/lib/action_mailer/deliver_later.rb deleted file mode 100644 index 5609e35d01..0000000000 --- a/actionmailer/lib/action_mailer/deliver_later.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'active_job' - -module ActionMailer - module DeliverLater - extend ActiveSupport::Autoload - autoload :Job - autoload :MailMessageWrapper - end -end \ No newline at end of file diff --git a/actionmailer/lib/action_mailer/deliver_later/job.rb b/actionmailer/lib/action_mailer/deliver_later/job.rb deleted file mode 100644 index fda3b626b2..0000000000 --- a/actionmailer/lib/action_mailer/deliver_later/job.rb +++ /dev/null @@ -1,11 +0,0 @@ -module ActionMailer - module DeliverLater - class Job < ActiveJob::Base - queue_as :mailers - - def perform(mailer, mail_method, delivery_method, *args) - mailer.constantize.send(mail_method, *args).send(delivery_method) - end - end - end -end diff --git a/actionmailer/lib/action_mailer/deliver_later/mail_message_wrapper.rb b/actionmailer/lib/action_mailer/deliver_later/mail_message_wrapper.rb deleted file mode 100644 index 5c0a5304d4..0000000000 --- a/actionmailer/lib/action_mailer/deliver_later/mail_message_wrapper.rb +++ /dev/null @@ -1,46 +0,0 @@ -module ActionMailer - module DeliverLater - class MailMessageWrapper < Delegator - def initialize(mailer, mail_method, *args) - @mailer = mailer - @mail_method = mail_method - @args = args - __getobj__ - end - - def __getobj__ - @obj ||= @mailer.send(:new, @mail_method, *@args).message - end - - def __setobj__(obj) - @obj = obj - end - - def deliver_later!(options={}) - enqueue_delivery :deliver!, options - end - - def deliver_later(options={}) - enqueue_delivery :deliver, options - end - - def method_missing(m, *args, &block) - __getobj__.__send__(m, *args, &block) - end - - private - def enqueue_delivery(delivery_method, options={}) - args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args - enqueue_method = :enqueue - if options[:at] - enqueue_method = :enqueue_at - args.unshift options[:at] - elsif options[:in] - enqueue_method = :enqueue_in - args.unshift options[:in] - end - ActionMailer::DeliverLater::Job.send enqueue_method, *args - end - end - end -end diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb new file mode 100644 index 0000000000..fa0aa43a2d --- /dev/null +++ b/actionmailer/lib/action_mailer/message_delivery.rb @@ -0,0 +1,47 @@ +module ActionMailer + class MessageDelivery < Delegator + def initialize(mailer, mail_method, *args) + @mailer = mailer + @mail_method = mail_method + @args = args + end + + def __getobj__ + @obj ||= @mailer.send(:new, @mail_method, *@args).message + end + + def __setobj__(obj) + @obj = obj + end + + def message #:nodoc: + __getobj__ + end + + def deliver_later!(options={}) + enqueue_delivery :deliver!, options + end + + def deliver_later(options={}) + enqueue_delivery :deliver, options + end + + def method_missing(m, *args, &block) + __getobj__.__send__(m, *args, &block) + end + + private + def enqueue_delivery(delivery_method, options={}) + args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args + enqueue_method = :enqueue + if options[:at] + enqueue_method = :enqueue_at + args.unshift options[:at] + elsif options[:in] + enqueue_method = :enqueue_in + args.unshift options[:in] + end + ActionMailer::DelayedDeliveryJob.send enqueue_method, *args + end + end +end diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index fd5f4e2831..c17e59f746 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -4,6 +4,7 @@ require 'set' require 'action_dispatch' require 'active_support/time' +require 'active_support/core_ext/object/itself' require 'mailers/base_mailer' require 'mailers/proc_mailer' @@ -243,7 +244,7 @@ class BaseTest < ActiveSupport::TestCase end end - e = assert_raises(RuntimeError) { LateAttachmentMailer.welcome } + e = assert_raises(RuntimeError) { LateAttachmentMailer.welcome.message } assert_match(/Can't add attachments after `mail` was called./, e.message) end @@ -255,7 +256,7 @@ class BaseTest < ActiveSupport::TestCase end end - e = assert_raises(RuntimeError) { LateInlineAttachmentMailer.welcome } + e = assert_raises(RuntimeError) { LateInlineAttachmentMailer.welcome.message } assert_match(/Can't add attachments after `mail` was called./, e.message) end diff --git a/actionmailer/test/deliver_later_test.rb b/actionmailer/test/deliver_later_test.rb deleted file mode 100644 index 829c96872f..0000000000 --- a/actionmailer/test/deliver_later_test.rb +++ /dev/null @@ -1,82 +0,0 @@ -# encoding: utf-8 -gem 'activejob' -require 'action_mailer/deliver_later' -require 'abstract_unit' -require 'minitest/mock' -require_relative 'mailers/delayed_mailer' - -class MailerTest < ActiveSupport::TestCase - - setup do - @previous_logger = ActiveJob::Base.logger - @previous_delivery_method = ActionMailer::Base.delivery_method - ActionMailer::Base.delivery_method = :test - ActiveJob::Base.logger = Logger.new('/dev/null') - @mail = DelayedMailer.test_message(1, 2, 3) - ActionMailer::Base.deliveries.clear - end - - teardown do - ActiveJob::Base.logger = @previous_logger - ActionMailer::Base.delivery_method = @previous_delivery_method - end - - test 'should be a MailMessageWrapper' do - assert_equal @mail.class, ActionMailer::DeliverLater::MailMessageWrapper - end - - test 'its object should be a Mail::Message' do - assert_equal @mail.__getobj__.class, Mail::Message - end - - test 'should respond to .deliver' do - assert_respond_to @mail, :deliver - end - - test 'should respond to .deliver!' do - assert_respond_to @mail, :deliver! - end - - test 'should respond to .deliver_later' do - assert_respond_to @mail, :deliver_later - end - - test 'should respond to .deliver_later!' do - assert_respond_to @mail, :deliver_later! - end - - test 'should enqueue and run correctly in activejob' do - @mail.deliver_later! - assert_equal ActionMailer::Base.deliveries.size, 1 - end - - test 'should enqueue the email with :deliver delivery method' do - ret = ActionMailer::DeliverLater::Job.stub :enqueue, ->(*args){ args } do - @mail.deliver_later - end - assert_equal ret, ["DelayedMailer", "test_message", "deliver", 1, 2, 3] - end - - test 'should enqueue the email with :deliver! delivery method' do - ret = ActionMailer::DeliverLater::Job.stub :enqueue, ->(*args){ args } do - @mail.deliver_later! - end - assert_equal ret, ["DelayedMailer", "test_message", "deliver!", 1, 2, 3] - end - - test 'should enqueue a delivery with a delay' do - ret = ActionMailer::DeliverLater::Job.stub :enqueue_in, ->(*args){ args } do - @mail.deliver_later in: 600 - end - assert_equal ret, [600, "DelayedMailer", "test_message", "deliver", 1, 2, 3] - end - - test 'should enqueue a delivery at a specific time' do - later_time = Time.now.to_i + 3600 - ret = ActionMailer::DeliverLater::Job.stub :enqueue_at, ->(*args){ args } do - @mail.deliver_later at: later_time - end - assert_equal ret, [later_time, "DelayedMailer", "test_message", "deliver", 1, 2, 3] - end - -end diff --git a/actionmailer/test/message_delivery_test.rb b/actionmailer/test/message_delivery_test.rb new file mode 100644 index 0000000000..6ad9b099a3 --- /dev/null +++ b/actionmailer/test/message_delivery_test.rb @@ -0,0 +1,82 @@ +# encoding: utf-8 +gem 'activejob' +require 'active_job' +require 'abstract_unit' +require 'minitest/mock' +require_relative 'mailers/delayed_mailer' + +class MessageDeliveryTest < ActiveSupport::TestCase + + setup do + @previous_logger = ActiveJob::Base.logger + @previous_delivery_method = ActionMailer::Base.delivery_method + ActionMailer::Base.delivery_method = :test + ActiveJob::Base.logger = Logger.new('/dev/null') + @mail = DelayedMailer.test_message(1, 2, 3) + ActionMailer::Base.deliveries.clear + end + + teardown do + ActiveJob::Base.logger = @previous_logger + ActionMailer::Base.delivery_method = @previous_delivery_method + end + + test 'should be a MessageDelivery' do + assert_equal @mail.class, ActionMailer::MessageDelivery + end + + test 'its object should be a Mail::Message' do + assert_equal @mail.__getobj__.class, Mail::Message + end + + test 'should respond to .deliver' do + assert_respond_to @mail, :deliver + end + + test 'should respond to .deliver!' do + assert_respond_to @mail, :deliver! + end + + test 'should respond to .deliver_later' do + assert_respond_to @mail, :deliver_later + end + + test 'should respond to .deliver_later!' do + assert_respond_to @mail, :deliver_later! + end + + test 'should enqueue and run correctly in activejob' do + @mail.deliver_later! + assert_equal ActionMailer::Base.deliveries.size, 1 + end + + test 'should enqueue the email with :deliver delivery method' do + ret = ActionMailer::DelayedDeliveryJob.stub :enqueue, ->(*args){ args } do + @mail.deliver_later + end + assert_equal ret, ["DelayedMailer", "test_message", "deliver", 1, 2, 3] + end + + test 'should enqueue the email with :deliver! delivery method' do + ret = ActionMailer::DelayedDeliveryJob.stub :enqueue, ->(*args){ args } do + @mail.deliver_later! + end + assert_equal ret, ["DelayedMailer", "test_message", "deliver!", 1, 2, 3] + end + + test 'should enqueue a delivery with a delay' do + ret = ActionMailer::DelayedDeliveryJob.stub :enqueue_in, ->(*args){ args } do + @mail.deliver_later in: 600 + end + assert_equal ret, [600, "DelayedMailer", "test_message", "deliver", 1, 2, 3] + end + + test 'should enqueue a delivery at a specific time' do + later_time = Time.now.to_i + 3600 + ret = ActionMailer::DelayedDeliveryJob.stub :enqueue_at, ->(*args){ args } do + @mail.deliver_later at: later_time + end + assert_equal ret, [later_time, "DelayedMailer", "test_message", "deliver", 1, 2, 3] + end + +end -- cgit v1.2.3 From 7a1dcfc64f0ead619132fb0d8b07834fa48290ae Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Thu, 14 Aug 2014 14:52:02 +0300 Subject: Added ActiveJob configuration for travis --- .travis.yml | 2 +- ci/travis.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 43b08044d3..4c45265db0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ rvm: env: - "GEM=railties" - "GEM=ap" - - "GEM=am,amo,as,av" + - "GEM=am,amo,as,av,aj" - "GEM=ar:mysql" - "GEM=ar:mysql2" - "GEM=ar:sqlite3" diff --git a/ci/travis.rb b/ci/travis.rb index 956a01dbee..db6e41ecfa 100755 --- a/ci/travis.rb +++ b/ci/travis.rb @@ -21,7 +21,8 @@ class Build 'amo' => 'activemodel', 'as' => 'activesupport', 'ar' => 'activerecord', - 'av' => 'actionview' + 'av' => 'actionview', + 'aj' => 'activejob' } attr_reader :component, :options -- cgit v1.2.3 From 9a7d7624a1e20094fddd0ff90d67e4049c09a568 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Fri, 15 Aug 2014 00:08:47 +0900 Subject: Warm up Symbols with where method Looks like #first wasn't warm enough... --- activerecord/test/cases/finder_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 40e51a0cdc..95d006279d 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -51,7 +51,7 @@ class FinderTest < ActiveRecord::TestCase end def test_symbols_table_ref - Post.first # warm up + Post.where("author_id" => nil) # warm up x = Symbol.all_symbols.count Post.where("title" => {"xxxqqqq" => "bar"}) assert_equal x, Symbol.all_symbols.count -- cgit v1.2.3 From fdd4b73cb8ddf35c941b96033cab15e5080f54ac Mon Sep 17 00:00:00 2001 From: Zachary Scott Date: Thu, 14 Aug 2014 08:57:19 -0700 Subject: Use string for order argument, fixed from #16501 [ci skip] --- activerecord/lib/active_record/associations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 6a1ea9623b..d3b9b8251a 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1209,7 +1209,7 @@ module ActiveRecord # Option examples: # has_many :comments, -> { order "posted_on" } # has_many :comments, -> { includes :author } - # has_many :people, -> { where(deleted: false).order(:name) }, class_name: "Person" + # has_many :people, -> { where(deleted: false).order("name") }, class_name: "Person" # has_many :tracks, -> { order "position" }, dependent: :destroy # has_many :comments, dependent: :nullify # has_many :tags, as: :taggable -- cgit v1.2.3 From cf38bda8e2a16ce205ec88207687b8091ee67617 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Fri, 15 Aug 2014 01:27:50 +0900 Subject: Be sure to reset PK name renamed in the test --- activerecord/test/cases/migration/columns_test.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/activerecord/test/cases/migration/columns_test.rb b/activerecord/test/cases/migration/columns_test.rb index 4e6d7963aa..e6aa901814 100644 --- a/activerecord/test/cases/migration/columns_test.rb +++ b/activerecord/test/cases/migration/columns_test.rb @@ -66,6 +66,9 @@ module ActiveRecord def test_mysql_rename_column_preserves_auto_increment rename_column "test_models", "id", "id_test" assert_equal "auto_increment", connection.columns("test_models").find { |c| c.name == "id_test" }.extra + TestModel.reset_column_information + ensure + rename_column "test_models", "id_test", "id" end end -- cgit v1.2.3 From 6c51cc85da458969299ae3d0565dccc3a8530df9 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 14 Aug 2014 10:23:14 -0700 Subject: extract methods and metaprogram less. --- .../action_dispatch/routing/polymorphic_routes.rb | 27 +++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb index bd3696cda1..cd8b1ab066 100644 --- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -142,22 +142,27 @@ module ActionDispatch %w(edit new).each do |action| module_eval <<-EOT, __FILE__, __LINE__ + 1 - def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {}) - polymorphic_url( # polymorphic_url( - record_or_hash, # record_or_hash, - options.merge(:action => "#{action}")) # options.merge(:action => "edit")) - end # end - # - def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {}) - polymorphic_url( # polymorphic_url( - record_or_hash, # record_or_hash, - options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path)) - end # end + def #{action}_polymorphic_url(record_or_hash, options = {}) + polymorphic_url_for_action("#{action}", record_or_hash, options) + end + + def #{action}_polymorphic_path(record_or_hash, options = {}) + polymorphic_path_for_action("#{action}", record_or_hash, options) + end EOT end private + def polymorphic_url_for_action(action, record_or_hash, options) + polymorphic_url(record_or_hash, options.merge(:action => action)) + end + + def polymorphic_path_for_action(action, record_or_hash, options) + options = options.merge(:action => action, :routing_type => :path) + polymorphic_path(record_or_hash, options) + end + class HelperMethodBuilder # :nodoc: CACHE = { 'path' => {}, 'url' => {} } -- cgit v1.2.3 From c8ede235374e948f2922250b2c322b6bc70a2449 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Fri, 15 Aug 2014 02:41:17 +0900 Subject: Ignore MySQL "SHOW VARIABLES" when counting queries --- activerecord/test/cases/test_case.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb index 23a170388e..4070216733 100644 --- a/activerecord/test/cases/test_case.rb +++ b/activerecord/test/cases/test_case.rb @@ -93,7 +93,7 @@ module ActiveRecord # ignored SQL, or better yet, use a different notification for the queries # instead examining the SQL content. oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im] - mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i] + mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /] postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i] sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im] -- cgit v1.2.3 From 4d47220d7c4d07d23e4a1f01bdd6d86fa76237ca Mon Sep 17 00:00:00 2001 From: schneems Date: Thu, 14 Aug 2014 12:29:25 -0500 Subject: Perf optimization for `url_for` called w/ Hash Benchmarking the existing code: ```ruby { :only_path => options[:host].nil? }.merge!(options.symbolize_keys)) ``` Against optimized code, that does not require a new hash or a merge: ```ruby options = options.symbolize_keys options[:only_path] = options[:host].nil? unless options.key?(:only_path) options ``` We see a statistically significant performance gain: ![](https://www.dropbox.com/s/onocpc0zfw4kjxl/Screenshot%202014-08-14%2012.45.30.png?dl=1) Updated to not mutate incoming parameters --- actionview/lib/action_view/routing_url_for.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/actionview/lib/action_view/routing_url_for.rb b/actionview/lib/action_view/routing_url_for.rb index 881a123572..75febb8652 100644 --- a/actionview/lib/action_view/routing_url_for.rb +++ b/actionview/lib/action_view/routing_url_for.rb @@ -82,7 +82,9 @@ module ActionView when nil super({:only_path => true}) when Hash - super({ :only_path => options[:host].nil? }.merge!(options.symbolize_keys)) + options = options.symbolize_keys + options[:only_path] = options[:host].nil? unless options.key?(:only_path) + super(options) when :back _back_url when Symbol -- cgit v1.2.3 From 8147e22a4d09a159a2084fb7ac5c2cfa527a9422 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Thu, 14 Aug 2014 22:54:56 +0000 Subject: Update resque_adapter.rb --- activejob/lib/active_job/queue_adapters/resque_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activejob/lib/active_job/queue_adapters/resque_adapter.rb b/activejob/lib/active_job/queue_adapters/resque_adapter.rb index b228825f07..30a51485bd 100644 --- a/activejob/lib/active_job/queue_adapters/resque_adapter.rb +++ b/activejob/lib/active_job/queue_adapters/resque_adapter.rb @@ -7,7 +7,7 @@ begin rescue LoadError begin require 'resque_scheduler' - rescue LoadError + rescue LoadError => e $stderr.puts 'The ActiveJob resque adapter requires resque-scheduler. Please add it to your Gemfile and run bundle install' raise e end -- cgit v1.2.3 From 20f32bbc3b93ef220ad75303a403fb221a5a2fea Mon Sep 17 00:00:00 2001 From: jbsmith86 Date: Thu, 14 Aug 2014 17:46:50 -0700 Subject: Spelling errors --- .../lib/active_record/associations/preloader/through_association.rb | 2 +- activerecord/lib/active_record/attribute_methods.rb | 4 ++-- .../lib/active_record/connection_adapters/postgresql/oid/jsonb.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index 1fed7f74e7..d57da366bd 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -63,7 +63,7 @@ module ActiveRecord should_reset = (through_scope != through_reflection.klass.unscoped) || (reflection.options[:source_type] && through_reflection.collection?) - # Dont cache the association - we would only be caching a subset + # Don't cache the association - we would only be caching a subset if should_reset owners.each { |owner| owner.association(association_name).reset diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index a2bb78dfcc..09e2faee86 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -279,9 +279,9 @@ module ActiveRecord end # Returns an #inspect-like string for the value of the - # attribute +attr_name+. String attributes are truncated upto 50 + # attribute +attr_name+. String attributes are truncated up to 50 # characters, Date and Time attributes are returned in the - # :db format, Array attributes are truncated upto 10 values. + # :db format, Array attributes are truncated up to 10 values. # Other attributes return the value of #inspect without # modification. # diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb index 34ed32ad35..380c50fc14 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb @@ -12,7 +12,7 @@ module ActiveRecord # roundtripping jsonb columns. This causes some false positives for # the comparison here. Therefore, we need to parse and re-dump the # raw value here to ensure the insignificant whitespaces are - # consitent with our encoder's output. + # consistent with our encoder's output. raw_old_value = type_cast_for_database(type_cast_from_database(raw_old_value)) super(raw_old_value, new_value) end -- cgit v1.2.3 From 2cc1a64579cca794844288e487bafd89f1351c19 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Fri, 15 Aug 2014 13:59:05 +0900 Subject: s/Dont'/Don't/ --- railties/lib/rails/generators/rails/controller/controller_generator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb index fbecab1823..a48cc13ed7 100644 --- a/railties/lib/rails/generators/rails/controller/controller_generator.rb +++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb @@ -2,7 +2,7 @@ module Rails module Generators class ControllerGenerator < NamedBase # :nodoc: argument :actions, type: :array, default: [], banner: "action action" - class_option :skip_routes, type: :boolean, desc: "Dont' add routes to config/routes.rb." + class_option :skip_routes, type: :boolean, desc: "Don't add routes to config/routes.rb." check_class_collision suffix: "Controller" -- cgit v1.2.3 From 4954de65a62941126a7aa194aa83275c0e98d486 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Fri, 15 Aug 2014 20:10:42 +0900 Subject: create_table + transactional_fixtures = :bomb: --- activerecord/test/cases/schema_dumper_test.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 4e71d04bc0..066e6b6468 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -2,6 +2,8 @@ require "cases/helper" require 'support/schema_dumping_helper' class SchemaDumperTest < ActiveRecord::TestCase + self.use_transactional_fixtures = false + setup do ActiveRecord::SchemaMigration.create_table end @@ -21,6 +23,8 @@ class SchemaDumperTest < ActiveRecord::TestCase schema_info = ActiveRecord::Base.connection.dump_schema_information assert_match(/20100201010101.*20100301010101/m, schema_info) + ensure + ActiveRecord::SchemaMigration.delete_all end def test_magic_comment -- cgit v1.2.3 From a3ee03083b50e1458c1276f499226ec59c0fb73d Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Thu, 14 Aug 2014 12:43:57 -0600 Subject: Use the method for determining attribute methods rather than duplicating I've been trying to reduce the number of places that care about `attributes`, and its existence. We have a method for this check, let's use it instead. --- activerecord/lib/active_record/timestamp.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 5ef98ed820..417cd61f0c 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -47,8 +47,9 @@ module ActiveRecord current_time = current_time_from_proper_timezone all_timestamp_attributes.each do |column| - if attributes.key?(column.to_s) && self.send(column).nil? - write_attribute(column.to_s, current_time) + column = column.to_s + if has_attribute?(column) && !attribute_present?(column) + write_attribute(column, current_time) end end end -- cgit v1.2.3 From 78f2ee50f00d670f6a075ca4325f8bcf4cf2d24b Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Fri, 15 Aug 2014 14:13:11 +0200 Subject: prefer `has_attribute?` over `attributes.key?`. Follow up to the discussion on #16505. --- activerecord/test/cases/attribute_methods_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index ab67cf4085..68aa6b8caf 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -263,7 +263,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase end assert_equal klass.column_names, klass.new.attributes.keys - assert_not klass.new.attributes.key?('id') + assert_not klass.new.has_attributes?('id') end def test_hashes_not_mangled -- cgit v1.2.3 From 60b2d29ee155707946dec8c0c1faf0cf7ba4aab5 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Fri, 15 Aug 2014 14:17:56 +0200 Subject: fix typo in method name (broken build :sweat:) --- activerecord/test/cases/attribute_methods_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 68aa6b8caf..535928783f 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -263,7 +263,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase end assert_equal klass.column_names, klass.new.attributes.keys - assert_not klass.new.has_attributes?('id') + assert_not klass.new.has_attribute?('id') end def test_hashes_not_mangled -- cgit v1.2.3 From 967a2ff9989bbf36d283ee447cfcc41750e8b025 Mon Sep 17 00:00:00 2001 From: Arun Agrawal Date: Fri, 15 Aug 2014 15:43:21 +0200 Subject: Fixed link for in_place_editor [ci skip] closes #16512 --- guides/source/security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/security.md b/guides/source/security.md index ebfcc5bdd0..d27ec5423f 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -847,7 +847,7 @@ It is recommended to _use RedCloth in combination with a whitelist input filter_ NOTE: _The same security precautions have to be taken for Ajax actions as for "normal" ones. There is at least one exception, however: The output has to be escaped in the controller already, if the action doesn't render a view._ -If you use the [in_place_editor plugin](http://dev.rubyonrails.org/browser/plugins/in_place_editing), or actions that return a string, rather than rendering a view, _you have to escape the return value in the action_. Otherwise, if the return value contains a XSS string, the malicious code will be executed upon return to the browser. Escape any input value using the h() method. +If you use the [in_place_editor plugin](https://github.com/rails/in_place_editing), or actions that return a string, rather than rendering a view, _you have to escape the return value in the action_. Otherwise, if the return value contains a XSS string, the malicious code will be executed upon return to the browser. Escape any input value using the h() method. ### Command Line Injection -- cgit v1.2.3 From a59b9e2284353dcf8cf6d77b97aaa4255563c807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 15 Aug 2014 10:43:55 -0300 Subject: Point to rubygems instead of Rails GitHub. [ci skip] The rails repository is not the official plugin anymore --- guides/source/security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/security.md b/guides/source/security.md index d27ec5423f..f1db81458c 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -847,7 +847,7 @@ It is recommended to _use RedCloth in combination with a whitelist input filter_ NOTE: _The same security precautions have to be taken for Ajax actions as for "normal" ones. There is at least one exception, however: The output has to be escaped in the controller already, if the action doesn't render a view._ -If you use the [in_place_editor plugin](https://github.com/rails/in_place_editing), or actions that return a string, rather than rendering a view, _you have to escape the return value in the action_. Otherwise, if the return value contains a XSS string, the malicious code will be executed upon return to the browser. Escape any input value using the h() method. +If you use the [in_place_editor plugin](https://rubygems.org/gems/in_place_editing), or actions that return a string, rather than rendering a view, _you have to escape the return value in the action_. Otherwise, if the return value contains a XSS string, the malicious code will be executed upon return to the browser. Escape any input value using the h() method. ### Command Line Injection -- cgit v1.2.3 From bc153cff9156e7e19b3587f0bb8062d238644b1d Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Fri, 15 Aug 2014 13:37:53 -0600 Subject: Implement `==` on `Type::Value` and `Attribute` This was a small self contained piece of the refactoring that I am working on, which required these objects to be comparable. --- activerecord/lib/active_record/attribute.rb | 7 +++++++ activerecord/lib/active_record/type/value.rb | 7 +++++++ activerecord/test/cases/attribute_test.rb | 30 ++++++++++++++++++++++++++++ activerecord/test/cases/types_test.rb | 6 ++++++ 4 files changed, 50 insertions(+) diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb index 6d38224830..15cbbcff68 100644 --- a/activerecord/lib/active_record/attribute.rb +++ b/activerecord/lib/active_record/attribute.rb @@ -62,6 +62,13 @@ module ActiveRecord true end + def ==(other) + self.class == other.class && + name == other.name && + value_before_type_cast == other.value_before_type_cast && + type == other.type + end + protected def initialize_dup(other) diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb index 475e130013..9456a4a56c 100644 --- a/activerecord/lib/active_record/type/value.rb +++ b/activerecord/lib/active_record/type/value.rb @@ -76,6 +76,13 @@ module ActiveRecord false end + def ==(other) + self.class == other.class && + precision == other.precision && + scale == other.scale && + limit == other.limit + end + private def type_cast(value) diff --git a/activerecord/test/cases/attribute_test.rb b/activerecord/test/cases/attribute_test.rb index 91f6aee931..7b325abf1d 100644 --- a/activerecord/test/cases/attribute_test.rb +++ b/activerecord/test/cases/attribute_test.rb @@ -138,5 +138,35 @@ module ActiveRecord test "uninitialized attributes have no value" do assert_nil Attribute.uninitialized(:foo, nil).value end + + test "attributes equal other attributes with the same constructor arguments" do + first = Attribute.from_database(:foo, 1, Type::Integer.new) + second = Attribute.from_database(:foo, 1, Type::Integer.new) + assert_equal first, second + end + + test "attributes do not equal attributes with different names" do + first = Attribute.from_database(:foo, 1, Type::Integer.new) + second = Attribute.from_database(:bar, 1, Type::Integer.new) + assert_not_equal first, second + end + + test "attributes do not equal attributes with different types" do + first = Attribute.from_database(:foo, 1, Type::Integer.new) + second = Attribute.from_database(:foo, 1, Type::Float.new) + assert_not_equal first, second + end + + test "attributes do not equal attributes with different values" do + first = Attribute.from_database(:foo, 1, Type::Integer.new) + second = Attribute.from_database(:foo, 2, Type::Integer.new) + assert_not_equal first, second + end + + test "attributes do not equal attributes of other classes" do + first = Attribute.from_database(:foo, 1, Type::Integer.new) + second = Attribute.from_user(:foo, 1, Type::Integer.new) + assert_not_equal first, second + end end end diff --git a/activerecord/test/cases/types_test.rb b/activerecord/test/cases/types_test.rb index 5c54812f30..db4f78d354 100644 --- a/activerecord/test/cases/types_test.rb +++ b/activerecord/test/cases/types_test.rb @@ -149,6 +149,12 @@ module ActiveRecord end end + def test_type_equality + assert_equal Type::Value.new, Type::Value.new + assert_not_equal Type::Value.new, Type::Integer.new + assert_not_equal Type::Value.new(precision: 1), Type::Value.new(precision: 2) + end + if current_adapter?(:SQLite3Adapter) def test_binary_encoding type = SQLite3Binary.new -- cgit v1.2.3 From 0002954512364f2f69d28798f7a79aa8e27d7b6b Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Fri, 8 Aug 2014 15:37:38 -0400 Subject: Use *_transaction methods in TransactionManager Use `commit_transaction`/`rollback_transaction` on `within_new_transaction` method, so they make sure they `pop` the transaction from the stack before calling the methods `commit`/`rollback`. --- .../connection_adapters/abstract/transaction.rb | 6 ++---- activerecord/test/cases/transactions_test.rb | 24 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 4a7f2aaca8..3fce32211d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -169,16 +169,14 @@ module ActiveRecord transaction = begin_transaction options yield rescue Exception => error - transaction.rollback if transaction + rollback_transaction if transaction raise ensure begin - transaction.commit unless error + commit_transaction unless error rescue Exception transaction.rollback raise - ensure - @stack.pop if transaction end end diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index b4849222b8..9cfe041de2 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -80,6 +80,30 @@ class TransactionTest < ActiveRecord::TestCase end end + def test_number_of_transactions_in_commit + num = nil + + Topic.connection.class_eval do + alias :real_commit_db_transaction :commit_db_transaction + define_method(:commit_db_transaction) do + num = transaction_manager.open_transactions + real_commit_db_transaction + end + end + + Topic.transaction do + @first.approved = true + @first.save! + end + + assert_equal 0, num + ensure + Topic.connection.class_eval do + remove_method :commit_db_transaction + alias :commit_db_transaction :real_commit_db_transaction rescue nil + end + end + def test_successful_with_instance_method @first.transaction do @first.approved = true -- cgit v1.2.3 From 2e90fe736de73cfd89eed68b38e03eb6175314bc Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Fri, 8 Aug 2014 15:46:00 -0400 Subject: Fix regression on after_commit in nested transactions. after_commit should not run in nested transactions, however they should run once the outermost transaction gets committed. This patch fixes the problem copying the records from the Savepoint to its parent. So the RealTransaction will have all records that needs to run callbacks on it. [fixes #16425] --- activerecord/CHANGELOG.md | 6 ++++++ .../connection_adapters/abstract/transaction.rb | 2 ++ activerecord/test/cases/transaction_callbacks_test.rb | 13 +++++++++++++ 3 files changed, 21 insertions(+) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 7d7870fe13..02660ae4c2 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Fix regression on after_commit that didnt fire when having nested transactions. + + Fixes #16425 + + *arthurnn* + * Do not try to write timestamps when a table has no timestamps columns. Fixes #8813. diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 3fce32211d..8f06cf3a1f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -111,6 +111,8 @@ module ActiveRecord def commit super connection.release_savepoint(savepoint_name) + parent = connection.transaction_manager.current_transaction + records.each { |r| parent.add_record(r) } end def full_rollback?; false; end diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb index 3d64ecb464..a3f39804b7 100644 --- a/activerecord/test/cases/transaction_callbacks_test.rb +++ b/activerecord/test/cases/transaction_callbacks_test.rb @@ -129,6 +129,19 @@ class TransactionCallbacksTest < ActiveRecord::TestCase assert_equal [:commit_on_update], @first.history end + def test_only_call_after_commit_on_top_level_transactions + @first.after_commit_block{|r| r.history << :after_commit} + assert @first.history.empty? + + @first.transaction do + @first.transaction(requires_new: true) do + @first.touch + end + assert @first.history.empty? + end + assert_equal [:after_commit], @first.history + end + def test_call_after_rollback_after_transaction_rollsback @first.after_commit_block{|r| r.history << :after_commit} @first.after_rollback_block{|r| r.history << :after_rollback} -- cgit v1.2.3 From e034ac33ea923cb068644a0fae4a8a8e7ed9dc2f Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Fri, 15 Aug 2014 23:20:55 +0300 Subject: Removed method missing from ActionMailer::MessageDelivery --- actionmailer/lib/action_mailer/base.rb | 4 ++++ actionmailer/lib/action_mailer/message_delivery.rb | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 3c144c8e5c..a229d9142c 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -586,6 +586,10 @@ module ActionMailer class NullMail #:nodoc: def body; '' end + def respond_to?(string, include_all=false) + true + end + def method_missing(*args) nil end diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb index fa0aa43a2d..27e0671427 100644 --- a/actionmailer/lib/action_mailer/message_delivery.rb +++ b/actionmailer/lib/action_mailer/message_delivery.rb @@ -26,10 +26,6 @@ module ActionMailer enqueue_delivery :deliver, options end - def method_missing(m, *args, &block) - __getobj__.__send__(m, *args, &block) - end - private def enqueue_delivery(delivery_method, options={}) args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args -- cgit v1.2.3 From 94ae25ecd5635f7f97a2e53afa8e3f82c408435d Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Fri, 15 Aug 2014 23:32:08 +0300 Subject: ActiveJob: Reworked queue_base_name as default_queue_name + Allow configure ActiveJob from app.config.active_job --- activejob/lib/active_job/queue_name.rb | 8 ++++---- activejob/lib/active_job/railtie.rb | 14 +++++++++++++- activejob/test/cases/queue_naming_test.rb | 10 +++++----- guides/source/active_job_basics.md | 2 +- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/activejob/lib/active_job/queue_name.rb b/activejob/lib/active_job/queue_name.rb index 859ddad034..c2186d9fe9 100644 --- a/activejob/lib/active_job/queue_name.rb +++ b/activejob/lib/active_job/queue_name.rb @@ -3,16 +3,16 @@ module ActiveJob extend ActiveSupport::Concern module ClassMethods - mattr_accessor(:queue_base_name) { "active_jobs" } + mattr_accessor(:default_queue_name) { "default" } def queue_as(part_name) - self.queue_name = "#{queue_base_name}_#{part_name}" + self.queue_name = part_name.to_s.presence || default_queue_name end end included do class_attribute :queue_name - self.queue_name = queue_base_name + self.queue_name = default_queue_name end end -end \ No newline at end of file +end diff --git a/activejob/lib/active_job/railtie.rb b/activejob/lib/active_job/railtie.rb index 16736374af..7c1dd8f275 100644 --- a/activejob/lib/active_job/railtie.rb +++ b/activejob/lib/active_job/railtie.rb @@ -4,8 +4,20 @@ require 'rails' module ActiveJob # = Active Job Railtie class Railtie < Rails::Railtie # :nodoc: - initializer 'active_job' do + config.active_job = ActiveSupport::OrderedOptions.new + + initializer 'active_job.logger' do ActiveSupport.on_load(:active_job) { self.logger = ::Rails.logger } end + + initializer "active_job.set_configs" do |app| + options = app.config.active_job + options.queue_adapter ||= :inline + + ActiveSupport.on_load(:active_job) do + options.each { |k,v| send("#{k}=", v) } + end + end + end end diff --git a/activejob/test/cases/queue_naming_test.rb b/activejob/test/cases/queue_naming_test.rb index 7cb516bbb4..426af608f0 100644 --- a/activejob/test/cases/queue_naming_test.rb +++ b/activejob/test/cases/queue_naming_test.rb @@ -5,7 +5,7 @@ require 'jobs/nested_job' class QueueNamingTest < ActiveSupport::TestCase test 'name derived from base' do - assert_equal "active_jobs", HelloJob.queue_name + assert_equal "default", HelloJob.queue_name end test 'name appended in job' do @@ -13,11 +13,11 @@ class QueueNamingTest < ActiveSupport::TestCase HelloJob.queue_as :greetings LoggingJob.queue_as :bookkeeping - assert_equal "active_jobs", NestedJob.queue_name - assert_equal "active_jobs_greetings", HelloJob.queue_name - assert_equal "active_jobs_bookkeeping", LoggingJob.queue_name + assert_equal "default", NestedJob.queue_name + assert_equal "greetings", HelloJob.queue_name + assert_equal "bookkeeping", LoggingJob.queue_name ensure - HelloJob.queue_name = LoggingJob.queue_name = ActiveJob::Base.queue_base_name + HelloJob.queue_name = LoggingJob.queue_name = ActiveJob::Base.default_queue_name end end end diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index 4442753370..c100448a48 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -224,7 +224,7 @@ You can easy change your adapter: ```ruby # be sure to have the adapter gem in your Gemfile and follow the adapter specific # installation and deployment instructions -YourApp::Application.config.active_job.adapter = :sidekiq +YourApp::Application.config.active_job.queue_adapter = :sidekiq ``` Queues -- cgit v1.2.3 From c2f1eca19409cbbe72bf89b2087b212341201aa1 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Fri, 15 Aug 2014 23:35:06 +0300 Subject: Suggest the perform method can take arguments when generating an ActiveJob job --- activejob/lib/rails/generators/job/templates/job.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activejob/lib/rails/generators/job/templates/job.rb b/activejob/lib/rails/generators/job/templates/job.rb index 6a21616d30..462c71d917 100644 --- a/activejob/lib/rails/generators/job/templates/job.rb +++ b/activejob/lib/rails/generators/job/templates/job.rb @@ -2,8 +2,8 @@ class <%= class_name %>Job < ActiveJob::Base queue_as :<%= options[:queue] %> - def perform + def perform(*args) # Do something later end end -<% end -%> \ No newline at end of file +<% end -%> -- cgit v1.2.3 From 788aee5acf195d55914e086540f907d9291e9d24 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Sat, 16 Aug 2014 00:02:06 +0300 Subject: Moved AR testing from using global variable to thread variable --- activejob/test/cases/job_serialization_test.rb | 4 ++-- activejob/test/cases/logging_test.rb | 2 +- activejob/test/cases/queuing_test.rb | 10 +++++----- activejob/test/cases/rescue_test.rb | 4 ++-- activejob/test/jobs/gid_job.rb | 4 ++-- activejob/test/jobs/hello_job.rb | 2 +- activejob/test/jobs/rescue_job.rb | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/activejob/test/cases/job_serialization_test.rb b/activejob/test/cases/job_serialization_test.rb index b1e24db22e..f3b89d8899 100644 --- a/activejob/test/cases/job_serialization_test.rb +++ b/activejob/test/cases/job_serialization_test.rb @@ -4,12 +4,12 @@ require 'models/person' class JobSerializationTest < ActiveSupport::TestCase setup do - $BUFFER = [] + Thread.current[:ajbuffer] = [] @person = Person.find(5) end test 'serialize job with gid' do GidJob.enqueue @person - assert_equal "Person with ID: 5", $BUFFER.pop + assert_equal "Person with ID: 5", Thread.current[:ajbuffer].pop end end diff --git a/activejob/test/cases/logging_test.rb b/activejob/test/cases/logging_test.rb index 3f21fa644c..f0f315c906 100644 --- a/activejob/test/cases/logging_test.rb +++ b/activejob/test/cases/logging_test.rb @@ -23,7 +23,7 @@ class AdapterTest < ActiveSupport::TestCase def setup super - $BUFFER = [] + Thread.current[:ajbuffer] = [] @old_logger = ActiveJob::Base.logger @logger = ActiveSupport::TaggedLogging.new(TestLogger.new) set_logger @logger diff --git a/activejob/test/cases/queuing_test.rb b/activejob/test/cases/queuing_test.rb index 3dd9ecd8d2..49760ce9c0 100644 --- a/activejob/test/cases/queuing_test.rb +++ b/activejob/test/cases/queuing_test.rb @@ -5,17 +5,17 @@ require 'active_support/core_ext/numeric/time' class QueuingTest < ActiveSupport::TestCase setup do - $BUFFER = [] + Thread.current[:ajbuffer] = [] end test 'run queued job' do HelloJob.enqueue - assert_equal "David says hello", $BUFFER.pop + assert_equal "David says hello", Thread.current[:ajbuffer].pop end test 'run queued job with arguments' do HelloJob.enqueue "Jamie" - assert_equal "Jamie says hello", $BUFFER.pop + assert_equal "Jamie says hello", Thread.current[:ajbuffer].pop end test 'run queued job later' do @@ -26,13 +26,13 @@ class QueuingTest < ActiveSupport::TestCase skip end end - + test 'job returned by enqueue has the arguments available' do job = HelloJob.enqueue "Jamie" assert_equal [ "Jamie" ], job.arguments end - + test 'job returned by enqueue_at has the timestamp available' do begin job = HelloJob.enqueue_at Time.utc(2014, 1, 1) diff --git a/activejob/test/cases/rescue_test.rb b/activejob/test/cases/rescue_test.rb index 3d4831bc62..4fbd27fe6c 100644 --- a/activejob/test/cases/rescue_test.rb +++ b/activejob/test/cases/rescue_test.rb @@ -5,13 +5,13 @@ require 'active_support/core_ext/object/inclusion' class RescueTest < ActiveSupport::TestCase setup do - $BUFFER = [] + Thread.current[:ajbuffer] = [] end test 'rescue perform exception with retry' do job = RescueJob.new job.execute(SecureRandom.uuid, "david") - assert_equal [ "rescued from ArgumentError", "performed beautifully" ], $BUFFER + assert_equal [ "rescued from ArgumentError", "performed beautifully" ], Thread.current[:ajbuffer] end test 'let through unhandled perform exception' do diff --git a/activejob/test/jobs/gid_job.rb b/activejob/test/jobs/gid_job.rb index c69e38d3cc..eeb34c8a87 100644 --- a/activejob/test/jobs/gid_job.rb +++ b/activejob/test/jobs/gid_job.rb @@ -1,6 +1,6 @@ class GidJob < ActiveJob::Base def perform(person) - $BUFFER << "Person with ID: #{person.id}" + Thread.current[:ajbuffer] << "Person with ID: #{person.id}" end end - \ No newline at end of file + diff --git a/activejob/test/jobs/hello_job.rb b/activejob/test/jobs/hello_job.rb index 25441dd0c8..cb067bbe69 100644 --- a/activejob/test/jobs/hello_job.rb +++ b/activejob/test/jobs/hello_job.rb @@ -1,5 +1,5 @@ class HelloJob < ActiveJob::Base def perform(greeter = "David") - $BUFFER << "#{greeter} says hello" + Thread.current[:ajbuffer] << "#{greeter} says hello" end end diff --git a/activejob/test/jobs/rescue_job.rb b/activejob/test/jobs/rescue_job.rb index acf2b81515..e42de4876e 100644 --- a/activejob/test/jobs/rescue_job.rb +++ b/activejob/test/jobs/rescue_job.rb @@ -2,7 +2,7 @@ class RescueJob < ActiveJob::Base class OtherError < StandardError; end rescue_from(ArgumentError) do - $BUFFER << "rescued from ArgumentError" + Thread.current[:ajbuffer] << "rescued from ArgumentError" arguments[0] = "DIFFERENT!" retry_now end @@ -14,7 +14,7 @@ class RescueJob < ActiveJob::Base when "other" raise OtherError else - $BUFFER << "performed beautifully" + Thread.current[:ajbuffer] << "performed beautifully" end end end -- cgit v1.2.3 From 0e4e496a5775adf4d9204aaa38de916f74623ea9 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Sat, 16 Aug 2014 00:30:20 +0300 Subject: Fixed indentation --- actionmailer/lib/action_mailer/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index a229d9142c..9aae14ec8c 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -549,7 +549,7 @@ module ActionMailer def method_missing(method_name, *args) # :nodoc: if action_methods.include?(method_name.to_s) - MessageDelivery.new(self, method_name, *args) + MessageDelivery.new(self, method_name, *args) else super end -- cgit v1.2.3 From b2cabb7aceac9e2db0a9cc4fea8a4ef50d4ea132 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Sat, 16 Aug 2014 01:31:39 +0300 Subject: Added docs for AJ::Callbacks; Added AJ to docs build map --- activejob/lib/active_job/callbacks.rb | 112 ++++++++++++++++++++++++++++++++-- railties/lib/rails/api/task.rb | 7 +++ 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/activejob/lib/active_job/callbacks.rb b/activejob/lib/active_job/callbacks.rb index c69e4a3b55..af92031bc9 100644 --- a/activejob/lib/active_job/callbacks.rb +++ b/activejob/lib/active_job/callbacks.rb @@ -1,40 +1,144 @@ require 'active_support/callbacks' module ActiveJob + # = Active Job Callbacks + # + # Active Job provides hooks during the lifecycle of a job. Callbacks allows you to trigger + # logic during the lifecycle of a job. Available callbacks: + # + # * before_enqueue + # * around_enqueue + # * after_enqueue + # * before_perform + # * around_perform + # * after_perform + # module Callbacks extend ActiveSupport::Concern include ActiveSupport::Callbacks - + included do define_callbacks :perform define_callbacks :enqueue end - + module ClassMethods + # Defines a callback that will get called right before the + # job's perform method is executed. + # + # class VideoProcessJob < ActiveJob::Base + # queue_as :default + # + # before_perform do |job| + # UserMailer.notify_video_started_processing(job.arguments.first) + # end + # + # def perform(video_id) + # Video.find(video_id).process + # end + # end + # def before_perform(*filters, &blk) set_callback(:perform, :before, *filters, &blk) end + # Defines a callback that will get called right after the + # job's perform method has finished. + # + # class VideoProcessJob < ActiveJob::Base + # queue_as :default + # + # after_perform do |job| + # UserMailer.notify_video_processed(job.arguments.first) + # end + # + # def perform(video_id) + # Video.find(video_id).process + # end + # end + # def after_perform(*filters, &blk) set_callback(:perform, :after, *filters, &blk) end + # Defines a callback that will get called around the job's perform method. + # + # class VideoProcessJob < ActiveJob::Base + # queue_as :default + # + # around_perform do |job, block| + # UserMailer.notify_video_started_processing(job.arguments.first) + # block.call + # UserMailer.notify_video_processed(job.arguments.first) + # end + # + # def perform(video_id) + # Video.find(video_id).process + # end + # end + # def around_perform(*filters, &blk) set_callback(:perform, :around, *filters, &blk) end - + # Defines a callback that will get called right before the + # job is enqueued. + # + # class VideoProcessJob < ActiveJob::Base + # queue_as :default + # + # before_enqueue do |job| + # $statsd.increment "enqueue-video-job.try" + # end + # + # def perform(video_id) + # Video.find(video_id).process + # end + # end + # def before_enqueue(*filters, &blk) set_callback(:enqueue, :before, *filters, &blk) end + # Defines a callback that will get called right after the + # job is enqueued. + # + # class VideoProcessJob < ActiveJob::Base + # queue_as :default + # + # after_enqueue do |job| + # $statsd.increment "enqueue-video-job.success" + # end + # + # def perform(video_id) + # Video.find(video_id).process + # end + # end + # def after_enqueue(*filters, &blk) set_callback(:enqueue, :after, *filters, &blk) end + # Defines a callback that will get called before and after the + # job is enqueued. + # + # class VideoProcessJob < ActiveJob::Base + # queue_as :default + # + # around_enqueue do |job, block| + # $statsd.time "video-job.process" do + # block.call + # end + # end + # + # def perform(video_id) + # Video.find(video_id).process + # end + # end + # def around_enqueue(*filters, &blk) set_callback(:enqueue, :around, *filters, &blk) end end end -end \ No newline at end of file +end diff --git a/railties/lib/rails/api/task.rb b/railties/lib/rails/api/task.rb index 3e32576040..4d49244807 100644 --- a/railties/lib/rails/api/task.rb +++ b/railties/lib/rails/api/task.rb @@ -50,6 +50,13 @@ module Rails ) }, + 'activejob' => { + :include => %w( + README.md + lib/active_job/**/*.rb + ) + }, + 'railties' => { :include => %w( README.rdoc -- cgit v1.2.3 From de2fab5f86a7b6da2e05069c87d3e34aa0fc27ee Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 16 Aug 2014 07:29:19 +0900 Subject: Unused ivar in a test suite --- activesupport/test/multibyte_chars_test.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index 659fceb852..8158a6efc7 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -103,7 +103,6 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase @chars = UNICODE_STRING.dup.mb_chars # Ruby 1.9 only supports basic whitespace @whitespace = "\n\t " - @byte_order_mark = [65279].pack('U') end def test_split_should_return_an_array_of_chars_instances -- cgit v1.2.3 From 5643cace2d39486829078b85dcac37bee34090f2 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 16 Aug 2014 07:51:13 +0900 Subject: Avoid polluting String class globally in a test case --- activesupport/test/multibyte_chars_test.rb | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index 8158a6efc7..22954a5590 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -3,13 +3,6 @@ require 'abstract_unit' require 'multibyte_test_helpers' require 'active_support/core_ext/string/multibyte' -class String - def __method_for_multibyte_testing_with_integer_result; 1; end - def __method_for_multibyte_testing; 'result'; end - def __method_for_multibyte_testing!; 'result'; end - def __method_for_multibyte_testing_that_returns_nil!; end -end - class MultibyteCharsTest < ActiveSupport::TestCase include MultibyteTestHelpers @@ -24,6 +17,8 @@ class MultibyteCharsTest < ActiveSupport::TestCase end def test_should_allow_method_calls_to_string + @chars.wrapped_string.singleton_class.class_eval { def __method_for_multibyte_testing; 'result'; end } + assert_nothing_raised do @chars.__method_for_multibyte_testing end @@ -33,16 +28,22 @@ class MultibyteCharsTest < ActiveSupport::TestCase end def test_forwarded_method_calls_should_return_new_chars_instance + @chars.wrapped_string.singleton_class.class_eval { def __method_for_multibyte_testing; 'result'; end } + assert_kind_of @proxy_class, @chars.__method_for_multibyte_testing assert_not_equal @chars.object_id, @chars.__method_for_multibyte_testing.object_id end def test_forwarded_bang_method_calls_should_return_the_original_chars_instance_when_result_is_not_nil + @chars.wrapped_string.singleton_class.class_eval { def __method_for_multibyte_testing!; 'result'; end } + assert_kind_of @proxy_class, @chars.__method_for_multibyte_testing! assert_equal @chars.object_id, @chars.__method_for_multibyte_testing!.object_id end def test_forwarded_bang_method_calls_should_return_nil_when_result_is_nil + @chars.wrapped_string.singleton_class.class_eval { def __method_for_multibyte_testing_that_returns_nil!; end } + assert_nil @chars.__method_for_multibyte_testing_that_returns_nil! end @@ -54,7 +55,11 @@ class MultibyteCharsTest < ActiveSupport::TestCase end def test_forwarded_method_with_non_string_result_should_be_returned_vertabim - assert_equal ''.__method_for_multibyte_testing_with_integer_result, @chars.__method_for_multibyte_testing_with_integer_result + str = '' + str.singleton_class.class_eval { def __method_for_multibyte_testing_with_integer_result; 1; end } + @chars.wrapped_string.singleton_class.class_eval { def __method_for_multibyte_testing_with_integer_result; 1; end } + + assert_equal str.__method_for_multibyte_testing_with_integer_result, @chars.__method_for_multibyte_testing_with_integer_result end def test_should_concatenate -- cgit v1.2.3 From cc9d1c52012cd9d7c3f55fd3fe4032080f83b4f2 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sat, 16 Aug 2014 10:19:42 +0900 Subject: Avoid mutating the constants in a test case --- activesupport/test/multibyte_chars_test.rb | 4 +--- activesupport/test/multibyte_test_helpers.rb | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index 22954a5590..73f640e8e5 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -8,7 +8,7 @@ class MultibyteCharsTest < ActiveSupport::TestCase def setup @proxy_class = ActiveSupport::Multibyte::Chars - @chars = @proxy_class.new UNICODE_STRING + @chars = @proxy_class.new UNICODE_STRING.dup end def test_wraps_the_original_string @@ -50,8 +50,6 @@ class MultibyteCharsTest < ActiveSupport::TestCase def test_methods_are_forwarded_to_wrapped_string_for_byte_strings original_encoding = BYTE_STRING.encoding assert_equal BYTE_STRING.length, BYTE_STRING.mb_chars.length - ensure - BYTE_STRING.force_encoding(original_encoding) end def test_forwarded_method_with_non_string_result_should_be_returned_vertabim diff --git a/activesupport/test/multibyte_test_helpers.rb b/activesupport/test/multibyte_test_helpers.rb index fdbe2f4350..90af2dadd4 100644 --- a/activesupport/test/multibyte_test_helpers.rb +++ b/activesupport/test/multibyte_test_helpers.rb @@ -1,9 +1,9 @@ # encoding: utf-8 module MultibyteTestHelpers - UNICODE_STRING = 'こにちわ' - ASCII_STRING = 'ohayo' - BYTE_STRING = "\270\236\010\210\245".force_encoding("ASCII-8BIT") + UNICODE_STRING = 'こにちわ'.freeze + ASCII_STRING = 'ohayo'.freeze + BYTE_STRING = "\270\236\010\210\245".force_encoding("ASCII-8BIT").freeze def chars(str) ActiveSupport::Multibyte::Chars.new(str) -- cgit v1.2.3 From cf75464d26687b876335d116e0ce69d95fa3eb3b Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Sat, 16 Aug 2014 15:28:09 +0300 Subject: ActiveJob guides improvements [ci skip] --- guides/source/active_job_basics.md | 137 ++++++++----------------------------- 1 file changed, 29 insertions(+), 108 deletions(-) diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index c100448a48..ae5d21d546 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -27,9 +27,9 @@ The Purpose of the Active Job The main point is to ensure that all Rails apps will have a job infrastructure in place, even if it's in the form of an "immediate runner". We can then have framework features and other gems build on top of that, without having to -worry about API differences between Delayed Job and Resque. Picking your -queuing backend becomes more of an operational concern, then. And you'll -be able to switch between them without having to rewrite your jobs. +worry about API differences between various job runners such as Delayed Job +and Resque. Picking your queuing backend becomes more of an operational concern, +then. And you'll be able to switch between them without having to rewrite your jobs. Creating a Job @@ -112,110 +112,18 @@ Active Job has adapters for the following queueing backends: #### Backends Features - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 AsyncQueuesDelayedPrioritiesTimeoutRetries
BackburnerYesYesYesYesJobGlobal
Delayed JobYesYesYesJobGlobalGlobal
QueYesYesYesJobNoJob
Queue ClassicYesYesGemNoNoNo
ResqueYesYesGemQueueGlobal?
SidekiqYesYesYesQueueNoJob
SneakersYesYesNoQueueQueueNo
Sucker PunchYesYesYesNoNoNo
Active JobYesYesWIPNoNoNo
Active Job InlineNoYesN/AN/AN/AN/A
- +| | Async | Queues | Delayed | Priorities | Timeout | Retries | +|-----------------------|-------|---------|---------|-------------|---------|---------| +| **Backburner** | Yes | Yes | Yes | Yes | Job | Global | +| **Delayed Job** | Yes | Yes | Yes | Job | Global | Global | +| **Que** | Yes | Yes | Yes | Job | No | Job | +| **Queue Classic** | Yes | Yes | Gem | No | No | No | +| **Resque** | Yes | Yes | Gem | Queue | Global | ? | +| **Sidekiq** | Yes | Yes | Yes | Queue | No | Job | +| **Sneakers** | Yes | Yes | No | Queue | Queue | No | +| **Sucker Punch** | Yes | Yes | Yes | No | No | No | +| **Active Job** | Yes | Yes | WIP | No | No | No | +| **Active Job Inline** | No | Yes | N/A | N/A | N/A | N/A | ### Change Backends @@ -235,7 +143,7 @@ to run on a specific queue: ```ruby class GuestsCleanupJob < ActiveJob::Base - queue_as :low_prio + queue_as :low_priority #.... end ``` @@ -279,7 +187,20 @@ class GuestsCleanupJob < ActiveJob::Base # Do something later end end +``` + +ActionMailer +------------ +One of the most common jobs in a modern web application is sending emails outside +of the request-response cycle, so the user doesn't have to wait on it. Active Job +is integrated with Action Mailer so you can easily send emails async: + +```ruby +# Instead of the classic +UserMailer.welcome(@user).deliver +# use #deliver later to send the email async +UserMailer.welcome(@user).deliver_later ``` GlobalID -- cgit v1.2.3 From 5e0f9e40a3523940a23b644e93231385d1760c26 Mon Sep 17 00:00:00 2001 From: Tom Kadwill Date: Sat, 16 Aug 2014 17:10:40 +0100 Subject: [ci skip] re-worded section on CookieStore to make it more readable. --- guides/source/security.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/guides/source/security.md b/guides/source/security.md index f1db81458c..125dd82666 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -118,9 +118,9 @@ It works like this: * A user receives credits, the amount is stored in a session (which is a bad idea anyway, but we'll do this for demonstration purposes). * The user buys something. -* Their new, lower credit will be stored in the session. -* The dark side of the user forces them to take the cookie from the first step (which they copied) and replace the current cookie in the browser. -* The user has their credit back. +* The new adjusted credit value is stored in the session. +* The user takes the cookie from the first step (which they previously copied) and replaces the current cookie in the browser. +* The user has their original credit back. Including a nonce (a random value) in the session solves replay attacks. A nonce is valid only once, and the server has to keep track of all the valid nonces. It gets even more complicated if you have several application servers (mongrels). Storing nonces in a database table would defeat the entire purpose of CookieStore (avoiding accessing the database). -- cgit v1.2.3 From 877ea784e4cd0d539bdfbd15839ae3d28169b156 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Sat, 12 Jul 2014 18:30:49 -0600 Subject: Implement `_was` and `changes` for in-place mutations of AR attributes --- activemodel/lib/active_model/dirty.rb | 19 +++++++++++++--- .../associations/has_many_association.rb | 2 +- activerecord/lib/active_record/attribute.rb | 8 +++++-- .../lib/active_record/attribute_methods/dirty.rb | 25 ++++++++++++---------- activerecord/lib/active_record/enum.rb | 4 ++-- activerecord/lib/active_record/persistence.rb | 2 +- activerecord/lib/active_record/timestamp.rb | 2 +- activerecord/test/cases/dirty_test.rb | 21 ++++++++++++++++++ 8 files changed, 62 insertions(+), 21 deletions(-) diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb index d11243c4c0..ed00d8cf12 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -114,7 +114,7 @@ module ActiveModel include ActiveModel::AttributeMethods included do - attribute_method_suffix '_changed?', '_change', '_will_change!', '_was' + attribute_method_suffix '_changed?', '_change', '_will_change!', '_was', '_was=' attribute_method_affix prefix: 'reset_', suffix: '!' attribute_method_affix prefix: 'restore_', suffix: '!' end @@ -180,13 +180,26 @@ module ActiveModel attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr) end + # Handle *_was= for +method_missing+ + def attribute_was=(attr, old_value) + attributes_changed_by_setter[attr] = old_value + end + alias_method :set_attribute_was, :attribute_was= + # Restore all previous data of the provided attributes. def restore_attributes(attributes = changed) attributes.each { |attr| restore_attribute! attr } end + # Remove changes information for the provided attributes. + def clear_attribute_changes(attributes) + attributes_changed_by_setter.except!(*attributes) + end + private + alias_method :attributes_changed_by_setter, :changed_attributes # :nodoc: + # Removes current changes and makes them accessible through +previous_changes+. def changes_applied # :doc: @previously_changed = changes @@ -219,7 +232,7 @@ module ActiveModel rescue TypeError, NoMethodError end - changed_attributes[attr] = value + set_attribute_was(attr, value) end # Handle reset_*! for +method_missing+. @@ -233,7 +246,7 @@ module ActiveModel def restore_attribute!(attr) if attribute_changed?(attr) __send__("#{attr}=", changed_attributes[attr]) - changed_attributes.delete(attr) + clear_attribute_changes([attr]) end end end diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 79c3d2b0f5..cf59699228 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -103,7 +103,7 @@ module ActiveRecord if has_cached_counter?(reflection) counter = cached_counter_attribute_name(reflection) owner[counter] += difference - owner.changed_attributes.delete(counter) # eww + owner.clear_attribute_changes([counter]) # eww end end diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb index 15cbbcff68..8cc1904575 100644 --- a/activerecord/lib/active_record/attribute.rb +++ b/activerecord/lib/active_record/attribute.rb @@ -30,10 +30,14 @@ module ActiveRecord def value # `defined?` is cheaper than `||=` when we get back falsy values - @value = type_cast(value_before_type_cast) unless defined?(@value) + @value = original_value unless defined?(@value) @value end + def original_value + type_cast(value_before_type_cast) + end + def value_for_database type.type_cast_for_database(value) end @@ -54,7 +58,7 @@ module ActiveRecord self.class.from_database(name, value, type) end - def type_cast + def type_cast(*) raise NotImplementedError end diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index b58295a106..d3f4e51c33 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -51,14 +51,6 @@ module ActiveRecord super | changed_in_place end - def attribute_changed?(attr_name, options = {}) - result = super - # We can't change "from" something in place. Only setters can define - # "from" and "to" - result ||= changed_in_place?(attr_name) unless options.key?(:from) - result - end - def changes_applied super store_original_raw_attributes @@ -69,12 +61,16 @@ module ActiveRecord original_raw_attributes.clear end + def changed_attributes + super.reverse_merge(attributes_changed_in_place).freeze + end + private def calculate_changes_from_defaults @changed_attributes = nil self.class.column_defaults.each do |attr, orig_value| - changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value) + set_attribute_was(attr, orig_value) if _field_changed?(attr, orig_value) end end @@ -100,9 +96,9 @@ module ActiveRecord def save_changed_attribute(attr, old_value) if attribute_changed?(attr) - changed_attributes.delete(attr) unless _field_changed?(attr, old_value) + clear_attribute_changes(attr) unless _field_changed?(attr, old_value) else - changed_attributes[attr] = old_value if _field_changed?(attr, old_value) + set_attribute_was(attr, old_value) if _field_changed?(attr, old_value) end end @@ -132,6 +128,13 @@ module ActiveRecord @attributes[attr].changed_from?(old_value) end + def attributes_changed_in_place + changed_in_place.each_with_object({}) do |attr_name, h| + orig = @attributes[attr_name].original_value + h[attr_name] = orig + end + end + def changed_in_place self.class.attribute_names.select do |attr_name| changed_in_place?(attr_name) diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index f0ee433d0b..5958373e88 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -145,11 +145,11 @@ module ActiveRecord value = read_attribute(attr_name) if attribute_changed?(attr_name) if mapping[old] == value - changed_attributes.delete(attr_name) + clear_attribute_changes([attr_name]) end else if old != value - changed_attributes[attr_name] = mapping.key old + set_attribute_was(attr_name, mapping.key(old)) end end else diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 51b1931ed5..755ff2b2f1 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -466,7 +466,7 @@ module ActiveRecord changes[self.class.locking_column] = increment_lock if locking_enabled? - changed_attributes.except!(*changes.keys) + clear_attribute_changes(changes.keys) primary_key = self.class.primary_key self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1 else diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 417cd61f0c..936a18d99a 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -114,7 +114,7 @@ module ActiveRecord def clear_timestamp_attributes all_timestamp_attributes_in_model.each do |attribute_name| self[attribute_name] = nil - changed_attributes.delete(attribute_name) + clear_attribute_changes([attribute_name]) end end end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 69a7f25213..0c77eedb52 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -661,6 +661,27 @@ class DirtyTest < ActiveRecord::TestCase assert_not model.foo_changed? end + test "in place mutation detection" do + pirate = Pirate.create!(catchphrase: "arrrr") + pirate.catchphrase << " matey!" + + assert pirate.catchphrase_changed? + expected_changes = { + "catchphrase" => ["arrrr", "arrrr matey!"] + } + assert_equal(expected_changes, pirate.changes) + assert_equal("arrrr", pirate.catchphrase_was) + assert pirate.catchphrase_changed?(from: "arrrr") + assert_not pirate.catchphrase_changed?(from: "anything else") + assert pirate.changed_attributes.include?(:catchphrase) + + pirate.save! + pirate.reload + + assert_equal "arrrr matey!", pirate.catchphrase + assert_not pirate.changed? + end + private def with_partial_writes(klass, on = true) old = klass.partial_writes? -- cgit v1.2.3 From 008f3da3835e47f719ba6820703ba404ff363640 Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Sat, 16 Aug 2014 22:55:01 -0700 Subject: Don't expose these new APIs yet (added in 877ea78 / #16189) WARNING: don't use them! They might change or go away between future beta/RC/ patch releases! Also added a CHANGELOG entry for this. --- activemodel/lib/active_model/dirty.rb | 29 +++++++++++----------- activerecord/CHANGELOG.md | 5 ++++ .../associations/has_many_association.rb | 2 +- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb index ed00d8cf12..ca04f48c1c 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -114,7 +114,7 @@ module ActiveModel include ActiveModel::AttributeMethods included do - attribute_method_suffix '_changed?', '_change', '_will_change!', '_was', '_was=' + attribute_method_suffix '_changed?', '_change', '_will_change!', '_was' attribute_method_affix prefix: 'reset_', suffix: '!' attribute_method_affix prefix: 'restore_', suffix: '!' end @@ -180,26 +180,13 @@ module ActiveModel attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr) end - # Handle *_was= for +method_missing+ - def attribute_was=(attr, old_value) - attributes_changed_by_setter[attr] = old_value - end - alias_method :set_attribute_was, :attribute_was= - # Restore all previous data of the provided attributes. def restore_attributes(attributes = changed) attributes.each { |attr| restore_attribute! attr } end - # Remove changes information for the provided attributes. - def clear_attribute_changes(attributes) - attributes_changed_by_setter.except!(*attributes) - end - private - alias_method :attributes_changed_by_setter, :changed_attributes # :nodoc: - # Removes current changes and makes them accessible through +previous_changes+. def changes_applied # :doc: @previously_changed = changes @@ -249,5 +236,19 @@ module ActiveModel clear_attribute_changes([attr]) end end + + # This is necessary because `changed_attributes` might be overridden in + # other implemntations (e.g. in `ActiveRecord`) + alias_method :attributes_changed_by_setter, :changed_attributes # :nodoc: + + # Force an attribute to have a particular "before" value + def set_attribute_was(attr, old_value) + attributes_changed_by_setter[attr] = old_value + end + + # Remove changes information for the provided attributes. + def clear_attribute_changes(attributes) + attributes_changed_by_setter.except!(*attributes) + end end end diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 02660ae4c2..495e9c77d7 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* `*_was` and `changes` now work correctly for in-place attribute changes as + well. + + *Sean Griffin* + * Fix regression on after_commit that didnt fire when having nested transactions. Fixes #16425 diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index cf59699228..1413efaf7f 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -103,7 +103,7 @@ module ActiveRecord if has_cached_counter?(reflection) counter = cached_counter_attribute_name(reflection) owner[counter] += difference - owner.clear_attribute_changes([counter]) # eww + owner.send(:clear_attribute_changes, counter) # eww end end -- cgit v1.2.3 From 2ba1670f378242438665b43819e41b31ca856bcc Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Sun, 17 Aug 2014 22:42:03 +0900 Subject: [ci skip] Fix sample code in Layouts and Rendering guide --- guides/source/layouts_and_rendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index f00f7bca1b..8b37b92139 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -903,7 +903,7 @@ You can also specify multiple videos to play by passing an array of videos to th This will produce: ```erb - + ``` #### Linking to Audio Files with the `audio_tag` -- cgit v1.2.3 From 6c96602bc1480b41f5fd20aef46fc70bcf582aab Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 16 Aug 2014 15:06:20 -0700 Subject: When your templates change, browser caches bust automatically. New default: the template digest is automatically included in your ETags. When you call `fresh_when @post`, the digest for `posts/show.html.erb` is mixed in so future changes to the HTML will blow HTTP caches for you. This makes it easy to HTTP-cache many more of your actions. If you render a different template, you can now pass the `:template` option to include its digest instead: fresh_when @post, template: 'widgets/show' Pass `template: false` to skip the lookup. To turn this off entirely, set: config.action_controller.etag_with_template_digest = false --- actionpack/CHANGELOG.md | 18 ++++++++ actionpack/lib/action_controller.rb | 1 + actionpack/lib/action_controller/base.rb | 1 + .../lib/action_controller/metal/conditional_get.rb | 37 +++++++++++++--- .../metal/etag_with_template_digest.rb | 50 ++++++++++++++++++++++ actionpack/test/controller/live_stream_test.rb | 2 +- actionpack/test/controller/render_test.rb | 36 +++++++++++++++- 7 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 actionpack/lib/action_controller/metal/etag_with_template_digest.rb diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 7c460fbaef..90fb99ad3a 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,21 @@ +* When your templates change, browser caches bust automatically. + + New default: the template digest is automatically included in your ETags. + When you call `fresh_when @post`, the digest for `posts/show.html.erb` + is mixed in so future changes to the HTML will blow HTTP caches for you. + This makes it easy to HTTP-cache many more of your actions. + + If you render a different template, you can now pass the `:template` + option to include its digest instead: + + fresh_when @post, template: 'widgets/show' + + Pass `template: false` to skip the lookup. To turn this off entirely, set: + + config.action_controller.etag_with_template_digest = false + + *Jeremy Kemper* + * Remove deprecated `AbstractController::Helpers::ClassMethods::MissingHelperError` in favor of `AbstractController::Helpers::MissingHelperError`. diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 50bc26a80f..7f1aeafe8b 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -17,6 +17,7 @@ module ActionController autoload :ConditionalGet autoload :Cookies autoload :DataStreaming + autoload :EtagWithTemplateDigest autoload :Flash autoload :ForceSSL autoload :Head diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index e6fe6b0b00..7bbf938987 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -213,6 +213,7 @@ module ActionController Rendering, Renderers::All, ConditionalGet, + EtagWithTemplateDigest, RackDelegation, Caching, MimeResponds, diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index 6e0cd51d8b..a93727df90 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -41,6 +41,11 @@ module ActionController # * :last_modified. # * :public By default the Cache-Control header is private, set this to # +true+ if you want your application to be cachable by other devices (proxy caches). + # * :template By default, the template digest for the current + # controller/action is included in ETags. If the action renders a + # different template, you can include its digest instead. If the action + # doesn't render a template at all, you can pass template: false + # to skip any attempt to check for a template digest. # # === Example: # @@ -66,18 +71,24 @@ module ActionController # @article = Article.find(params[:id]) # fresh_when(@article, public: true) # end + # + # When rendering a different template than the default controller/action + # style, you can indicate which digest to include in the ETag: + # + # before_action { fresh_when @article, template: 'widgets/show' } + # def fresh_when(record_or_options, additional_options = {}) if record_or_options.is_a? Hash options = record_or_options - options.assert_valid_keys(:etag, :last_modified, :public) + options.assert_valid_keys(:etag, :last_modified, :public, :template) else record = record_or_options options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options) end - response.etag = combine_etags(options[:etag]) if options[:etag] - response.last_modified = options[:last_modified] if options[:last_modified] - response.cache_control[:public] = true if options[:public] + response.etag = combine_etags(options) if options[:etag] || options[:template] + response.last_modified = options[:last_modified] if options[:last_modified] + response.cache_control[:public] = true if options[:public] head :not_modified if request.fresh?(response) end @@ -93,6 +104,11 @@ module ActionController # * :last_modified. # * :public By default the Cache-Control header is private, set this to # +true+ if you want your application to be cachable by other devices (proxy caches). + # * :template By default, the template digest for the current + # controller/action is included in ETags. If the action renders a + # different template, you can include its digest instead. If the action + # doesn't render a template at all, you can pass template: false + # to skip any attempt to check for a template digest. # # === Example: # @@ -133,6 +149,14 @@ module ActionController # end # end # end + # + # When rendering a different template than the default controller/action + # style, you can indicate which digest to include in the ETag: + # + # def show + # super if stale? @article, template: 'widgets/show' + # end + # def stale?(record_or_options, additional_options = {}) fresh_when(record_or_options, additional_options) !request.fresh?(response) @@ -168,8 +192,9 @@ module ActionController end private - def combine_etags(etag) - [ etag, *etaggers.map { |etagger| instance_exec(&etagger) }.compact ] + def combine_etags(options) + etags = etaggers.map { |etagger| instance_exec(options, &etagger) }.compact + etags.unshift options[:etag] end end end diff --git a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb new file mode 100644 index 0000000000..3ca0c6837a --- /dev/null +++ b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb @@ -0,0 +1,50 @@ +module ActionController + # When our views change, they should bubble up into HTTP cache freshness + # and bust browser caches. So the template digest for the current action + # is automatically included in the ETag. + # + # Enabled by default for apps that use Action View. Disable by setting + # + # config.action_controller.etag_with_template_digest = false + # + # Override the template to digest by passing `:template` to `fresh_when` + # and `stale?` calls. For example: + # + # # We're going to render widgets/show, not posts/show + # fresh_when @post, template: 'widgets/show' + # + # # We're not going to render a template, so omit it from the ETag. + # fresh_when @post, template: false + # + module EtagWithTemplateDigest + extend ActiveSupport::Concern + + include ActionController::ConditionalGet + + included do + class_attribute :etag_with_template_digest + self.etag_with_template_digest = true + + ActiveSupport.on_load :action_view, yield: true do |action_view_base| + etag do |options| + determine_template_etag(options) if etag_with_template_digest + end + end + end + + private + def determine_template_etag(options) + if template = pick_template_for_etag(options) + lookup_and_digest_template(template) + end + end + + def pick_template_for_etag(options) + options.fetch(:template) { "#{controller_name}/#{action_name}" } + end + + def lookup_and_digest_template(template) + ActionView::Digestor.digest name: template, finder: lookup_context + end + end +end diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb index 0500b7c789..7fd1276e98 100644 --- a/actionpack/test/controller/live_stream_test.rb +++ b/actionpack/test/controller/live_stream_test.rb @@ -162,7 +162,7 @@ module ActionController end def with_stale - render :text => 'stale' if stale?(:etag => "123") + render text: 'stale' if stale?(etag: "123", template: false) end def exception_in_view diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 9926130c02..b036b6c08e 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -10,11 +10,17 @@ class TestControllerWithExtraEtags < ActionController::Base etag { nil } def fresh - render text: "stale" if stale?(etag: '123') + render text: "stale" if stale?(etag: '123', template: false) end def array - render text: "stale" if stale?(etag: %w(1 2 3)) + render text: "stale" if stale?(etag: %w(1 2 3), template: false) + end + + def with_template + if stale? template: 'test/hello_world' + render text: 'stale' + end end end @@ -409,6 +415,32 @@ class EtagRenderTest < ActionController::TestCase assert_response :success end + def test_etag_reflects_template_digest + get :with_template + assert_response :ok + assert_not_nil etag = @response.etag + + request.if_none_match = etag + get :with_template + assert_response :not_modified + + # Modify the template digest + path = File.expand_path('../../fixtures/test/hello_world.erb', __FILE__) + old = File.read(path) + + begin + File.write path, 'foo' + ActionView::Digestor.cache.clear + + request.if_none_match = etag + get :with_template + assert_response :ok + assert_not_equal etag, @response.etag + ensure + File.write path, old + end + end + def etag(record) Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(record)).inspect end -- cgit v1.2.3 From eb714d4460c39c8fb6d853d84d112aa9aa1ad63d Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Sun, 17 Aug 2014 13:16:01 -0300 Subject: Allow usage of bundle local config for rack by specifying the branch [ci skip] --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 04cef17458..64b0a31cf4 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ gemspec # ensure correct loading order gem 'mocha', '~> 0.14', require: false -gem 'rack', github: 'rack/rack' +gem 'rack', github: 'rack/rack', branch: 'master' gem 'rack-cache', '~> 1.2' gem 'jquery-rails', '~> 3.1.0' gem 'turbolinks', github: 'rails/turbolinks', branch: 'master' -- cgit v1.2.3 From ee77770d57de9da87b05a2fe84b9d46ec6852c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 16 Aug 2014 16:24:08 -0400 Subject: Move respond_with to the responders gem respond_with (and consequently the class-level respond_to) are being removed from Rails. Instead of moving it to a 3rd library, the functionality will be moved to responders gem (at github.com/plataformatec/responders) which already provides some responders extensions. --- actionpack/CHANGELOG.md | 5 + .../lib/action_controller/metal/mime_responds.rb | 234 +------ .../lib/action_controller/metal/responder.rb | 297 --------- .../test/controller/mime/respond_with_test.rb | 737 --------------------- 4 files changed, 8 insertions(+), 1265 deletions(-) delete mode 100644 actionpack/lib/action_controller/metal/responder.rb delete mode 100644 actionpack/test/controller/mime/respond_with_test.rb diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 90fb99ad3a..84ea8f4b22 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,8 @@ +* Move `respond_with` (and the class-level `respond_to`) to + the `responders` gem. + + *José Valim* + * When your templates change, browser caches bust automatically. New default: the template digest is automatically included in your ETags. diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 00e7e980f8..a533f03b6d 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -5,58 +5,6 @@ module ActionController #:nodoc: module MimeResponds extend ActiveSupport::Concern - included do - class_attribute :responder, :mimes_for_respond_to - self.responder = ActionController::Responder - clear_respond_to - end - - module ClassMethods - # Defines mime types that are rendered by default when invoking - # respond_with. - # - # respond_to :html, :xml, :json - # - # Specifies that all actions in the controller respond to requests - # for :html, :xml and :json. - # - # To specify on per-action basis, use :only and - # :except with an array of actions or a single action: - # - # respond_to :html - # respond_to :xml, :json, except: [ :edit ] - # - # This specifies that all actions respond to :html - # and all actions except :edit respond to :xml and - # :json. - # - # respond_to :json, only: :create - # - # This specifies that the :create action and no other responds - # to :json. - def respond_to(*mimes) - options = mimes.extract_options! - - only_actions = Array(options.delete(:only)).map(&:to_s) - except_actions = Array(options.delete(:except)).map(&:to_s) - - new = mimes_for_respond_to.dup - mimes.each do |mime| - mime = mime.to_sym - new[mime] = {} - new[mime][:only] = only_actions unless only_actions.empty? - new[mime][:except] = except_actions unless except_actions.empty? - end - self.mimes_for_respond_to = new.freeze - end - - # Clear all mime types in respond_to. - # - def clear_respond_to - self.mimes_for_respond_to = Hash.new.freeze - end - end - # Without web-service support, an action which collects the data for displaying a list of people # might look something like this: # @@ -253,189 +201,13 @@ module ActionController #:nodoc: def respond_to(*mimes, &block) raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given? - if collector = retrieve_collector_from_mimes(mimes, &block) - response = collector.response - response ? response.call : render({}) - end - end - - # For a given controller action, respond_with generates an appropriate - # response based on the mime-type requested by the client. - # - # If the method is called with just a resource, as in this example - - # - # class PeopleController < ApplicationController - # respond_to :html, :xml, :json - # - # def index - # @people = Person.all - # respond_with @people - # end - # end - # - # then the mime-type of the response is typically selected based on the - # request's Accept header and the set of available formats declared - # by previous calls to the controller's class method +respond_to+. Alternatively - # the mime-type can be selected by explicitly setting request.format in - # the controller. - # - # If an acceptable format is not identified, the application returns a - # '406 - not acceptable' status. Otherwise, the default response is to render - # a template named after the current action and the selected format, - # e.g. index.html.erb. If no template is available, the behavior - # depends on the selected format: - # - # * for an html response - if the request method is +get+, an exception - # is raised but for other requests such as +post+ the response - # depends on whether the resource has any validation errors (i.e. - # assuming that an attempt has been made to save the resource, - # e.g. by a +create+ action) - - # 1. If there are no errors, i.e. the resource - # was saved successfully, the response +redirect+'s to the resource - # i.e. its +show+ action. - # 2. If there are validation errors, the response - # renders a default action, which is :new for a - # +post+ request or :edit for +patch+ or +put+. - # Thus an example like this - - # - # respond_to :html, :xml - # - # def create - # @user = User.new(params[:user]) - # flash[:notice] = 'User was successfully created.' if @user.save - # respond_with(@user) - # end - # - # is equivalent, in the absence of create.html.erb, to - - # - # def create - # @user = User.new(params[:user]) - # respond_to do |format| - # if @user.save - # flash[:notice] = 'User was successfully created.' - # format.html { redirect_to(@user) } - # format.xml { render xml: @user } - # else - # format.html { render action: "new" } - # format.xml { render xml: @user } - # end - # end - # end - # - # * for a JavaScript request - if the template isn't found, an exception is - # raised. - # * for other requests - i.e. data formats such as xml, json, csv etc, if - # the resource passed to +respond_with+ responds to to_, - # the method attempts to render the resource in the requested format - # directly, e.g. for an xml request, the response is equivalent to calling - # render xml: resource. - # - # === Nested resources - # - # As outlined above, the +resources+ argument passed to +respond_with+ - # can play two roles. It can be used to generate the redirect url - # for successful html requests (e.g. for +create+ actions when - # no template exists), while for formats other than html and JavaScript - # it is the object that gets rendered, by being converted directly to the - # required format (again assuming no template exists). - # - # For redirecting successful html requests, +respond_with+ also supports - # the use of nested resources, which are supplied in the same way as - # in form_for and polymorphic_url. For example - - # - # def create - # @project = Project.find(params[:project_id]) - # @task = @project.comments.build(params[:task]) - # flash[:notice] = 'Task was successfully created.' if @task.save - # respond_with(@project, @task) - # end - # - # This would cause +respond_with+ to redirect to project_task_url - # instead of task_url. For request formats other than html or - # JavaScript, if multiple resources are passed in this way, it is the last - # one specified that is rendered. - # - # === Customizing response behavior - # - # Like +respond_to+, +respond_with+ may also be called with a block that - # can be used to overwrite any of the default responses, e.g. - - # - # def create - # @user = User.new(params[:user]) - # flash[:notice] = "User was successfully created." if @user.save - # - # respond_with(@user) do |format| - # format.html { render } - # end - # end - # - # The argument passed to the block is an ActionController::MimeResponds::Collector - # object which stores the responses for the formats defined within the - # block. Note that formats with responses defined explicitly in this way - # do not have to first be declared using the class method +respond_to+. - # - # Also, a hash passed to +respond_with+ immediately after the specified - # resource(s) is interpreted as a set of options relevant to all - # formats. Any option accepted by +render+ can be used, e.g. - # respond_with @people, status: 200 - # However, note that these options are ignored after an unsuccessful attempt - # to save a resource, e.g. when automatically rendering :new - # after a post request. - # - # Two additional options are relevant specifically to +respond_with+ - - # 1. :location - overwrites the default redirect location used after - # a successful html +post+ request. - # 2. :action - overwrites the default render action used after an - # unsuccessful html +post+ request. - def respond_with(*resources, &block) - if self.class.mimes_for_respond_to.empty? - raise "In order to use respond_with, first you need to declare the " \ - "formats your controller responds to in the class level." - end - - if collector = retrieve_collector_from_mimes(&block) - options = resources.size == 1 ? {} : resources.extract_options! - options = options.clone - options[:default_response] = collector.response - (options.delete(:responder) || self.class.responder).call(self, resources, options) - end - end - - protected - - # Collect mimes declared in the class method respond_to valid for the - # current action. - def collect_mimes_from_class_level #:nodoc: - action = action_name.to_s - - self.class.mimes_for_respond_to.keys.select do |mime| - config = self.class.mimes_for_respond_to[mime] - - if config[:except] - !config[:except].include?(action) - elsif config[:only] - config[:only].include?(action) - else - true - end - end - end - - # Returns a Collector object containing the appropriate mime-type response - # for the current request, based on the available responses defined by a block. - # In typical usage this is the block passed to +respond_with+ or +respond_to+. - # - # Sends :not_acceptable to the client and returns nil if no suitable format - # is available. - def retrieve_collector_from_mimes(mimes=nil, &block) #:nodoc: - mimes ||= collect_mimes_from_class_level collector = Collector.new(mimes, request.variant) block.call(collector) if block_given? - format = collector.negotiate_format(request) - if format + if format = collector.negotiate_format(request) _process_format(format) - collector + response = collector.response + response ? response.call : render({}) else raise ActionController::UnknownFormat end diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb deleted file mode 100644 index 5096558c67..0000000000 --- a/actionpack/lib/action_controller/metal/responder.rb +++ /dev/null @@ -1,297 +0,0 @@ -require 'active_support/json' - -module ActionController #:nodoc: - # Responsible for exposing a resource to different mime requests, - # usually depending on the HTTP verb. The responder is triggered when - # respond_with is called. The simplest case to study is a GET request: - # - # class PeopleController < ApplicationController - # respond_to :html, :xml, :json - # - # def index - # @people = Person.all - # respond_with(@people) - # end - # end - # - # When a request comes in, for example for an XML response, three steps happen: - # - # 1) the responder searches for a template at people/index.xml; - # - # 2) if the template is not available, it will invoke #to_xml on the given resource; - # - # 3) if the responder does not respond_to :to_xml, call #to_format on it. - # - # === Built-in HTTP verb semantics - # - # The default \Rails responder holds semantics for each HTTP verb. Depending on the - # content type, verb and the resource status, it will behave differently. - # - # Using \Rails default responder, a POST request for creating an object could - # be written as: - # - # def create - # @user = User.new(params[:user]) - # flash[:notice] = 'User was successfully created.' if @user.save - # respond_with(@user) - # end - # - # Which is exactly the same as: - # - # def create - # @user = User.new(params[:user]) - # - # respond_to do |format| - # if @user.save - # flash[:notice] = 'User was successfully created.' - # format.html { redirect_to(@user) } - # format.xml { render xml: @user, status: :created, location: @user } - # else - # format.html { render action: "new" } - # format.xml { render xml: @user.errors, status: :unprocessable_entity } - # end - # end - # end - # - # The same happens for PATCH/PUT and DELETE requests. - # - # === Nested resources - # - # You can supply nested resources as you do in form_for and polymorphic_url. - # Consider the project has many tasks example. The create action for - # TasksController would be like: - # - # def create - # @project = Project.find(params[:project_id]) - # @task = @project.tasks.build(params[:task]) - # flash[:notice] = 'Task was successfully created.' if @task.save - # respond_with(@project, @task) - # end - # - # Giving several resources ensures that the responder will redirect to - # project_task_url instead of task_url. - # - # Namespaced and singleton resources require a symbol to be given, as in - # polymorphic urls. If a project has one manager which has many tasks, it - # should be invoked as: - # - # respond_with(@project, :manager, @task) - # - # Note that if you give an array, it will be treated as a collection, - # so the following is not equivalent: - # - # respond_with [@project, :manager, @task] - # - # === Custom options - # - # respond_with also allows you to pass options that are forwarded - # to the underlying render call. Those options are only applied for success - # scenarios. For instance, you can do the following in the create method above: - # - # def create - # @project = Project.find(params[:project_id]) - # @task = @project.tasks.build(params[:task]) - # flash[:notice] = 'Task was successfully created.' if @task.save - # respond_with(@project, @task, status: 201) - # end - # - # This will return status 201 if the task was saved successfully. If not, - # it will simply ignore the given options and return status 422 and the - # resource errors. You can also override the location to redirect to: - # - # respond_with(@project, location: root_path) - # - # To customize the failure scenario, you can pass a block to - # respond_with: - # - # def create - # @project = Project.find(params[:project_id]) - # @task = @project.tasks.build(params[:task]) - # respond_with(@project, @task, status: 201) do |format| - # if @task.save - # flash[:notice] = 'Task was successfully created.' - # else - # format.html { render "some_special_template" } - # end - # end - # end - # - # Using respond_with with a block follows the same syntax as respond_to. - class Responder - attr_reader :controller, :request, :format, :resource, :resources, :options - - DEFAULT_ACTIONS_FOR_VERBS = { - :post => :new, - :patch => :edit, - :put => :edit - } - - def initialize(controller, resources, options={}) - @controller = controller - @request = @controller.request - @format = @controller.formats.first - @resource = resources.last - @resources = resources - @options = options - @action = options.delete(:action) - @default_response = options.delete(:default_response) - end - - delegate :head, :render, :redirect_to, :to => :controller - delegate :get?, :post?, :patch?, :put?, :delete?, :to => :request - - # Undefine :to_json and :to_yaml since it's defined on Object - undef_method(:to_json) if method_defined?(:to_json) - undef_method(:to_yaml) if method_defined?(:to_yaml) - - # Initializes a new responder and invokes the proper format. If the format is - # not defined, call to_format. - # - def self.call(*args) - new(*args).respond - end - - # Main entry point for responder responsible to dispatch to the proper format. - # - def respond - method = "to_#{format}" - respond_to?(method) ? send(method) : to_format - end - - # HTML format does not render the resource, it always attempt to render a - # template. - # - def to_html - default_render - rescue ActionView::MissingTemplate => e - navigation_behavior(e) - end - - # to_js simply tries to render a template. If no template is found, raises the error. - def to_js - default_render - end - - # All other formats follow the procedure below. First we try to render a - # template, if the template is not available, we verify if the resource - # responds to :to_format and display it. - # - def to_format - if get? || !has_errors? || response_overridden? - default_render - else - display_errors - end - rescue ActionView::MissingTemplate => e - api_behavior(e) - end - - protected - - # This is the common behavior for formats associated with browsing, like :html, :iphone and so forth. - def navigation_behavior(error) - if get? - raise error - elsif has_errors? && default_action - render :action => default_action - else - redirect_to navigation_location - end - end - - # This is the common behavior for formats associated with APIs, such as :xml and :json. - def api_behavior(error) - raise error unless resourceful? - raise MissingRenderer.new(format) unless has_renderer? - - if get? - display resource - elsif post? - display resource, :status => :created, :location => api_location - else - head :no_content - end - end - - # Checks whether the resource responds to the current format or not. - # - def resourceful? - resource.respond_to?("to_#{format}") - end - - # Returns the resource location by retrieving it from the options or - # returning the resources array. - # - def resource_location - options[:location] || resources - end - alias :navigation_location :resource_location - alias :api_location :resource_location - - # If a response block was given, use it, otherwise call render on - # controller. - # - def default_render - if @default_response - @default_response.call(options) - else - controller.default_render(options) - end - end - - # Display is just a shortcut to render a resource with the current format. - # - # display @user, status: :ok - # - # For XML requests it's equivalent to: - # - # render xml: @user, status: :ok - # - # Options sent by the user are also used: - # - # respond_with(@user, status: :created) - # display(@user, status: :ok) - # - # Results in: - # - # render xml: @user, status: :created - # - def display(resource, given_options={}) - controller.render given_options.merge!(options).merge!(format => resource) - end - - def display_errors - controller.render format => resource_errors, :status => :unprocessable_entity - end - - # Check whether the resource has errors. - # - def has_errors? - resource.respond_to?(:errors) && !resource.errors.empty? - end - - # Check whether the necessary Renderer is available - def has_renderer? - Renderers::RENDERERS.include?(format) - end - - # By default, render the :edit action for HTML requests with errors, unless - # the verb was POST. - # - def default_action - @action ||= DEFAULT_ACTIONS_FOR_VERBS[request.request_method_symbol] - end - - def resource_errors - respond_to?("#{format}_resource_errors", true) ? send("#{format}_resource_errors") : resource.errors - end - - def json_resource_errors - {:errors => resource.errors} - end - - def response_overridden? - @default_response.present? - end - end -end diff --git a/actionpack/test/controller/mime/respond_with_test.rb b/actionpack/test/controller/mime/respond_with_test.rb deleted file mode 100644 index 115f3b2f41..0000000000 --- a/actionpack/test/controller/mime/respond_with_test.rb +++ /dev/null @@ -1,737 +0,0 @@ -require 'abstract_unit' -require 'controller/fake_models' - -class RespondWithController < ActionController::Base - class CustomerWithJson < Customer - def to_json; super; end - end - - respond_to :html, :json, :touch - respond_to :xml, :except => :using_resource_with_block - respond_to :js, :only => [ :using_resource_with_block, :using_resource, 'using_hash_resource' ] - - def using_resource - respond_with(resource) - end - - def using_hash_resource - respond_with({:result => resource}) - end - - def using_resource_with_block - respond_with(resource) do |format| - format.csv { render :text => "CSV" } - end - end - - def using_resource_with_overwrite_block - respond_with(resource) do |format| - format.html { render :text => "HTML" } - end - end - - def using_resource_with_collection - respond_with([resource, Customer.new("jamis", 9)]) - end - - def using_resource_with_parent - respond_with(Quiz::Store.new("developer?", 11), Customer.new("david", 13)) - end - - def using_resource_with_status_and_location - respond_with(resource, :location => "http://test.host/", :status => :created) - end - - def using_resource_with_json - respond_with(CustomerWithJson.new("david", request.delete? ? nil : 13)) - end - - def using_invalid_resource_with_template - respond_with(resource) - end - - def using_options_with_template - @customer = resource - respond_with(@customer, :status => 123, :location => "http://test.host/") - end - - def using_resource_with_responder - responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" } - respond_with(resource, :responder => responder) - end - - def using_resource_with_action - respond_with(resource, :action => :foo) do |format| - format.html { raise ActionView::MissingTemplate.new([], "bar", ["foo"], {}, false) } - end - end - - def using_responder_with_respond - responder = Class.new(ActionController::Responder) do - def respond; @controller.render :text => "respond #{format}"; end - end - respond_with(resource, :responder => responder) - end - - def respond_with_additional_params - @params = RespondWithController.params - respond_with({:result => resource}, @params) - end - -protected - def self.params - { - :foo => 'bar' - } - end - - def resource - Customer.new("david", request.delete? ? nil : 13) - end -end - -class InheritedRespondWithController < RespondWithController - clear_respond_to - respond_to :xml, :json - - def index - respond_with(resource) do |format| - format.json { render :text => "JSON" } - end - end -end - -class RenderJsonRespondWithController < RespondWithController - clear_respond_to - respond_to :json - - def index - respond_with(resource) do |format| - format.json { render :json => RenderJsonTestException.new('boom') } - end - end - - def create - resource = ValidatedCustomer.new(params[:name], 1) - respond_with(resource) do |format| - format.json do - if resource.errors.empty? - render :json => { :valid => true } - else - render :json => { :valid => false } - end - end - end - end -end - -class CsvRespondWithController < ActionController::Base - respond_to :csv - - class RespondWithCsv - def to_csv - "c,s,v" - end - end - - def index - respond_with(RespondWithCsv.new) - end -end - -class EmptyRespondWithController < ActionController::Base - def index - respond_with(Customer.new("david", 13)) - end -end - -class RespondWithControllerTest < ActionController::TestCase - def setup - super - @request.host = "www.example.com" - Mime::Type.register_alias('text/html', :iphone) - Mime::Type.register_alias('text/html', :touch) - Mime::Type.register('text/x-mobile', :mobile) - end - - def teardown - super - Mime::Type.unregister(:iphone) - Mime::Type.unregister(:touch) - Mime::Type.unregister(:mobile) - end - - def test_respond_with_shouldnt_modify_original_hash - get :respond_with_additional_params - assert_equal RespondWithController.params, assigns(:params) - end - - def test_using_resource - @request.accept = "application/xml" - get :using_resource - assert_equal "application/xml", @response.content_type - assert_equal "david", @response.body - - @request.accept = "application/json" - assert_raise ActionView::MissingTemplate do - get :using_resource - end - end - - def test_using_resource_with_js_simply_tries_to_render_the_template - @request.accept = "text/javascript" - get :using_resource - assert_equal "text/javascript", @response.content_type - assert_equal "alert(\"Hi\");", @response.body - end - - def test_using_hash_resource_with_js_raises_an_error_if_template_cant_be_found - @request.accept = "text/javascript" - assert_raise ActionView::MissingTemplate do - get :using_hash_resource - end - end - - def test_using_hash_resource - @request.accept = "application/xml" - get :using_hash_resource - assert_equal "application/xml", @response.content_type - assert_equal "\n\n david\n\n", @response.body - - @request.accept = "application/json" - get :using_hash_resource - assert_equal "application/json", @response.content_type - assert @response.body.include?("result") - assert @response.body.include?('"name":"david"') - assert @response.body.include?('"id":13') - end - - def test_using_hash_resource_with_post - @request.accept = "application/json" - assert_raise ArgumentError, "Nil location provided. Can't build URI." do - post :using_hash_resource - end - end - - def test_using_resource_with_block - @request.accept = "*/*" - get :using_resource_with_block - assert_equal "text/html", @response.content_type - assert_equal 'Hello world!', @response.body - - @request.accept = "text/csv" - get :using_resource_with_block - assert_equal "text/csv", @response.content_type - assert_equal "CSV", @response.body - - @request.accept = "application/xml" - get :using_resource - assert_equal "application/xml", @response.content_type - assert_equal "david", @response.body - end - - def test_using_resource_with_overwrite_block - get :using_resource_with_overwrite_block - assert_equal "text/html", @response.content_type - assert_equal "HTML", @response.body - end - - def test_not_acceptable - @request.accept = "application/xml" - assert_raises(ActionController::UnknownFormat) do - get :using_resource_with_block - end - - @request.accept = "text/javascript" - assert_raises(ActionController::UnknownFormat) do - get :using_resource_with_overwrite_block - end - end - - def test_using_resource_for_post_with_html_redirects_on_success - with_test_route_set do - post :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers/13", @response.location - assert @response.redirect? - end - end - - def test_using_resource_for_post_with_html_rerender_on_failure - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "New world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_post_with_xml_yields_created_on_success - with_test_route_set do - @request.accept = "application/xml" - post :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 201, @response.status - assert_equal "david", @response.body - assert_equal "http://www.example.com/customers/13", @response.location - end - end - - def test_using_resource_for_post_with_xml_yields_unprocessable_entity_on_failure - with_test_route_set do - @request.accept = "application/xml" - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 422, @response.status - assert_equal errors.to_xml, @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_post_with_json_yields_unprocessable_entity_on_failure - with_test_route_set do - @request.accept = "application/json" - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "application/json", @response.content_type - assert_equal 422, @response.status - errors = {:errors => errors} - assert_equal errors.to_json, @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_patch_with_html_redirects_on_success - with_test_route_set do - patch :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers/13", @response.location - assert @response.redirect? - end - end - - def test_using_resource_for_patch_with_html_rerender_on_failure - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - patch :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_patch_with_html_rerender_on_failure_even_on_method_override - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - @request.env["rack.methodoverride.original_method"] = "POST" - patch :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_put_with_html_redirects_on_success - with_test_route_set do - put :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers/13", @response.location - assert @response.redirect? - end - end - - def test_using_resource_for_put_with_html_rerender_on_failure - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - put :using_resource - - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_put_with_html_rerender_on_failure_even_on_method_override - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - @request.env["rack.methodoverride.original_method"] = "POST" - put :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_put_with_xml_yields_no_content_on_success - @request.accept = "application/xml" - put :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 204, @response.status - assert_equal "", @response.body - end - - def test_using_resource_for_put_with_json_yields_no_content_on_success - @request.accept = "application/json" - put :using_resource_with_json - assert_equal "application/json", @response.content_type - assert_equal 204, @response.status - assert_equal "", @response.body - end - - def test_using_resource_for_put_with_xml_yields_unprocessable_entity_on_failure - @request.accept = "application/xml" - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - put :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 422, @response.status - assert_equal errors.to_xml, @response.body - assert_nil @response.location - end - - def test_using_resource_for_put_with_json_yields_unprocessable_entity_on_failure - @request.accept = "application/json" - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - put :using_resource - assert_equal "application/json", @response.content_type - assert_equal 422, @response.status - errors = {:errors => errors} - assert_equal errors.to_json, @response.body - assert_nil @response.location - end - - def test_using_resource_for_delete_with_html_redirects_on_success - with_test_route_set do - Customer.any_instance.stubs(:destroyed?).returns(true) - delete :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers", @response.location - end - end - - def test_using_resource_for_delete_with_xml_yields_no_content_on_success - Customer.any_instance.stubs(:destroyed?).returns(true) - @request.accept = "application/xml" - delete :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 204, @response.status - assert_equal "", @response.body - end - - def test_using_resource_for_delete_with_json_yields_no_content_on_success - Customer.any_instance.stubs(:destroyed?).returns(true) - @request.accept = "application/json" - delete :using_resource_with_json - assert_equal "application/json", @response.content_type - assert_equal 204, @response.status - assert_equal "", @response.body - end - - def test_using_resource_for_delete_with_html_redirects_on_failure - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - Customer.any_instance.stubs(:destroyed?).returns(false) - delete :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers", @response.location - end - end - - def test_using_resource_with_parent_for_get - @request.accept = "application/xml" - get :using_resource_with_parent - assert_equal "application/xml", @response.content_type - assert_equal 200, @response.status - assert_equal "david", @response.body - end - - def test_using_resource_with_parent_for_post - with_test_route_set do - @request.accept = "application/xml" - - post :using_resource_with_parent - assert_equal "application/xml", @response.content_type - assert_equal 201, @response.status - assert_equal "david", @response.body - assert_equal "http://www.example.com/quiz_stores/11/customers/13", @response.location - - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 422, @response.status - assert_equal errors.to_xml, @response.body - assert_nil @response.location - end - end - - def test_using_resource_with_collection - @request.accept = "application/xml" - get :using_resource_with_collection - assert_equal "application/xml", @response.content_type - assert_equal 200, @response.status - assert_match(/david<\/name>/, @response.body) - assert_match(/jamis<\/name>/, @response.body) - end - - def test_using_resource_with_action - @controller.instance_eval do - def render(params={}) - self.response_body = "#{params[:action]} - #{formats}" - end - end - - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - - post :using_resource_with_action - assert_equal "foo - #{[:html].to_s}", @controller.response.body - end - - def test_respond_as_responder_entry_point - @request.accept = "text/html" - get :using_responder_with_respond - assert_equal "respond html", @response.body - - @request.accept = "application/xml" - get :using_responder_with_respond - assert_equal "respond xml", @response.body - end - - def test_clear_respond_to - @controller = InheritedRespondWithController.new - @request.accept = "text/html" - assert_raises(ActionController::UnknownFormat) do - get :index - end - end - - def test_first_in_respond_to_has_higher_priority - @controller = InheritedRespondWithController.new - @request.accept = "*/*" - get :index - assert_equal "application/xml", @response.content_type - assert_equal "david", @response.body - end - - def test_block_inside_respond_with_is_rendered - @controller = InheritedRespondWithController.new - @request.accept = "application/json" - get :index - assert_equal "JSON", @response.body - end - - def test_render_json_object_responds_to_str_still_produce_json - @controller = RenderJsonRespondWithController.new - @request.accept = "application/json" - get :index, :format => :json - assert_match(/"message":"boom"/, @response.body) - assert_match(/"error":"RenderJsonTestException"/, @response.body) - end - - def test_api_response_with_valid_resource_respect_override_block - @controller = RenderJsonRespondWithController.new - post :create, :name => "sikachu", :format => :json - assert_equal '{"valid":true}', @response.body - end - - def test_api_response_with_invalid_resource_respect_override_block - @controller = RenderJsonRespondWithController.new - post :create, :name => "david", :format => :json - assert_equal '{"valid":false}', @response.body - end - - def test_no_double_render_is_raised - @request.accept = "text/html" - assert_raise ActionView::MissingTemplate do - get :using_resource - end - end - - def test_using_resource_with_status_and_location - @request.accept = "text/html" - post :using_resource_with_status_and_location - assert @response.redirect? - assert_equal "http://test.host/", @response.location - - @request.accept = "application/xml" - get :using_resource_with_status_and_location - assert_equal 201, @response.status - end - - def test_using_resource_with_status_and_location_with_invalid_resource - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - - @request.accept = "text/xml" - - post :using_resource_with_status_and_location - assert_equal errors.to_xml, @response.body - assert_equal 422, @response.status - assert_equal nil, @response.location - - put :using_resource_with_status_and_location - assert_equal errors.to_xml, @response.body - assert_equal 422, @response.status - assert_equal nil, @response.location - end - - def test_using_invalid_resource_with_template - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - - @request.accept = "text/xml" - - post :using_invalid_resource_with_template - assert_equal errors.to_xml, @response.body - assert_equal 422, @response.status - assert_equal nil, @response.location - - put :using_invalid_resource_with_template - assert_equal errors.to_xml, @response.body - assert_equal 422, @response.status - assert_equal nil, @response.location - end - - def test_using_options_with_template - @request.accept = "text/xml" - - post :using_options_with_template - assert_equal "david", @response.body - assert_equal 123, @response.status - assert_equal "http://test.host/", @response.location - - put :using_options_with_template - assert_equal "david", @response.body - assert_equal 123, @response.status - assert_equal "http://test.host/", @response.location - end - - def test_using_resource_with_responder - get :using_resource_with_responder - assert_equal "Resource name is david", @response.body - end - - def test_using_resource_with_set_responder - RespondWithController.responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" } - get :using_resource - assert_equal "Resource name is david", @response.body - ensure - RespondWithController.responder = ActionController::Responder - end - - def test_uses_renderer_if_an_api_behavior - ActionController::Renderers.add :csv do |obj, options| - send_data obj.to_csv, type: Mime::CSV - end - @controller = CsvRespondWithController.new - get :index, format: 'csv' - assert_equal Mime::CSV, @response.content_type - assert_equal "c,s,v", @response.body - ensure - ActionController::Renderers.remove :csv - end - - def test_raises_missing_renderer_if_an_api_behavior_with_no_renderer - @controller = CsvRespondWithController.new - assert_raise ActionController::MissingRenderer do - get :index, format: 'csv' - end - end - - def test_removing_renderers - ActionController::Renderers.add :csv do |obj, options| - send_data obj.to_csv, type: Mime::CSV - end - @controller = CsvRespondWithController.new - @request.accept = "text/csv" - get :index, format: 'csv' - assert_equal Mime::CSV, @response.content_type - - ActionController::Renderers.remove :csv - assert_raise ActionController::MissingRenderer do - get :index, format: 'csv' - end - ensure - ActionController::Renderers.remove :csv - end - - def test_error_is_raised_if_no_respond_to_is_declared_and_respond_with_is_called - @controller = EmptyRespondWithController.new - @request.accept = "*/*" - assert_raise RuntimeError do - get :index - end - end - - private - def with_test_route_set - with_routing do |set| - set.draw do - resources :customers - resources :quiz_stores do - resources :customers - end - get ":controller/:action" - end - yield - end - end -end - -class FlashResponder < ActionController::Responder - def initialize(controller, resources, options={}) - super - end - - def to_html - controller.flash[:notice] = 'Success' - super - end -end - -class FlashResponderController < ActionController::Base - self.responder = FlashResponder - respond_to :html - - def index - respond_with Object.new do |format| - format.html { render :text => 'HTML' } - end - end -end - -class FlashResponderControllerTest < ActionController::TestCase - tests FlashResponderController - - def test_respond_with_block_executed - get :index - assert_equal 'HTML', @response.body - end - - def test_flash_responder_executed - get :index - assert_equal 'Success', flash[:notice] - end -end -- cgit v1.2.3 From 2b0c602bc3dd2928b8a77465f305c765dbb447e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 16 Aug 2014 16:45:11 -0400 Subject: Remove usafe of respond_to in ActionView tests --- actionview/test/activerecord/controller_runtime_test.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/actionview/test/activerecord/controller_runtime_test.rb b/actionview/test/activerecord/controller_runtime_test.rb index 368bec1c70..b5bfcf12a3 100644 --- a/actionview/test/activerecord/controller_runtime_test.rb +++ b/actionview/test/activerecord/controller_runtime_test.rb @@ -8,8 +8,6 @@ ActionController::Base.send :include, ActiveRecord::Railties::ControllerRuntime class ControllerRuntimeLogSubscriberTest < ActionController::TestCase class LogSubscriberController < ActionController::Base - respond_to :html - def show render :inline => "<%= Project.all %>" end @@ -21,7 +19,7 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase def create ActiveRecord::LogSubscriber.runtime += 100 project = Project.last - respond_with(project, location: url_for(action: :show)) + redirect_to "/" end def redirect -- cgit v1.2.3 From 57f5b00ba4f675873d5bab3bc88dae55d4fe7857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 16 Aug 2014 17:04:26 -0400 Subject: Remove more references to respond_with --- actionpack/test/fixtures/respond_with/edit.html.erb | 1 - actionpack/test/fixtures/respond_with/new.html.erb | 1 - .../fixtures/respond_with/respond_with_additional_params.html.erb | 0 .../respond_with/using_invalid_resource_with_template.xml.erb | 1 - .../test/fixtures/respond_with/using_options_with_template.xml.erb | 1 - actionpack/test/fixtures/respond_with/using_resource.js.erb | 1 - .../test/fixtures/respond_with/using_resource_with_block.html.erb | 1 - guides/source/caching_with_rails.md | 7 ++++++- guides/source/contributing_to_ruby_on_rails.md | 2 +- 9 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 actionpack/test/fixtures/respond_with/edit.html.erb delete mode 100644 actionpack/test/fixtures/respond_with/new.html.erb delete mode 100644 actionpack/test/fixtures/respond_with/respond_with_additional_params.html.erb delete mode 100644 actionpack/test/fixtures/respond_with/using_invalid_resource_with_template.xml.erb delete mode 100644 actionpack/test/fixtures/respond_with/using_options_with_template.xml.erb delete mode 100644 actionpack/test/fixtures/respond_with/using_resource.js.erb delete mode 100644 actionpack/test/fixtures/respond_with/using_resource_with_block.html.erb diff --git a/actionpack/test/fixtures/respond_with/edit.html.erb b/actionpack/test/fixtures/respond_with/edit.html.erb deleted file mode 100644 index ae82dfa4fc..0000000000 --- a/actionpack/test/fixtures/respond_with/edit.html.erb +++ /dev/null @@ -1 +0,0 @@ -Edit world! diff --git a/actionpack/test/fixtures/respond_with/new.html.erb b/actionpack/test/fixtures/respond_with/new.html.erb deleted file mode 100644 index 96c8f1b88b..0000000000 --- a/actionpack/test/fixtures/respond_with/new.html.erb +++ /dev/null @@ -1 +0,0 @@ -New world! diff --git a/actionpack/test/fixtures/respond_with/respond_with_additional_params.html.erb b/actionpack/test/fixtures/respond_with/respond_with_additional_params.html.erb deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/actionpack/test/fixtures/respond_with/using_invalid_resource_with_template.xml.erb b/actionpack/test/fixtures/respond_with/using_invalid_resource_with_template.xml.erb deleted file mode 100644 index bf5869ed22..0000000000 --- a/actionpack/test/fixtures/respond_with/using_invalid_resource_with_template.xml.erb +++ /dev/null @@ -1 +0,0 @@ -I should not be displayed \ No newline at end of file diff --git a/actionpack/test/fixtures/respond_with/using_options_with_template.xml.erb b/actionpack/test/fixtures/respond_with/using_options_with_template.xml.erb deleted file mode 100644 index b313017913..0000000000 --- a/actionpack/test/fixtures/respond_with/using_options_with_template.xml.erb +++ /dev/null @@ -1 +0,0 @@ -<%= @customer.name %> \ No newline at end of file diff --git a/actionpack/test/fixtures/respond_with/using_resource.js.erb b/actionpack/test/fixtures/respond_with/using_resource.js.erb deleted file mode 100644 index 4417680bce..0000000000 --- a/actionpack/test/fixtures/respond_with/using_resource.js.erb +++ /dev/null @@ -1 +0,0 @@ -alert("Hi"); \ No newline at end of file diff --git a/actionpack/test/fixtures/respond_with/using_resource_with_block.html.erb b/actionpack/test/fixtures/respond_with/using_resource_with_block.html.erb deleted file mode 100644 index 6769dd60bd..0000000000 --- a/actionpack/test/fixtures/respond_with/using_resource_with_block.html.erb +++ /dev/null @@ -1 +0,0 @@ -Hello world! \ No newline at end of file diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index 0902e347e2..d0f3a596fe 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -353,7 +353,12 @@ Instead of an options hash, you can also simply pass in a model, Rails will use class ProductsController < ApplicationController def show @product = Product.find(params[:id]) - respond_with(@product) if stale?(@product) + + if stale?(@product) + respond_to do |wants| + # ... normal response processing + end + end end end ``` diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index 0b05725623..8bc4b10591 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -397,7 +397,7 @@ inside, just indent it with 4 spaces: class ArticlesController def index - respond_with Article.limit(10) + render json: Article.limit(10) end end -- cgit v1.2.3 From 69ed422a9c534224eb818636d79d4263e2abd0a2 Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Sun, 17 Aug 2014 11:44:31 -0700 Subject: Fixed broken reference caused by 14965ba --- actionpack/test/dispatch/cookies_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index b40c21c067..7e7dd94425 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -519,8 +519,8 @@ class CookiesTest < ActionController::TestCase sign_secret = @request.env["action_dispatch.key_generator"].generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"]) - sha1_verifier = ActiveSupport::MessageVerifier.new(sign_secret, serializer: ActionDispatch::Cookies::NullSerializer, digest: 'SHA1') - sha256_verifier = ActiveSupport::MessageVerifier.new(sign_secret, serializer: ActionDispatch::Cookies::NullSerializer, digest: 'SHA256') + sha1_verifier = ActiveSupport::MessageVerifier.new(sign_secret, serializer: ActiveSupport::MessageEncryptor::NullSerializer, digest: 'SHA1') + sha256_verifier = ActiveSupport::MessageVerifier.new(sign_secret, serializer: ActiveSupport::MessageEncryptor::NullSerializer, digest: 'SHA256') assert_raises(ActiveSupport::MessageVerifier::InvalidSignature) do sha1_verifier.verify(cookies[:foo]) -- cgit v1.2.3 From 24226c51f075ed8d8e721cdefb6d2661c0a1f53a Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Sun, 17 Aug 2014 11:54:09 -0700 Subject: Raise a more helpful error for people who are using these extracted features --- .../lib/action_controller/metal/mime_responds.rb | 17 +++++++++++- actionpack/test/controller/mime/responder_test.rb | 30 ++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 actionpack/test/controller/mime/responder_test.rb diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index a533f03b6d..f5565947aa 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -5,6 +5,21 @@ module ActionController #:nodoc: module MimeResponds extend ActiveSupport::Concern + module ClassMethods + def respond_to(*) + raise NoMethodError, "The controller-level `respond_to' feature has " \ + "been extracted to the `responder` gem. Add it to your Gemfile to " \ + "continue using this feature. Consult the Rails upgrade guide for " \ + "details." + end + end + + def respond_with(*) + raise NoMethodError, "The `respond_with' feature has been extracted " \ + "to the `responder` gem. Add it to your Gemfile to continue using " \ + "this feature. Consult the Rails upgrade guide for details." + end + # Without web-service support, an action which collects the data for displaying a list of people # might look something like this: # @@ -165,7 +180,7 @@ module ActionController #:nodoc: # format.html.phone { redirect_to progress_path } # format.html.none { render "trash" } # end - # + # # Variants also support common `any`/`all` block that formats have. # # It works for both inline: diff --git a/actionpack/test/controller/mime/responder_test.rb b/actionpack/test/controller/mime/responder_test.rb new file mode 100644 index 0000000000..6201af3299 --- /dev/null +++ b/actionpack/test/controller/mime/responder_test.rb @@ -0,0 +1,30 @@ +require 'abstract_unit' +require 'controller/fake_models' + +class ResponderTest < ActionController::TestCase + def test_class_level_respond_to + e = assert_raises(NoMethodError) do + Class.new(ActionController::Base) do + respond_to :json + end + end + + assert_includes e.message, '`responder` gem' + end + + def test_respond_with + klass = Class.new(ActionController::Base) do + def index + respond_with Customer.new("david", 13) + end + end + + @controller = klass.new + + e = assert_raises(NoMethodError) do + get :index + end + + assert_includes e.message, '`responder` gem' + end +end -- cgit v1.2.3 From b662273df3d546a1fdd2de79005fd802f0e12643 Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Sun, 17 Aug 2014 11:58:17 -0700 Subject: The gem is called 'responders' --- .../lib/action_controller/metal/mime_responds.rb | 4 +-- actionpack/test/controller/mime/responder_test.rb | 30 ---------------------- actionpack/test/controller/mime/responders_test.rb | 30 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 32 deletions(-) delete mode 100644 actionpack/test/controller/mime/responder_test.rb create mode 100644 actionpack/test/controller/mime/responders_test.rb diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index f5565947aa..5c869d4ce2 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -8,7 +8,7 @@ module ActionController #:nodoc: module ClassMethods def respond_to(*) raise NoMethodError, "The controller-level `respond_to' feature has " \ - "been extracted to the `responder` gem. Add it to your Gemfile to " \ + "been extracted to the `responders` gem. Add it to your Gemfile to " \ "continue using this feature. Consult the Rails upgrade guide for " \ "details." end @@ -16,7 +16,7 @@ module ActionController #:nodoc: def respond_with(*) raise NoMethodError, "The `respond_with' feature has been extracted " \ - "to the `responder` gem. Add it to your Gemfile to continue using " \ + "to the `responders` gem. Add it to your Gemfile to continue using " \ "this feature. Consult the Rails upgrade guide for details." end diff --git a/actionpack/test/controller/mime/responder_test.rb b/actionpack/test/controller/mime/responder_test.rb deleted file mode 100644 index 6201af3299..0000000000 --- a/actionpack/test/controller/mime/responder_test.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'abstract_unit' -require 'controller/fake_models' - -class ResponderTest < ActionController::TestCase - def test_class_level_respond_to - e = assert_raises(NoMethodError) do - Class.new(ActionController::Base) do - respond_to :json - end - end - - assert_includes e.message, '`responder` gem' - end - - def test_respond_with - klass = Class.new(ActionController::Base) do - def index - respond_with Customer.new("david", 13) - end - end - - @controller = klass.new - - e = assert_raises(NoMethodError) do - get :index - end - - assert_includes e.message, '`responder` gem' - end -end diff --git a/actionpack/test/controller/mime/responders_test.rb b/actionpack/test/controller/mime/responders_test.rb new file mode 100644 index 0000000000..416e3191e6 --- /dev/null +++ b/actionpack/test/controller/mime/responders_test.rb @@ -0,0 +1,30 @@ +require 'abstract_unit' +require 'controller/fake_models' + +class ResponderTest < ActionController::TestCase + def test_class_level_respond_to + e = assert_raises(NoMethodError) do + Class.new(ActionController::Base) do + respond_to :json + end + end + + assert_includes e.message, '`responders` gem' + end + + def test_respond_with + klass = Class.new(ActionController::Base) do + def index + respond_with Customer.new("david", 13) + end + end + + @controller = klass.new + + e = assert_raises(NoMethodError) do + get :index + end + + assert_includes e.message, '`responders` gem' + end +end -- cgit v1.2.3 From a485633b16abd64b0955010fa93e0bc4894c7b7c Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Sun, 17 Aug 2014 12:19:06 -0700 Subject: `responders` 1.x won't do it. Told you to RTFM for details! --- actionpack/lib/action_controller/metal/mime_responds.rb | 9 ++++++--- actionpack/test/controller/mime/responders_test.rb | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 5c869d4ce2..dc572f13d2 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -9,15 +9,18 @@ module ActionController #:nodoc: def respond_to(*) raise NoMethodError, "The controller-level `respond_to' feature has " \ "been extracted to the `responders` gem. Add it to your Gemfile to " \ - "continue using this feature. Consult the Rails upgrade guide for " \ - "details." + "continue using this feature:\n" \ + " gem 'responders', '~> 2.0'\n" \ + "Consult the Rails upgrade guide for details." end end def respond_with(*) raise NoMethodError, "The `respond_with' feature has been extracted " \ "to the `responders` gem. Add it to your Gemfile to continue using " \ - "this feature. Consult the Rails upgrade guide for details." + "this feature:\n" \ + " gem 'responders', '~> 2.0'\n" \ + "Consult the Rails upgrade guide for details." end # Without web-service support, an action which collects the data for displaying a list of people diff --git a/actionpack/test/controller/mime/responders_test.rb b/actionpack/test/controller/mime/responders_test.rb index 416e3191e6..032b4c0ab1 100644 --- a/actionpack/test/controller/mime/responders_test.rb +++ b/actionpack/test/controller/mime/responders_test.rb @@ -10,6 +10,7 @@ class ResponderTest < ActionController::TestCase end assert_includes e.message, '`responders` gem' + assert_includes e.message, '~> 2.0' end def test_respond_with @@ -26,5 +27,6 @@ class ResponderTest < ActionController::TestCase end assert_includes e.message, '`responders` gem' + assert_includes e.message, '~> 2.0' end end -- cgit v1.2.3 From 80ceb0be7c982150eb2f868ed5e1a56a376fb2cb Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Mon, 18 Aug 2014 04:13:09 +0900 Subject: format --- activesupport/lib/active_support/multibyte/unicode.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb index 62caff77a3..a4467e0edc 100644 --- a/activesupport/lib/active_support/multibyte/unicode.rb +++ b/activesupport/lib/active_support/multibyte/unicode.rb @@ -336,7 +336,7 @@ module ActiveSupport begin @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read } rescue => e - raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable") + raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable") end # Redefine the === method so we can write shorter rules for grapheme cluster breaks @@ -385,7 +385,6 @@ module ActiveSupport def database @database ||= UnicodeDatabase.new end - end end end -- cgit v1.2.3 From 393e19e508a08ede0f5037bccb984e3eb252d579 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Mon, 18 Aug 2014 04:13:18 +0900 Subject: Preload UnicodeDatabase outside the loop This fixes random multibyte_chars_test fail under Ruby 1.9.3. I don't know why the tests fail. And I really don't know why this fixes. Maybe we need some more investigation... --- activesupport/lib/active_support/multibyte/unicode.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb index a4467e0edc..89009d1f55 100644 --- a/activesupport/lib/active_support/multibyte/unicode.rb +++ b/activesupport/lib/active_support/multibyte/unicode.rb @@ -368,6 +368,7 @@ module ActiveSupport private def apply_mapping(string, mapping) #:nodoc: + database.codepoints string.each_codepoint.map do |codepoint| cp = database.codepoints[codepoint] if cp and (ncp = cp.send(mapping)) and ncp > 0 -- cgit v1.2.3 From e158ee50e64e2ae2460cd26be53039922f588300 Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Sun, 17 Aug 2014 12:40:24 -0700 Subject: Use AS::JSON for (de)serializing cookies Use the Active Support JSON encoder for cookie jars using the `:json` or `:hybrid` serializer. This allows you to serialize custom Ruby objects into cookies by defining the `#as_json` hook on such objects. Fixes #16520. --- actionpack/CHANGELOG.md | 8 +++++ .../lib/action_dispatch/middleware/cookies.rb | 5 +-- actionpack/test/dispatch/cookies_test.rb | 39 ++++++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 17617206cb..67f117454f 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,11 @@ +* Use the Active Support JSON encoder for cookie jars using the `:json` or + `:hybrid` serializer. This allows you to serialize custom Ruby objects into + cookies by defining the `#as_json` hook on such objects. + + Fixes #16520. + + *Godfrey Chan* + * Add `config.action_dispatch.cookies_digest` option for setting custom digest. The default remains the same - 'SHA1'. diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 5b3c0e7316..83ac62a83d 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/object/blank' require 'active_support/key_generator' require 'active_support/message_verifier' +require 'active_support/json' module ActionDispatch class Request < Rack::Request @@ -391,11 +392,11 @@ module ActionDispatch class JsonSerializer def self.load(value) - JSON.parse(value, quirks_mode: true) + ActiveSupport::JSON.decode(value) end def self.dump(value) - JSON.generate(value, quirks_mode: true) + ActiveSupport::JSON.encode(value) end end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 7e7dd94425..9b03c805a0 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -21,6 +21,16 @@ class CookiesTest < ActionController::TestCase end end + class JSONWrapper + def initialize(obj) + @obj = obj + end + + def as_json(options = nil) + "wrapped: #{@obj.as_json(options)}" + end + end + class TestController < ActionController::Base def authenticate cookies["user_name"] = "david" @@ -85,6 +95,11 @@ class CookiesTest < ActionController::TestCase head :ok end + def set_wrapped_signed_cookie + cookies.signed[:user_id] = JSONWrapper.new(45) + head :ok + end + def get_signed_cookie cookies.signed[:user_id] head :ok @@ -95,6 +110,11 @@ class CookiesTest < ActionController::TestCase head :ok end + def set_wrapped_encrypted_cookie + cookies.encrypted[:foo] = JSONWrapper.new('bar') + head :ok + end + def get_encrypted_cookie cookies.encrypted[:foo] head :ok @@ -421,6 +441,14 @@ class CookiesTest < ActionController::TestCase assert_equal 45, cookies.signed[:user_id] end + def test_wrapped_signed_cookie_using_json_serializer + @request.env["action_dispatch.cookies_serializer"] = :json + get :set_wrapped_signed_cookie + cookies = @controller.send :cookies + assert_not_equal 'wrapped: 45', cookies[:user_id] + assert_equal 'wrapped: 45', cookies.signed[:user_id] + end + def test_signed_cookie_using_custom_serializer @request.env["action_dispatch.cookies_serializer"] = CustomSerializer get :set_signed_cookie @@ -503,6 +531,17 @@ class CookiesTest < ActionController::TestCase assert_equal 'bar', cookies.encrypted[:foo] end + def test_wrapped_encrypted_cookie_using_json_serializer + @request.env["action_dispatch.cookies_serializer"] = :json + get :set_wrapped_encrypted_cookie + cookies = @controller.send :cookies + assert_not_equal 'wrapped: bar', cookies[:foo] + assert_raises ::JSON::ParserError do + cookies.signed[:foo] + end + assert_equal 'wrapped: bar', cookies.encrypted[:foo] + end + def test_encrypted_cookie_using_custom_serializer @request.env["action_dispatch.cookies_serializer"] = CustomSerializer get :set_encrypted_cookie -- cgit v1.2.3 From 9f6e82ee4783e491c20f5244a613fdeb4024beb5 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 17 Aug 2014 12:52:54 -0700 Subject: Fix rounding errors with #travel_to by resetting the usec on any passed time to zero, so we only travel with per-second precision, not anything deeper than that. --- activesupport/CHANGELOG.md | 5 +++++ activesupport/lib/active_support/testing/time_helpers.rb | 6 +++++- activesupport/test/test_test.rb | 13 ++++++++++--- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index b961b373cb..96bce53999 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,8 @@ +* Fix rounding errors with #travel_to by resetting the usec on any passed time to zero, so we only travel + with per-second precision, not anything deeper than that. + + *DHH* + * Fix ActiveSupport::TestCase not to order users' test cases by default. If this change breaks your tests because your tests are order dependent, you need to explicitly call ActiveSupport::TestCase.my_tests_are_order_dependent! at the top of your tests. diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb index eefa84262e..1112c6e0b1 100644 --- a/activesupport/lib/active_support/testing/time_helpers.rb +++ b/activesupport/lib/active_support/testing/time_helpers.rb @@ -78,6 +78,10 @@ module ActiveSupport # or Date.today, in order to honor the application time zone # please always use Time.current and Date.current.) # + # Note that the usec for the time passed will be set to 0 to prevent rounding + # errors with external services, like MySQL (which will round instead of floor, + # leading to off-by-one-second errors). + # # This method also accepts a block, which will return the current time back to its original # state at the end of the block: # @@ -90,7 +94,7 @@ module ActiveSupport if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime) now = date_or_time.midnight.to_time else - now = date_or_time.to_time + now = date_or_time.to_time.change(usec: 0) end simple_stubs.stub_object(Time, :now, now) diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb index 6f63a8a725..b74bc9bf20 100644 --- a/activesupport/test/test_test.rb +++ b/activesupport/test/test_test.rb @@ -188,7 +188,7 @@ class TimeHelperTest < ActiveSupport::TestCase expected_time = Time.now + 1.day travel 1.day - assert_equal expected_time, Time.now + assert_equal expected_time.to_s(:db), Time.now.to_s(:db) assert_equal expected_time.to_date, Date.today end @@ -196,11 +196,11 @@ class TimeHelperTest < ActiveSupport::TestCase expected_time = Time.now + 1.day travel 1.day do - assert_equal expected_time, Time.now + assert_equal expected_time.to_s(:db), Time.now.to_s(:db) assert_equal expected_time.to_date, Date.today end - assert_not_equal expected_time, Time.now + assert_not_equal expected_time.to_s(:db), Time.now.to_s(:db) assert_not_equal expected_time.to_date, Date.today end @@ -235,4 +235,11 @@ class TimeHelperTest < ActiveSupport::TestCase assert_not_equal expected_time, Time.now assert_not_equal Date.new(2004, 11, 24), Date.today end + + def test_travel_to_will_reset_the_usec_to_avoid_mysql_rouding + travel_to Time.utc(2014, 10, 10, 10, 10, 50, 999999) do + assert_equal 50, Time.now.sec + assert_equal 0, Time.now.usec + end + end end -- cgit v1.2.3 From 07ca1d3ab6a51635df5e84f70d00de5c8962457c Mon Sep 17 00:00:00 2001 From: Genadi Samokovarov Date: Mon, 18 Aug 2014 02:03:58 +0300 Subject: Lock web-console to 2.0.0.beta1 for the first 4.2 beta The console on exception code isn't available on the stable releases yet. Lock it for now, so the beta users get a sneak peak of the console features. --- railties/lib/rails/generators/rails/app/templates/Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile index 8b51fda359..d6311829fa 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile @@ -31,7 +31,7 @@ group :development, :test do <%- end -%> # Access an IRB console on exceptions page and /console in development - gem 'web-console' + gem 'web-console', '2.0.0.beta1' <%- if spring_install? %> # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' -- cgit v1.2.3 From 6a2ca149fda46c8a36d80e0f95affb1b6de11aae Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Thu, 14 Aug 2014 23:33:40 +0000 Subject: [ActiveJob] Fix test syntax --- actionmailer/test/message_delivery_test.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/actionmailer/test/message_delivery_test.rb b/actionmailer/test/message_delivery_test.rb index 6ad9b099a3..49958081f5 100644 --- a/actionmailer/test/message_delivery_test.rb +++ b/actionmailer/test/message_delivery_test.rb @@ -11,7 +11,7 @@ class MessageDeliveryTest < ActiveSupport::TestCase @previous_logger = ActiveJob::Base.logger @previous_delivery_method = ActionMailer::Base.delivery_method ActionMailer::Base.delivery_method = :test - ActiveJob::Base.logger = Logger.new('/dev/null') + ActiveJob::Base.logger = Logger.new(nil) @mail = DelayedMailer.test_message(1, 2, 3) ActionMailer::Base.deliveries.clear end @@ -21,12 +21,12 @@ class MessageDeliveryTest < ActiveSupport::TestCase ActionMailer::Base.delivery_method = @previous_delivery_method end - test 'should be a MessageDelivery' do - assert_equal @mail.class, ActionMailer::MessageDelivery + test 'should have a message' do + assert @mail.message end - test 'its object should be a Mail::Message' do - assert_equal @mail.__getobj__.class, Mail::Message + test 'its message should be a Mail::Message' do + assert_equal Mail::Message , @mail.message.class end test 'should respond to .deliver' do @@ -47,28 +47,28 @@ class MessageDeliveryTest < ActiveSupport::TestCase test 'should enqueue and run correctly in activejob' do @mail.deliver_later! - assert_equal ActionMailer::Base.deliveries.size, 1 + assert_equal 1 , ActionMailer::Base.deliveries.size end test 'should enqueue the email with :deliver delivery method' do ret = ActionMailer::DelayedDeliveryJob.stub :enqueue, ->(*args){ args } do @mail.deliver_later end - assert_equal ret, ["DelayedMailer", "test_message", "deliver", 1, 2, 3] + assert_equal ['DelayedMailer', 'test_message', 'deliver', 1, 2, 3], ret end test 'should enqueue the email with :deliver! delivery method' do ret = ActionMailer::DelayedDeliveryJob.stub :enqueue, ->(*args){ args } do @mail.deliver_later! end - assert_equal ret, ["DelayedMailer", "test_message", "deliver!", 1, 2, 3] + assert_equal ['DelayedMailer', 'test_message', 'deliver!', 1, 2, 3], ret end test 'should enqueue a delivery with a delay' do ret = ActionMailer::DelayedDeliveryJob.stub :enqueue_in, ->(*args){ args } do @mail.deliver_later in: 600 end - assert_equal ret, [600, "DelayedMailer", "test_message", "deliver", 1, 2, 3] + assert_equal [600, 'DelayedMailer', 'test_message', 'deliver', 1, 2, 3], ret end test 'should enqueue a delivery at a specific time' do @@ -76,7 +76,7 @@ class MessageDeliveryTest < ActiveSupport::TestCase ret = ActionMailer::DelayedDeliveryJob.stub :enqueue_at, ->(*args){ args } do @mail.deliver_later at: later_time end - assert_equal ret, [later_time, "DelayedMailer", "test_message", "deliver", 1, 2, 3] + assert_equal [later_time, 'DelayedMailer', 'test_message', 'deliver', 1, 2, 3], ret end end -- cgit v1.2.3 From 299d1f262629aaa25e070db6ae15438e95137054 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Thu, 14 Aug 2014 23:47:38 +0000 Subject: [ActionMailer] Rename ActionMailer::DelayedDeliveryJob to ActionMailer::DeliveryJob --- actionmailer/lib/action_mailer.rb | 2 +- actionmailer/lib/action_mailer/delayed_delivery_job.rb | 11 ----------- actionmailer/lib/action_mailer/delivery_job.rb | 11 +++++++++++ actionmailer/lib/action_mailer/message_delivery.rb | 4 +++- actionmailer/test/message_delivery_test.rb | 8 ++++---- 5 files changed, 19 insertions(+), 17 deletions(-) delete mode 100644 actionmailer/lib/action_mailer/delayed_delivery_job.rb create mode 100644 actionmailer/lib/action_mailer/delivery_job.rb diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 1add881054..b994ef3182 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -46,5 +46,5 @@ module ActionMailer autoload :TestCase autoload :TestHelper autoload :MessageDelivery - autoload :DelayedDeliveryJob + autoload :DeliveryJob end diff --git a/actionmailer/lib/action_mailer/delayed_delivery_job.rb b/actionmailer/lib/action_mailer/delayed_delivery_job.rb deleted file mode 100644 index 159198829f..0000000000 --- a/actionmailer/lib/action_mailer/delayed_delivery_job.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'active_job' - -module ActionMailer - class DelayedDeliveryJob < ActiveJob::Base - queue_as :mailers - - def perform(mailer, mail_method, delivery_method, *args) - mailer.constantize.send(mail_method, *args).send(delivery_method) - end - end -end diff --git a/actionmailer/lib/action_mailer/delivery_job.rb b/actionmailer/lib/action_mailer/delivery_job.rb new file mode 100644 index 0000000000..b2cfa245fd --- /dev/null +++ b/actionmailer/lib/action_mailer/delivery_job.rb @@ -0,0 +1,11 @@ +require 'active_job' + +module ActionMailer + class DeliveryJob < ActiveJob::Base + queue_as :mailers + + def perform(mailer, mail_method, delivery_method, *args) + mailer.constantize.public_send(mail_method, *args).send(delivery_method) + end + end +end diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb index 27e0671427..80a0517bff 100644 --- a/actionmailer/lib/action_mailer/message_delivery.rb +++ b/actionmailer/lib/action_mailer/message_delivery.rb @@ -1,3 +1,5 @@ +require 'delegate' + module ActionMailer class MessageDelivery < Delegator def initialize(mailer, mail_method, *args) @@ -37,7 +39,7 @@ module ActionMailer enqueue_method = :enqueue_in args.unshift options[:in] end - ActionMailer::DelayedDeliveryJob.send enqueue_method, *args + ActionMailer::DeliveryJob.send enqueue_method, *args end end end diff --git a/actionmailer/test/message_delivery_test.rb b/actionmailer/test/message_delivery_test.rb index 49958081f5..a097d0e84d 100644 --- a/actionmailer/test/message_delivery_test.rb +++ b/actionmailer/test/message_delivery_test.rb @@ -51,21 +51,21 @@ class MessageDeliveryTest < ActiveSupport::TestCase end test 'should enqueue the email with :deliver delivery method' do - ret = ActionMailer::DelayedDeliveryJob.stub :enqueue, ->(*args){ args } do + ret = ActionMailer::DeliveryJob.stub :enqueue, ->(*args){ args } do @mail.deliver_later end assert_equal ['DelayedMailer', 'test_message', 'deliver', 1, 2, 3], ret end test 'should enqueue the email with :deliver! delivery method' do - ret = ActionMailer::DelayedDeliveryJob.stub :enqueue, ->(*args){ args } do + ret = ActionMailer::DeliveryJob.stub :enqueue, ->(*args){ args } do @mail.deliver_later! end assert_equal ['DelayedMailer', 'test_message', 'deliver!', 1, 2, 3], ret end test 'should enqueue a delivery with a delay' do - ret = ActionMailer::DelayedDeliveryJob.stub :enqueue_in, ->(*args){ args } do + ret = ActionMailer::DeliveryJob.stub :enqueue_in, ->(*args){ args } do @mail.deliver_later in: 600 end assert_equal [600, 'DelayedMailer', 'test_message', 'deliver', 1, 2, 3], ret @@ -73,7 +73,7 @@ class MessageDeliveryTest < ActiveSupport::TestCase test 'should enqueue a delivery at a specific time' do later_time = Time.now.to_i + 3600 - ret = ActionMailer::DelayedDeliveryJob.stub :enqueue_at, ->(*args){ args } do + ret = ActionMailer::DeliveryJob.stub :enqueue_at, ->(*args){ args } do @mail.deliver_later at: later_time end assert_equal [later_time, 'DelayedMailer', 'test_message', 'deliver', 1, 2, 3], ret -- cgit v1.2.3 From fdc7dbc5b239f44243b9a973197ca7c031bc6313 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Fri, 15 Aug 2014 09:50:45 +0000 Subject: [ActiveJob] require files in logging.rb --- activejob/lib/active_job/logging.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activejob/lib/active_job/logging.rb b/activejob/lib/active_job/logging.rb index d20bc3efce..d9e544acf5 100644 --- a/activejob/lib/active_job/logging.rb +++ b/activejob/lib/active_job/logging.rb @@ -1,4 +1,6 @@ require 'active_support/core_ext/string/filters' +require 'active_support/tagged_logging' +require 'active_support/logger' module ActiveJob module Logging -- cgit v1.2.3 From 3954fdf5f325719143df12860f65d778350d12ed Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Fri, 15 Aug 2014 10:04:06 +0000 Subject: [ActiveJob] Convert ActiveJob::Arguments into module --- activejob/lib/active_job/arguments.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb index ef6a3fce1d..8486a9e918 100644 --- a/activejob/lib/active_job/arguments.rb +++ b/activejob/lib/active_job/arguments.rb @@ -2,19 +2,20 @@ require 'active_model/global_locator' require 'active_model/global_identification' module ActiveJob - class Arguments + module Arguments + extend self TYPE_WHITELIST = [ NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum ] - def self.serialize(arguments) + def serialize(arguments) arguments.map { |argument| serialize_argument(argument) } end - def self.deserialize(arguments) + def deserialize(arguments) arguments.map { |argument| deserialize_argument(argument) } end private - def self.serialize_argument(argument) + def serialize_argument(argument) case argument when ActiveModel::GlobalIdentification argument.global_id.to_s @@ -29,7 +30,7 @@ module ActiveJob end end - def self.deserialize_argument(argument) + def deserialize_argument(argument) case argument when Array deserialize(argument) @@ -40,7 +41,7 @@ module ActiveJob end end - def self.serialize_hash_key(key) + def serialize_hash_key(key) case key when String, Symbol key.to_s -- cgit v1.2.3 From b06d91924ffeb07c5ca6a3d96eff288267ee80c6 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Fri, 15 Aug 2014 10:11:58 +0000 Subject: [ActiveJob] remove ruby warnings --- activejob/lib/active_job/enqueuing.rb | 2 +- activejob/lib/active_job/execution.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb index e3ac11ba97..c00aab40da 100644 --- a/activejob/lib/active_job/enqueuing.rb +++ b/activejob/lib/active_job/enqueuing.rb @@ -57,7 +57,7 @@ module ActiveJob end def retry_now - self.class.enqueue *arguments + self.class.enqueue(*arguments) end def retry_in(interval) diff --git a/activejob/lib/active_job/execution.rb b/activejob/lib/active_job/execution.rb index 78ada3d908..181d049616 100644 --- a/activejob/lib/active_job/execution.rb +++ b/activejob/lib/active_job/execution.rb @@ -14,14 +14,14 @@ module ActiveJob self.arguments = Arguments.deserialize(serialized_args) run_callbacks :perform do - perform *arguments + perform(*arguments) end rescue => exception rescue_with_handler(exception) || raise(exception) end def perform(*) - raise NotImplementedError + fail NotImplementedError end end end -- cgit v1.2.3 From 59221cc4f1b8f87553455aad26905c4f28b424f8 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Sat, 16 Aug 2014 15:49:03 +0300 Subject: [ActiveJob] make the resque-scheduler optional --- activejob/lib/active_job/queue_adapters/resque_adapter.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/activejob/lib/active_job/queue_adapters/resque_adapter.rb b/activejob/lib/active_job/queue_adapters/resque_adapter.rb index 30a51485bd..384aa0c4cc 100644 --- a/activejob/lib/active_job/queue_adapters/resque_adapter.rb +++ b/activejob/lib/active_job/queue_adapters/resque_adapter.rb @@ -7,9 +7,8 @@ begin rescue LoadError begin require 'resque_scheduler' - rescue LoadError => e - $stderr.puts 'The ActiveJob resque adapter requires resque-scheduler. Please add it to your Gemfile and run bundle install' - raise e + rescue LoadError + false end end @@ -22,6 +21,10 @@ module ActiveJob end def enqueue_at(job, timestamp, *args) + unless Resque.respond_to?(:enqueue_at_with_queue) + raise NotImplementedError, "To be able to schedule jobs with Resque you need the " \ + "resque-scheduler gem. Please add it to your Gemfile and run bundle install" + end Resque.enqueue_at_with_queue job.queue_name, timestamp, JobWrapper, job.name, *args end end -- cgit v1.2.3 From 2f7b239fca6630e49ba8ad9df6fc7db25e1080f0 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Sun, 17 Aug 2014 01:06:30 +0000 Subject: [ActiveJob] Use globalid gem --- Gemfile | 1 + actionmailer/test/base_test.rb | 1 - activejob/README.md | 4 ++-- activejob/activejob.gemspec | 3 +-- activejob/lib/active_job.rb | 1 + activejob/lib/active_job/arguments.rb | 7 ++----- activejob/lib/active_job/enqueuing.rb | 2 +- activejob/test/helper.rb | 2 ++ activejob/test/models/person.rb | 4 +--- 9 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Gemfile b/Gemfile index d059926fdb..dc6dc855a2 100644 --- a/Gemfile +++ b/Gemfile @@ -36,6 +36,7 @@ end gem 'dalli', '>= 2.2.1' # ActiveJob +gem 'globalid', github: 'rails/globalid' gem 'resque', require: false gem 'resque-scheduler', require: false gem 'sidekiq', require: false diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index c17e59f746..fc24639bf4 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -4,7 +4,6 @@ require 'set' require 'action_dispatch' require 'active_support/time' -require 'active_support/core_ext/object/itself' require 'mailers/base_mailer' require 'mailers/proc_mailer' diff --git a/activejob/README.md b/activejob/README.md index ebb7876c81..e48070bcfc 100644 --- a/activejob/README.md +++ b/activejob/README.md @@ -59,7 +59,7 @@ That's it! ## GlobalID support -Active Job supports [GlobalID serialization](https://github.com/rails/activemodel-globalid/) for parameters. This makes it possible +Active Job supports [GlobalID serialization](https://github.com/rails/globalid/) for parameters. This makes it possible to pass live Active Record objects to your job instead of class/id pairs, which you then have to manually deserialize. Before, jobs would look like this: @@ -82,7 +82,7 @@ class TrashableCleanupJob end ``` -This works with any class that mixes in ActiveModel::GlobalIdentification, which +This works with any class that mixes in GlobalID::Identification, which by default has been mixed into Active Record classes. diff --git a/activejob/activejob.gemspec b/activejob/activejob.gemspec index fd551e540e..d609bb8fce 100644 --- a/activejob/activejob.gemspec +++ b/activejob/activejob.gemspec @@ -18,6 +18,5 @@ Gem::Specification.new do |s| s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.md', 'lib/**/*'] s.require_path = 'lib' - s.add_dependency 'activesupport', version - s.add_dependency 'activemodel-globalid' + s.add_dependency 'globalid' end diff --git a/activejob/lib/active_job.rb b/activejob/lib/active_job.rb index f0a34ffb44..ef92406725 100644 --- a/activejob/lib/active_job.rb +++ b/activejob/lib/active_job.rb @@ -24,6 +24,7 @@ require 'active_support' require 'active_support/rails' require 'active_job/version' +require 'global_id' module ActiveJob extend ActiveSupport::Autoload diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb index 8486a9e918..b7572b0e29 100644 --- a/activejob/lib/active_job/arguments.rb +++ b/activejob/lib/active_job/arguments.rb @@ -1,6 +1,3 @@ -require 'active_model/global_locator' -require 'active_model/global_identification' - module ActiveJob module Arguments extend self @@ -17,7 +14,7 @@ module ActiveJob private def serialize_argument(argument) case argument - when ActiveModel::GlobalIdentification + when GlobalID::Identification argument.global_id.to_s when *TYPE_WHITELIST argument @@ -37,7 +34,7 @@ module ActiveJob when Hash Hash[ argument.map { |key, value| [ key, deserialize_argument(value) ] } ].with_indifferent_access else - ActiveModel::GlobalLocator.locate(argument) || argument + GlobalID::Locator.locate(argument) || argument end end diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb index c00aab40da..3d00d51867 100644 --- a/activejob/lib/active_job/enqueuing.rb +++ b/activejob/lib/active_job/enqueuing.rb @@ -7,7 +7,7 @@ module ActiveJob module ClassMethods # Push a job onto the queue. The arguments must be legal JSON types # (string, int, float, nil, true, false, hash or array) or - # ActiveModel::GlobalIdentication instances. Arbitrary Ruby objects + # GlobalID::Identification instances. Arbitrary Ruby objects # are not supported. # # Returns an instance of the job class queued with args available in diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb index 5e491332ee..a56b35da12 100644 --- a/activejob/test/helper.rb +++ b/activejob/test/helper.rb @@ -2,6 +2,8 @@ require File.expand_path('../../../load_paths', __FILE__) require 'active_job' +GlobalID.app = 'aj' + @adapter = ENV['AJADAPTER'] || 'inline' def sidekiq? diff --git a/activejob/test/models/person.rb b/activejob/test/models/person.rb index a5bdbc462b..b31396db4b 100644 --- a/activejob/test/models/person.rb +++ b/activejob/test/models/person.rb @@ -1,7 +1,5 @@ -require 'active_model/global_identification' - class Person - include ActiveModel::GlobalIdentification + include GlobalID::Identification attr_reader :id -- cgit v1.2.3 From 931cfc40796bfd2f4638d8ca7a11723d7562e9cd Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Sun, 17 Aug 2014 13:23:24 +0000 Subject: [ActiveJob] Fix tests for sucker_punch --- activejob/test/cases/job_serialization_test.rb | 4 ++-- activejob/test/cases/logging_test.rb | 2 +- activejob/test/cases/queuing_test.rb | 6 +++--- activejob/test/cases/rescue_test.rb | 4 ++-- activejob/test/helper.rb | 20 ++++++++++++++++++++ activejob/test/jobs/gid_job.rb | 2 +- activejob/test/jobs/hello_job.rb | 2 +- activejob/test/jobs/rescue_job.rb | 4 ++-- 8 files changed, 32 insertions(+), 12 deletions(-) diff --git a/activejob/test/cases/job_serialization_test.rb b/activejob/test/cases/job_serialization_test.rb index f3b89d8899..fc1b77744c 100644 --- a/activejob/test/cases/job_serialization_test.rb +++ b/activejob/test/cases/job_serialization_test.rb @@ -4,12 +4,12 @@ require 'models/person' class JobSerializationTest < ActiveSupport::TestCase setup do - Thread.current[:ajbuffer] = [] + JobBuffer.clear @person = Person.find(5) end test 'serialize job with gid' do GidJob.enqueue @person - assert_equal "Person with ID: 5", Thread.current[:ajbuffer].pop + assert_equal "Person with ID: 5", JobBuffer.last_value end end diff --git a/activejob/test/cases/logging_test.rb b/activejob/test/cases/logging_test.rb index f0f315c906..888c183a0b 100644 --- a/activejob/test/cases/logging_test.rb +++ b/activejob/test/cases/logging_test.rb @@ -23,7 +23,7 @@ class AdapterTest < ActiveSupport::TestCase def setup super - Thread.current[:ajbuffer] = [] + JobBuffer.clear @old_logger = ActiveJob::Base.logger @logger = ActiveSupport::TaggedLogging.new(TestLogger.new) set_logger @logger diff --git a/activejob/test/cases/queuing_test.rb b/activejob/test/cases/queuing_test.rb index 49760ce9c0..f020316d7e 100644 --- a/activejob/test/cases/queuing_test.rb +++ b/activejob/test/cases/queuing_test.rb @@ -5,17 +5,17 @@ require 'active_support/core_ext/numeric/time' class QueuingTest < ActiveSupport::TestCase setup do - Thread.current[:ajbuffer] = [] + JobBuffer.clear end test 'run queued job' do HelloJob.enqueue - assert_equal "David says hello", Thread.current[:ajbuffer].pop + assert_equal "David says hello", JobBuffer.last_value end test 'run queued job with arguments' do HelloJob.enqueue "Jamie" - assert_equal "Jamie says hello", Thread.current[:ajbuffer].pop + assert_equal "Jamie says hello", JobBuffer.last_value end test 'run queued job later' do diff --git a/activejob/test/cases/rescue_test.rb b/activejob/test/cases/rescue_test.rb index 4fbd27fe6c..250ab68671 100644 --- a/activejob/test/cases/rescue_test.rb +++ b/activejob/test/cases/rescue_test.rb @@ -5,13 +5,13 @@ require 'active_support/core_ext/object/inclusion' class RescueTest < ActiveSupport::TestCase setup do - Thread.current[:ajbuffer] = [] + JobBuffer.clear end test 'rescue perform exception with retry' do job = RescueJob.new job.execute(SecureRandom.uuid, "david") - assert_equal [ "rescued from ArgumentError", "performed beautifully" ], Thread.current[:ajbuffer] + assert_equal [ "rescued from ArgumentError", "performed beautifully" ], JobBuffer.values end test 'let through unhandled perform exception' do diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb index a56b35da12..ca67700273 100644 --- a/activejob/test/helper.rb +++ b/activejob/test/helper.rb @@ -28,3 +28,23 @@ require "adapters/#{@adapter}" require 'active_support/testing/autorun' ActiveJob::Base.logger.level = Logger::DEBUG + +module JobBuffer + class << self + def clear + @buffer = [] + end + + def add(value) + @buffer << value + end + + def values + @buffer + end + + def last_value + @buffer.last + end + end +end diff --git a/activejob/test/jobs/gid_job.rb b/activejob/test/jobs/gid_job.rb index eeb34c8a87..35c2366ec4 100644 --- a/activejob/test/jobs/gid_job.rb +++ b/activejob/test/jobs/gid_job.rb @@ -1,6 +1,6 @@ class GidJob < ActiveJob::Base def perform(person) - Thread.current[:ajbuffer] << "Person with ID: #{person.id}" + JobBuffer.add("Person with ID: #{person.id}") end end diff --git a/activejob/test/jobs/hello_job.rb b/activejob/test/jobs/hello_job.rb index cb067bbe69..4c6256af0d 100644 --- a/activejob/test/jobs/hello_job.rb +++ b/activejob/test/jobs/hello_job.rb @@ -1,5 +1,5 @@ class HelloJob < ActiveJob::Base def perform(greeter = "David") - Thread.current[:ajbuffer] << "#{greeter} says hello" + JobBuffer.add("#{greeter} says hello") end end diff --git a/activejob/test/jobs/rescue_job.rb b/activejob/test/jobs/rescue_job.rb index e42de4876e..77084160d9 100644 --- a/activejob/test/jobs/rescue_job.rb +++ b/activejob/test/jobs/rescue_job.rb @@ -2,7 +2,7 @@ class RescueJob < ActiveJob::Base class OtherError < StandardError; end rescue_from(ArgumentError) do - Thread.current[:ajbuffer] << "rescued from ArgumentError" + JobBuffer.add('rescued from ArgumentError') arguments[0] = "DIFFERENT!" retry_now end @@ -14,7 +14,7 @@ class RescueJob < ActiveJob::Base when "other" raise OtherError else - Thread.current[:ajbuffer] << "performed beautifully" + JobBuffer.add('performed beautifully') end end end -- cgit v1.2.3 From 9a3426220145cf8862324f204eece64f3a6a4634 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Sun, 17 Aug 2014 19:16:43 +0000 Subject: [ActiveJob] Add deserialize_arguments method to job --- activejob/lib/active_job/execution.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/activejob/lib/active_job/execution.rb b/activejob/lib/active_job/execution.rb index 181d049616..0e7b5bdd72 100644 --- a/activejob/lib/active_job/execution.rb +++ b/activejob/lib/active_job/execution.rb @@ -11,7 +11,7 @@ module ActiveJob def execute(job_id, *serialized_args) self.job_id = job_id - self.arguments = Arguments.deserialize(serialized_args) + self.arguments = deserialize_arguments(serialized_args) run_callbacks :perform do perform(*arguments) @@ -23,5 +23,11 @@ module ActiveJob def perform(*) fail NotImplementedError end + + private + def deserialize_arguments(serialized_args) + Arguments.deserialize(serialized_args) + end + end end -- cgit v1.2.3 From 3faa61ede58aa29400e4e062a799c61913ae213f Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Sun, 17 Aug 2014 23:48:44 +0300 Subject: [ActiveJob] raise DeserializationError when got an error deserializing --- activejob/lib/active_job/arguments.rb | 12 ++++++++++++ activejob/test/cases/rescue_test.rb | 7 +++++++ activejob/test/jobs/rescue_job.rb | 5 +++++ activejob/test/models/person.rb | 3 +++ 4 files changed, 27 insertions(+) diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb index b7572b0e29..369e716912 100644 --- a/activejob/lib/active_job/arguments.rb +++ b/activejob/lib/active_job/arguments.rb @@ -1,4 +1,14 @@ module ActiveJob + class DeserializationError < StandardError + attr_reader :original_exception + + def initialize(e) + super ("Error while trying to deserialize arguments: #{e.message}") + @original_exception = e + set_backtrace e.backtrace + end + end + module Arguments extend self TYPE_WHITELIST = [ NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum ] @@ -36,6 +46,8 @@ module ActiveJob else GlobalID::Locator.locate(argument) || argument end + rescue => e + raise DeserializationError.new(e) end def serialize_hash_key(key) diff --git a/activejob/test/cases/rescue_test.rb b/activejob/test/cases/rescue_test.rb index 250ab68671..c9473314f2 100644 --- a/activejob/test/cases/rescue_test.rb +++ b/activejob/test/cases/rescue_test.rb @@ -20,4 +20,11 @@ class RescueTest < ActiveSupport::TestCase job.execute(SecureRandom.uuid, "other") end end + + test 'rescue from deserialization errors' do + RescueJob.enqueue Person.new(404) + assert_includes JobBuffer.values, 'rescued from DeserializationError' + assert_includes JobBuffer.values, 'DeserializationError original exception was Person::RecordNotFound' + assert_not_includes JobBuffer.values, 'performed beautifully' + end end diff --git a/activejob/test/jobs/rescue_job.rb b/activejob/test/jobs/rescue_job.rb index 77084160d9..e9cb37d1c4 100644 --- a/activejob/test/jobs/rescue_job.rb +++ b/activejob/test/jobs/rescue_job.rb @@ -7,6 +7,11 @@ class RescueJob < ActiveJob::Base retry_now end + rescue_from(ActiveJob::DeserializationError) do |e| + JobBuffer.add('rescued from DeserializationError') + JobBuffer.add("DeserializationError original exception was #{e.original_exception.class.name}") + end + def perform(person = "david") case person when "david" diff --git a/activejob/test/models/person.rb b/activejob/test/models/person.rb index b31396db4b..76a8f40616 100644 --- a/activejob/test/models/person.rb +++ b/activejob/test/models/person.rb @@ -1,9 +1,12 @@ class Person + class RecordNotFound < StandardError; end + include GlobalID::Identification attr_reader :id def self.find(id) + raise RecordNotFound.new("Cannot find person with ID=404") if id.to_i==404 new(id) end -- cgit v1.2.3 From 080296be61105f4f941441a506a6a9fe5c562772 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Sun, 17 Aug 2014 23:17:38 +0000 Subject: [ActiveJob] require global_id/railtie --- activejob/lib/active_job/railtie.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activejob/lib/active_job/railtie.rb b/activejob/lib/active_job/railtie.rb index 7c1dd8f275..6538ac1b30 100644 --- a/activejob/lib/active_job/railtie.rb +++ b/activejob/lib/active_job/railtie.rb @@ -1,5 +1,5 @@ +require 'global_id/railtie' require 'active_job' -require 'rails' module ActiveJob # = Active Job Railtie -- cgit v1.2.3 From c20c86ee9e944e38ce0eb581e4af3ad4af839875 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 17 Aug 2014 16:40:41 -0700 Subject: Dont encourage people to run without autoflush in production --- .../rails/app/templates/config/environments/production.rb.tt | 3 --- 1 file changed, 3 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index 277fe01e89..bec7716a9b 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -70,9 +70,6 @@ Rails.application.configure do # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify - # Disable automatic flushing of the log to improve performance. - # config.autoflush_log = false - # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new <%- unless options.skip_active_record? -%> -- cgit v1.2.3 From 6e6ebeb03cf0ff58c9ef778bfbdd9b0b9891b17b Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 17 Aug 2014 16:57:57 -0700 Subject: Add CHANGELOG entry for #deliver_later --- actionmailer/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index 451270bae3..e6952a4b3b 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,3 +1,10 @@ +* Added #deliver_later in addition to #deliver, which will enqueue a job to render and + deliver the mail instead of delivering it right at that moment. The job is enqueued + using the new Active Job framework in Rails, and will use whatever queue is configured for Rails. + + *DHH/Abdelkader Boudih/Cristian Bica* + + * Make ActionMailer::Previews methods class methods. Previously they were instance methods and ActionMailer tries to render a message when they are called. -- cgit v1.2.3