aboutsummaryrefslogtreecommitdiffstats
path: root/activejob
diff options
context:
space:
mode:
Diffstat (limited to 'activejob')
-rw-r--r--activejob/.gitignore1
-rw-r--r--activejob/README.md6
-rw-r--r--activejob/Rakefile31
-rw-r--r--activejob/activejob.gemspec2
-rw-r--r--activejob/lib/active_job.rb3
-rw-r--r--activejob/lib/active_job/arguments.rb18
-rw-r--r--activejob/lib/active_job/base.rb7
-rw-r--r--activejob/lib/active_job/callbacks.rb4
-rw-r--r--activejob/lib/active_job/configured_job.rb16
-rw-r--r--activejob/lib/active_job/core.rb89
-rw-r--r--activejob/lib/active_job/enqueuing.rb98
-rw-r--r--activejob/lib/active_job/execution.rb32
-rw-r--r--activejob/lib/active_job/gem_version.rb2
-rw-r--r--activejob/lib/active_job/identifier.rb15
-rw-r--r--activejob/lib/active_job/logging.rb36
-rw-r--r--activejob/lib/active_job/queue_adapter.rb32
-rw-r--r--activejob/lib/active_job/queue_adapters.rb1
-rw-r--r--activejob/lib/active_job/queue_adapters/backburner_adapter.rb14
-rw-r--r--activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb12
-rw-r--r--activejob/lib/active_job/queue_adapters/inline_adapter.rb4
-rw-r--r--activejob/lib/active_job/queue_adapters/qu_adapter.rb9
-rw-r--r--activejob/lib/active_job/queue_adapters/que_adapter.rb12
-rw-r--r--activejob/lib/active_job/queue_adapters/queue_classic_adapter.rb27
-rw-r--r--activejob/lib/active_job/queue_adapters/resque_adapter.rb12
-rw-r--r--activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb12
-rw-r--r--activejob/lib/active_job/queue_adapters/sneakers_adapter.rb12
-rw-r--r--activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb10
-rw-r--r--activejob/lib/active_job/queue_adapters/test_adapter.rb76
-rw-r--r--activejob/lib/active_job/queue_name.rb23
-rw-r--r--activejob/lib/active_job/test_case.rb7
-rw-r--r--activejob/lib/active_job/test_helper.rb196
-rw-r--r--activejob/test/cases/callbacks_test.rb5
-rw-r--r--activejob/test/cases/job_serialization_test.rb2
-rw-r--r--activejob/test/cases/logging_test.rb41
-rw-r--r--activejob/test/cases/parameters_test.rb16
-rw-r--r--activejob/test/cases/queue_naming_test.rb37
-rw-r--r--activejob/test/cases/queuing_test.rb14
-rw-r--r--activejob/test/cases/rescue_test.rb15
-rw-r--r--activejob/test/cases/test_case_test.rb14
-rw-r--r--activejob/test/cases/test_helper_test.rb214
-rw-r--r--activejob/test/helper.rb38
-rw-r--r--activejob/test/integration/queuing_test.rb46
-rw-r--r--activejob/test/jobs/gid_job.rb2
-rw-r--r--activejob/test/jobs/hello_job.rb2
-rw-r--r--activejob/test/jobs/nested_job.rb2
-rw-r--r--activejob/test/jobs/rescue_job.rb4
-rw-r--r--activejob/test/support/integration/adapters/backburner.rb38
-rw-r--r--activejob/test/support/integration/adapters/delayed_job.rb20
-rw-r--r--activejob/test/support/integration/adapters/inline.rb15
-rw-r--r--activejob/test/support/integration/adapters/qu.rb38
-rw-r--r--activejob/test/support/integration/adapters/que.rb37
-rw-r--r--activejob/test/support/integration/adapters/queue_classic.rb45
-rw-r--r--activejob/test/support/integration/adapters/resque.rb47
-rw-r--r--activejob/test/support/integration/adapters/sidekiq.rb58
-rw-r--r--activejob/test/support/integration/adapters/sneakers.rb90
-rw-r--r--activejob/test/support/integration/adapters/sucker_punch.rb5
-rw-r--r--activejob/test/support/integration/dummy_app_template.rb21
-rw-r--r--activejob/test/support/integration/helper.rb32
-rw-r--r--activejob/test/support/integration/jobs_manager.rb27
-rw-r--r--activejob/test/support/integration/test_case_helpers.rb48
-rw-r--r--activejob/test/support/job_buffer.rb19
-rw-r--r--activejob/test/support/queue_classic/inline.rb12
62 files changed, 1569 insertions, 254 deletions
diff --git a/activejob/.gitignore b/activejob/.gitignore
new file mode 100644
index 0000000000..b3aaf55871
--- /dev/null
+++ b/activejob/.gitignore
@@ -0,0 +1 @@
+test/dummy
diff --git a/activejob/README.md b/activejob/README.md
index 1f300fcf62..5734ca413e 100644
--- a/activejob/README.md
+++ b/activejob/README.md
@@ -43,15 +43,15 @@ end
Enqueue a job like so:
```ruby
-MyJob.enqueue record # Enqueue a job to be performed as soon the queueing system is free.
+MyJob.perform_later 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.
+MyJob.set(wait_until: Date.tomorrow.noon).perform_later(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.
+MyJob.set(wait: 1.week).perform_later(record) # Enqueue a job to be performed 1 week from now.
```
That's it!
diff --git a/activejob/Rakefile b/activejob/Rakefile
index 484cd1d0b8..e918428459 100644
--- a/activejob/Rakefile
+++ b/activejob/Rakefile
@@ -34,6 +34,12 @@ namespace :test do
tasks = ACTIVEJOB_ADAPTERS.map{|a| "isolated_test_#{a}" }
run_without_aborting(*tasks)
end
+
+ desc 'Run all adapter integration tests'
+ task :integration do
+ tasks = ACTIVEJOB_ADAPTERS.map{|a| "integration_test_#{a}" }
+ run_without_aborting(*tasks)
+ end
end
@@ -53,6 +59,15 @@ ACTIVEJOB_ADAPTERS.each do |adapter|
end or raise 'Failures'
end
end
+
+ namespace :integration do
+ Rake::TestTask.new(adapter => "#{adapter}:env") do |t|
+ t.description = ""
+ t.libs << 'test'
+ t.test_files = FileList['test/integration/**/*_test.rb']
+ t.verbose = true
+ end
+ end
end
namespace adapter do
@@ -60,6 +75,17 @@ ACTIVEJOB_ADAPTERS.each do |adapter|
task isolated_test: "isolated_test_#{adapter}"
task(:env) { ENV['AJADAPTER'] = adapter }
+
+ namespace :isolated do
+ task(:env) { ENV['AJADAPTER'] = adapter }
+ end
+
+ namespace :integration do
+ task(:env) do
+ ENV['AJADAPTER'] = adapter
+ ENV['AJ_INTEGRATION_TESTS'] = "1"
+ end
+ end
end
@@ -67,7 +93,10 @@ ACTIVEJOB_ADAPTERS.each do |adapter|
task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"]
desc "Run #{adapter} tests in isolation"
- task "isolated_test_#{adapter}" => ["#{adapter}:env", "test:isolated:#{adapter}"]
+ task "isolated_test_#{adapter}" => ["#{adapter}:isolated:env", "test:isolated:#{adapter}"]
+
+ desc "Run #{adapter} integration tests"
+ task "integration_test_#{adapter}" => ["#{adapter}:integration:env", "test:integration:#{adapter}"]
end
diff --git a/activejob/activejob.gemspec b/activejob/activejob.gemspec
index 9944b2a1bd..a9be2a8f00 100644
--- a/activejob/activejob.gemspec
+++ b/activejob/activejob.gemspec
@@ -19,5 +19,5 @@ Gem::Specification.new do |s|
s.require_path = 'lib'
s.add_dependency 'activesupport', version
- s.add_dependency 'globalid', '>= 0.2.3'
+ s.add_dependency 'globalid', '>= 0.3.0'
end
diff --git a/activejob/lib/active_job.rb b/activejob/lib/active_job.rb
index 29123170b8..1b582f5877 100644
--- a/activejob/lib/active_job.rb
+++ b/activejob/lib/active_job.rb
@@ -31,4 +31,7 @@ module ActiveJob
autoload :Base
autoload :QueueAdapters
+ autoload :ConfiguredJob
+ autoload :TestCase
+ autoload :TestHelper
end
diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb
index e54f4afbc7..175a2f0956 100644
--- a/activejob/lib/active_job/arguments.rb
+++ b/activejob/lib/active_job/arguments.rb
@@ -5,8 +5,8 @@ module ActiveJob
class DeserializationError < StandardError
attr_reader :original_exception
- def initialize(e)
- super ("Error while trying to deserialize arguments: #{e.message}")
+ def initialize(e) #:nodoc:
+ super("Error while trying to deserialize arguments: #{e.message}")
@original_exception = e
set_backtrace e.backtrace
end
@@ -30,17 +30,19 @@ module ActiveJob
def deserialize(arguments)
arguments.map { |argument| deserialize_argument(argument) }
+ rescue => e
+ raise DeserializationError.new(e)
end
private
def serialize_argument(argument)
case argument
when GlobalID::Identification
- argument.global_id.to_s
+ argument.to_global_id.to_s
when *TYPE_WHITELIST
argument
when Array
- serialize(argument)
+ argument.map { |arg| serialize_argument(arg) }
when Hash
Hash[ argument.map { |key, value| [ serialize_hash_key(key), serialize_argument(value) ] } ]
else
@@ -51,14 +53,14 @@ module ActiveJob
def deserialize_argument(argument)
case argument
when Array
- deserialize(argument)
+ argument.map { |arg| deserialize_argument(arg) }
when Hash
Hash[ argument.map { |key, value| [ key, deserialize_argument(value) ] } ].with_indifferent_access
- else
+ when String, GlobalID
GlobalID::Locator.locate(argument) || argument
+ else
+ argument
end
- rescue => e
- raise DeserializationError.new(e)
end
def serialize_hash_key(key)
diff --git a/activejob/lib/active_job/base.rb b/activejob/lib/active_job/base.rb
index d5ba253016..a3bec1f827 100644
--- a/activejob/lib/active_job/base.rb
+++ b/activejob/lib/active_job/base.rb
@@ -1,20 +1,19 @@
+require 'active_job/core'
require 'active_job/queue_adapter'
require 'active_job/queue_name'
require 'active_job/enqueuing'
require 'active_job/execution'
require 'active_job/callbacks'
-require 'active_job/identifier'
require 'active_job/logging'
module ActiveJob
class Base
- extend QueueAdapter
-
+ include Core
+ include QueueAdapter
include QueueName
include Enqueuing
include Execution
include Callbacks
- include Identifier
include Logging
ActiveSupport.run_load_hooks(:active_job, self)
diff --git a/activejob/lib/active_job/callbacks.rb b/activejob/lib/active_job/callbacks.rb
index 8901fa77f2..cafa3438c0 100644
--- a/activejob/lib/active_job/callbacks.rb
+++ b/activejob/lib/active_job/callbacks.rb
@@ -3,8 +3,8 @@ require 'active_support/callbacks'
module ActiveJob
# = Active Job Callbacks
#
- # Active Job provides hooks during the lifecycle of a job. Callbacks allow you to trigger
- # logic during the lifecycle of a job. Available callbacks:
+ # Active Job provides hooks during the lifecycle of a job. Callbacks allow you
+ # to trigger logic during the lifecycle of a job. Available callbacks are:
#
# * <tt>before_enqueue</tt>
# * <tt>around_enqueue</tt>
diff --git a/activejob/lib/active_job/configured_job.rb b/activejob/lib/active_job/configured_job.rb
new file mode 100644
index 0000000000..979280b910
--- /dev/null
+++ b/activejob/lib/active_job/configured_job.rb
@@ -0,0 +1,16 @@
+module ActiveJob
+ class ConfiguredJob #:nodoc:
+ def initialize(job_class, options={})
+ @options = options
+ @job_class = job_class
+ end
+
+ def perform_now(*args)
+ @job_class.new(*args).perform_now
+ end
+
+ def perform_later(*args)
+ @job_class.new(*args).enqueue @options
+ end
+ end
+end
diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb
new file mode 100644
index 0000000000..b6dd03a0bc
--- /dev/null
+++ b/activejob/lib/active_job/core.rb
@@ -0,0 +1,89 @@
+module ActiveJob
+ module Core
+ extend ActiveSupport::Concern
+
+ included do
+ # Job arguments
+ attr_accessor :arguments
+ attr_writer :serialized_arguments
+
+ # Timestamp when the job should be performed
+ attr_accessor :scheduled_at
+
+ # Job Identifier
+ attr_accessor :job_id
+
+ # Queue on which the job should be run on.
+ attr_writer :queue_name
+ end
+
+ module ClassMethods
+ # Creates a new job instance from a hash created with +serialize+
+ def deserialize(job_data)
+ job = job_data['job_class'].constantize.new
+ job.job_id = job_data['job_id']
+ job.queue_name = job_data['queue_name']
+ job.serialized_arguments = job_data['arguments']
+ job
+ end
+
+ # Creates a job preconfigured with the given options. You can call
+ # perform_later with the job arguments to enqueue the job with the
+ # preconfigured options
+ #
+ # ==== Options
+ # * <tt>:wait</tt> - Enqueues the job with the specified delay
+ # * <tt>:wait_until</tt> - Enqueues the job at the time specified
+ # * <tt>:queue</tt> - Enqueues the job on the specified queue
+ #
+ # ==== Examples
+ #
+ # VideoJob.set(queue: :some_queue).perform_later(Video.last)
+ # VideoJob.set(wait: 5.minutes).perform_later(Video.last)
+ # VideoJob.set(wait_until: Time.tomorroe).perform_later(Video.last)
+ # VideoJob.set(queue: :some_queue, wait: 5.minutes).perform_later(Video.last)
+ # VideoJob.set(queue: :some_queue, wait_until: Time.tomorroe).perform_later(Video.last)
+ def set(options={})
+ ConfiguredJob.new(self, options)
+ end
+ end
+
+ # Creates a new job instance. Takes as arguments the arguments that
+ # will be passed to the perform method.
+ def initialize(*arguments)
+ @arguments = arguments
+ @job_id = SecureRandom.uuid
+ @queue_name = self.class.queue_name
+ end
+
+ # Returns a hash with the job data that can safely be passed to the
+ # queueing adapter.
+ def serialize
+ {
+ 'job_class' => self.class.name,
+ 'job_id' => job_id,
+ 'queue_name' => queue_name,
+ 'arguments' => serialize_arguments(arguments)
+ }
+ end
+
+ private
+ def deserialize_arguments_if_needed
+ if defined?(@serialized_arguments) && @serialized_arguments.present?
+ @arguments = deserialize_arguments(@serialized_arguments)
+ @serialized_arguments = nil
+ end
+ end
+
+ def serialize_arguments(serialized_args)
+ Arguments.serialize(serialized_args)
+ end
+
+ def deserialize_arguments(serialized_args)
+ Arguments.deserialize(serialized_args)
+ end
+ end
+end
+
+
+
diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb
index 3d00d51867..e8bc44cbc4 100644
--- a/activejob/lib/active_job/enqueuing.rb
+++ b/activejob/lib/active_job/enqueuing.rb
@@ -12,60 +12,64 @@ module ActiveJob
#
# 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, job.job_id, *Arguments.serialize(args)
- end
- end
- 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
+ def perform_later(*args)
+ job_or_instantiate(*args).enqueue
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
-
- job.run_callbacks :enqueue do
- queue_adapter.enqueue_at self, timestamp.to_f, job.job_id, *Arguments.serialize(args)
- end
+ protected
+ def job_or_instantiate(*args)
+ args.first.is_a?(self) ? args.first : new(*args)
end
- end
- end
-
- included do
- attr_accessor :arguments
- attr_accessor :enqueued_at
- end
-
- def initialize(arguments = nil)
- @arguments = arguments
end
- def retry_now
- self.class.enqueue(*arguments)
+ # Reschedule the job to be re-executed. This is usefull in combination
+ # with the +rescue_from+ option. When you rescue an exception from your job
+ # you can ask Active Job to retry performing your job.
+ #
+ # ==== Options
+ # * <tt>:wait</tt> - Enqueues the job with the specified delay
+ # * <tt>:wait_until</tt> - Enqueues the job at the time specified
+ # * <tt>:queue</tt> - Enqueues the job on the specified queue
+ #
+ # ==== Examples
+ #
+ # class SiteScrapperJob < ActiveJob::Base
+ # rescue_from(ErrorLoadingSite) do
+ # retry_job queue: :low_priority
+ # end
+ # def perform(*args)
+ # # raise ErrorLoadingSite if cannot scrape
+ # end
+ # end
+ def retry_job(options={})
+ enqueue options
end
- def retry_in(interval)
- self.class.enqueue_in interval, *arguments
- end
-
- def retry_at(timestamp)
- self.class.enqueue_at timestamp, *arguments
+ # Equeue the job to be performed by the queue adapter.
+ #
+ # ==== Options
+ # * <tt>:wait</tt> - Enqueues the job with the specified delay
+ # * <tt>:wait_until</tt> - Enqueues the job at the time specified
+ # * <tt>:queue</tt> - Enqueues the job on the specified queue
+ #
+ # ==== Examples
+ #
+ # my_job_instance.enqueue
+ # my_job_instance.enqueue wait: 5.minutes
+ # my_job_instance.enqueue queue: :important
+ # my_job_instance.enqueue wait_until: Date.tomorrow.midnight
+ def enqueue(options={})
+ self.scheduled_at = options[:wait].seconds.from_now.to_f if options[:wait]
+ self.scheduled_at = options[:wait_until].to_f if options[:wait_until]
+ self.queue_name = self.class.queue_name_from_part(options[:queue]) if options[:queue]
+ run_callbacks :enqueue do
+ if self.scheduled_at
+ self.class.queue_adapter.enqueue_at self, self.scheduled_at
+ else
+ self.class.queue_adapter.enqueue self
+ end
+ end
+ self
end
end
end
diff --git a/activejob/lib/active_job/execution.rb b/activejob/lib/active_job/execution.rb
index 0e7b5bdd72..d6d67c46e3 100644
--- a/activejob/lib/active_job/execution.rb
+++ b/activejob/lib/active_job/execution.rb
@@ -4,15 +4,29 @@ require 'active_job/arguments'
module ActiveJob
module Execution
extend ActiveSupport::Concern
+ include ActiveSupport::Rescuable
- included do
- include ActiveSupport::Rescuable
- end
+ module ClassMethods
+ # Performs the job immediately.
+ #
+ # MyJob.perform_now("mike")
+ #
+ def perform_now(*args)
+ job_or_instantiate(*args).perform_now
+ end
- def execute(job_id, *serialized_args)
- self.job_id = job_id
- self.arguments = deserialize_arguments(serialized_args)
+ def execute(job_data) #:nodoc:
+ job = deserialize(job_data)
+ job.perform_now
+ end
+ end
+ # Performs the job immediately. The job is not sent to the queueing adapter
+ # and will block the execution until it's finished.
+ #
+ # MyJob.new(*args).perform_now
+ def perform_now
+ deserialize_arguments_if_needed
run_callbacks :perform do
perform(*arguments)
end
@@ -23,11 +37,5 @@ module ActiveJob
def perform(*)
fail NotImplementedError
end
-
- private
- def deserialize_arguments(serialized_args)
- Arguments.deserialize(serialized_args)
- end
-
end
end
diff --git a/activejob/lib/active_job/gem_version.rb b/activejob/lib/active_job/gem_version.rb
index 2545e09845..1f25d48326 100644
--- a/activejob/lib/active_job/gem_version.rb
+++ b/activejob/lib/active_job/gem_version.rb
@@ -8,7 +8,7 @@ module ActiveJob
MAJOR = 4
MINOR = 2
TINY = 0
- PRE = "alpha"
+ PRE = "beta1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activejob/lib/active_job/identifier.rb b/activejob/lib/active_job/identifier.rb
deleted file mode 100644
index c7f522087d..0000000000
--- a/activejob/lib/active_job/identifier.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-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/activejob/lib/active_job/logging.rb b/activejob/lib/active_job/logging.rb
index ae098a80f3..962005cd15 100644
--- a/activejob/lib/active_job/logging.rb
+++ b/activejob/lib/active_job/logging.rb
@@ -17,7 +17,7 @@ module ActiveJob
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}
+ payload = {adapter: job.class.queue_adapter, job: job}
ActiveSupport::Notifications.instrument("perform_start.active_job", payload.dup)
ActiveSupport::Notifications.instrument("perform.active_job", payload) do
block.call
@@ -26,12 +26,12 @@ module ActiveJob
end
before_enqueue do |job|
- if job.enqueued_at
+ if job.scheduled_at
ActiveSupport::Notifications.instrument "enqueue_at.active_job",
- adapter: job.class.queue_adapter, job: job.class, job_id: job.job_id, args: job.arguments, timestamp: job.enqueued_at
+ adapter: job.class.queue_adapter, job: job
else
ActiveSupport::Notifications.instrument "enqueue.active_job",
- adapter: job.class.queue_adapter, job: job.class, job_id: job.job_id, args: job.arguments
+ adapter: job.class.queue_adapter, job: job
end
end
end
@@ -52,19 +52,31 @@ module ActiveJob
class LogSubscriber < ActiveSupport::LogSubscriber
def enqueue(event)
- info { "Enqueued #{event.payload[:job].name} (Job ID: #{event.payload[:job_id]}) to #{queue_name(event)}" + args_info(event) }
+ info do
+ job = event.payload[:job]
+ "Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)}" + args_info(job)
+ end
end
def enqueue_at(event)
- info { "Enqueued #{event.payload[:job].name} (Job ID: #{event.payload[:job_id]}) to #{queue_name(event)} at #{enqueued_at(event)}" + args_info(event) }
+ info do
+ job = event.payload[:job]
+ "Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)} at #{scheduled_at(event)}" + args_info(job)
+ end
end
def perform_start(event)
- info { "Performing #{event.payload[:job].name} from #{queue_name(event)}" + args_info(event) }
+ info do
+ job = event.payload[:job]
+ "Performing #{job.class.name} from #{queue_name(event)}" + args_info(job)
+ end
end
def perform(event)
- info { "Performed #{event.payload[:job].name} from #{queue_name(event)} in #{event.duration.round(2).to_s}ms" }
+ info do
+ job = event.payload[:job]
+ "Performed #{job.class.name} from #{queue_name(event)} in #{event.duration.round(2).to_s}ms"
+ end
end
private
@@ -72,12 +84,12 @@ module ActiveJob
event.payload[:adapter].name.demodulize.remove('Adapter') + "(#{event.payload[:job].queue_name})"
end
- def args_info(event)
- event.payload[:args].any? ? " with arguments: #{event.payload[:args].map(&:inspect).join(", ")}" : ""
+ def args_info(job)
+ job.arguments.any? ? " with arguments: #{job.arguments.map(&:inspect).join(", ")}" : ""
end
- def enqueued_at(event)
- Time.at(event.payload[:timestamp]).utc
+ def scheduled_at(event)
+ Time.at(event.payload[:job].scheduled_at).utc
end
def logger
diff --git a/activejob/lib/active_job/queue_adapter.rb b/activejob/lib/active_job/queue_adapter.rb
index 13c23abce4..fb54aec75e 100644
--- a/activejob/lib/active_job/queue_adapter.rb
+++ b/activejob/lib/active_job/queue_adapter.rb
@@ -3,21 +3,27 @@ require 'active_support/core_ext/string/inflections'
module ActiveJob
module QueueAdapter
- mattr_reader(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter }
+ extend ActiveSupport::Concern
- def queue_adapter=(name_or_adapter)
- @@queue_adapter = \
- case name_or_adapter
- when Symbol, String
- load_adapter(name_or_adapter)
- when Class
- name_or_adapter
- end
- end
+ module ClassMethods
+ mattr_reader(:queue_adapter) { ActiveJob::QueueAdapters::InlineAdapter }
- private
- def load_adapter(name)
- "ActiveJob::QueueAdapters::#{name.to_s.camelize}Adapter".constantize
+ def queue_adapter=(name_or_adapter)
+ @@queue_adapter = \
+ case name_or_adapter
+ when :test
+ ActiveJob::QueueAdapters::TestAdapter.new
+ when Symbol, String
+ load_adapter(name_or_adapter)
+ when Class
+ name_or_adapter
+ end
end
+
+ private
+ def load_adapter(name)
+ "ActiveJob::QueueAdapters::#{name.to_s.camelize}Adapter".constantize
+ end
+ end
end
end \ No newline at end of file
diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb
index 007068ff0a..345b01ef00 100644
--- a/activejob/lib/active_job/queue_adapters.rb
+++ b/activejob/lib/active_job/queue_adapters.rb
@@ -12,5 +12,6 @@ module ActiveJob
autoload :SidekiqAdapter
autoload :SneakersAdapter
autoload :SuckerPunchAdapter
+ autoload :TestAdapter
end
end
diff --git a/activejob/lib/active_job/queue_adapters/backburner_adapter.rb b/activejob/lib/active_job/queue_adapters/backburner_adapter.rb
index 8d34155645..e1b00f1de7 100644
--- a/activejob/lib/active_job/queue_adapters/backburner_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/backburner_adapter.rb
@@ -4,20 +4,20 @@ module ActiveJob
module QueueAdapters
class BackburnerAdapter
class << self
- def enqueue(job, *args)
- Backburner::Worker.enqueue JobWrapper, [ job.name, *args ], queue: job.queue_name
+ def enqueue(job)
+ Backburner::Worker.enqueue JobWrapper, [ job.serialize ], queue: job.queue_name
end
- def enqueue_at(job, timestamp, *args)
- delay = Time.current.to_f - timestamp
- Backburner::Worker.enqueue JobWrapper, [ job.name, *args ], queue: job.queue_name, delay: delay
+ def enqueue_at(job, timestamp)
+ delay = timestamp - Time.current.to_f
+ Backburner::Worker.enqueue JobWrapper, [ job.serialize ], queue: job.queue_name, delay: delay
end
end
class JobWrapper
class << self
- def perform(job_name, *args)
- job_name.constantize.new.execute(*args)
+ def perform(job_data)
+ Base.execute job_data
end
end
end
diff --git a/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb b/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb
index a00569833a..658799edfc 100644
--- a/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb
@@ -4,18 +4,18 @@ module ActiveJob
module QueueAdapters
class DelayedJobAdapter
class << self
- def enqueue(job, *args)
- JobWrapper.new.delay(queue: job.queue_name).perform(job, *args)
+ def enqueue(job)
+ JobWrapper.new.delay(queue: job.queue_name).perform(job.serialize)
end
- def enqueue_at(job, timestamp, *args)
- JobWrapper.new.delay(queue: job.queue_name, run_at: Time.at(timestamp)).perform(job, *args)
+ def enqueue_at(job, timestamp)
+ JobWrapper.new.delay(queue: job.queue_name, run_at: Time.at(timestamp)).perform(job.serialize)
end
end
class JobWrapper
- def perform(job, *args)
- job.new.execute(*args)
+ def perform(job_data)
+ Base.execute(job_data)
end
end
end
diff --git a/activejob/lib/active_job/queue_adapters/inline_adapter.rb b/activejob/lib/active_job/queue_adapters/inline_adapter.rb
index 5805340fb0..fdefa38d5e 100644
--- a/activejob/lib/active_job/queue_adapters/inline_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/inline_adapter.rb
@@ -2,8 +2,8 @@ module ActiveJob
module QueueAdapters
class InlineAdapter
class << self
- def enqueue(job, *args)
- job.new.execute(*args)
+ def enqueue(job)
+ Base.execute(job.serialize)
end
def enqueue_at(*)
diff --git a/activejob/lib/active_job/queue_adapters/qu_adapter.rb b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
index 5cb741c094..f681fd7e8a 100644
--- a/activejob/lib/active_job/queue_adapters/qu_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
@@ -5,7 +5,7 @@ module ActiveJob
class QuAdapter
class << self
def enqueue(job, *args)
- Qu::Payload.new(klass: JobWrapper, args: [job.name, *args]).tap do |payload|
+ Qu::Payload.new(klass: JobWrapper, args: [job.serialize]).tap do |payload|
payload.instance_variable_set(:@queue, job.queue_name)
end.push
end
@@ -16,13 +16,12 @@ module ActiveJob
end
class JobWrapper < Qu::Job
- def initialize(job_name, *args)
- @job = job_name.constantize
- @args = args
+ def initialize(job_data)
+ @job_data = job_data
end
def perform
- @job.new.execute(*@args)
+ Base.execute @job_data
end
end
end
diff --git a/activejob/lib/active_job/queue_adapters/que_adapter.rb b/activejob/lib/active_job/queue_adapters/que_adapter.rb
index 9c84c74f83..51891ab07b 100644
--- a/activejob/lib/active_job/queue_adapters/que_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/que_adapter.rb
@@ -4,18 +4,18 @@ module ActiveJob
module QueueAdapters
class QueAdapter
class << self
- def enqueue(job, *args)
- JobWrapper.enqueue job.name, *args, queue: job.queue_name
+ def enqueue(job)
+ JobWrapper.enqueue job.serialize, queue: job.queue_name
end
- def enqueue_at(job, timestamp, *args)
- JobWrapper.enqueue job.name, *args, queue: job.queue_name, run_at: Time.at(timestamp)
+ def enqueue_at(job, timestamp)
+ JobWrapper.enqueue job.serialize, queue: job.queue_name, run_at: Time.at(timestamp)
end
end
class JobWrapper < Que::Job
- def run(job_name, *args)
- job_name.constantize.new.execute(*args)
+ def run(job_data)
+ Base.execute job_data
end
end
end
diff --git a/activejob/lib/active_job/queue_adapters/queue_classic_adapter.rb b/activejob/lib/active_job/queue_adapters/queue_classic_adapter.rb
index d74f8cf90e..ddcc868317 100644
--- a/activejob/lib/active_job/queue_adapters/queue_classic_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/queue_classic_adapter.rb
@@ -4,19 +4,34 @@ module ActiveJob
module QueueAdapters
class QueueClassicAdapter
class << self
- def enqueue(job, *args)
- QC::Queue.new(job.queue_name).enqueue("#{JobWrapper.name}.perform", job.name, *args)
+ def enqueue(job)
+ build_queue(job.queue_name).enqueue("#{JobWrapper.name}.perform", job.serialize)
end
- def enqueue_at(job, timestamp, *args)
- raise NotImplementedError
+ def enqueue_at(job, timestamp)
+ queue = build_queue(job.queue_name)
+ unless queue.respond_to?(:enqueue_at)
+ raise NotImplementedError, 'To be able to schedule jobs with Queue Classic ' \
+ 'the QC::Queue needs to respond to `enqueue_at(timestamp, method, *args)`. '
+ 'You can implement this yourself or you can use the queue_classic-later gem.'
+ end
+ queue.enqueue_at(timestamp, "#{JobWrapper.name}.perform", job.serialize)
+ end
+
+ # Builds a <tt>QC::Queue</tt> object to schedule jobs on.
+ #
+ # If you have a custom <tt>QC::Queue</tt> subclass you'll need to suclass
+ # <tt>ActiveJob::QueueAdapters::QueueClassicAdapter</tt> and override the
+ # <tt>build_queue</tt> method.
+ def build_queue(queue_name)
+ QC::Queue.new(queue_name)
end
end
class JobWrapper
class << self
- def perform(job_name, *args)
- job_name.constantize.new.execute(*args)
+ def perform(job_data)
+ Base.execute job_data
end
end
end
diff --git a/activejob/lib/active_job/queue_adapters/resque_adapter.rb b/activejob/lib/active_job/queue_adapters/resque_adapter.rb
index da8212fc9b..affa3bdfbc 100644
--- a/activejob/lib/active_job/queue_adapters/resque_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/resque_adapter.rb
@@ -16,23 +16,23 @@ module ActiveJob
module QueueAdapters
class ResqueAdapter
class << self
- def enqueue(job, *args)
- Resque.enqueue_to job.queue_name, JobWrapper, job.name, *args
+ def enqueue(job)
+ Resque.enqueue_to job.queue_name, JobWrapper, job.serialize
end
- def enqueue_at(job, timestamp, *args)
+ def enqueue_at(job, timestamp)
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
+ Resque.enqueue_at_with_queue job.queue_name, timestamp, JobWrapper, job.serialize
end
end
class JobWrapper
class << self
- def perform(job_name, *args)
- job_name.constantize.new.execute(*args)
+ def perform(job_data)
+ Base.execute job_data
end
end
end
diff --git a/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb b/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb
index 3e20bec44c..79926a1e24 100644
--- a/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb
@@ -4,20 +4,20 @@ module ActiveJob
module QueueAdapters
class SidekiqAdapter
class << self
- def enqueue(job, *args)
+ def enqueue(job)
#Sidekiq::Client does not support symbols as keys
Sidekiq::Client.push \
'class' => JobWrapper,
'queue' => job.queue_name,
- 'args' => [ job, *args ],
+ 'args' => [ job.serialize ],
'retry' => true
end
- def enqueue_at(job, timestamp, *args)
+ def enqueue_at(job, timestamp)
Sidekiq::Client.push \
'class' => JobWrapper,
'queue' => job.queue_name,
- 'args' => [ job, *args ],
+ 'args' => [ job.serialize ],
'retry' => true,
'at' => timestamp
end
@@ -26,8 +26,8 @@ module ActiveJob
class JobWrapper
include Sidekiq::Worker
- def perform(job_name, *args)
- job_name.constantize.new.execute(*args)
+ def perform(job_data)
+ Base.execute job_data
end
end
end
diff --git a/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb b/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
index 48b3df6a46..1ab0a87485 100644
--- a/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/sneakers_adapter.rb
@@ -7,25 +7,25 @@ module ActiveJob
@monitor = Monitor.new
class << self
- def enqueue(job, *args)
+ def enqueue(job)
@monitor.synchronize do
JobWrapper.from_queue job.queue_name
- JobWrapper.enqueue ActiveSupport::JSON.encode([ job.name, *args ])
+ JobWrapper.enqueue ActiveSupport::JSON.encode(job.serialize)
end
end
- def enqueue_at(job, timestamp, *args)
+ def enqueue_at(job, timestamp)
raise NotImplementedError
end
end
class JobWrapper
include Sneakers::Worker
- from_queue 'active_jobs_default'
+ from_queue 'default'
def work(msg)
- job_name, *args = ActiveSupport::JSON.decode(msg)
- job_name.constantize.new.execute(*args)
+ job_data = ActiveSupport::JSON.decode(msg)
+ Base.execute job_data
ack!
end
end
diff --git a/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb
index 16f05744f3..b19a38093f 100644
--- a/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb
@@ -4,11 +4,11 @@ module ActiveJob
module QueueAdapters
class SuckerPunchAdapter
class << self
- def enqueue(job, *args)
- JobWrapper.new.async.perform job, *args
+ def enqueue(job)
+ JobWrapper.new.async.perform job.serialize
end
- def enqueue_at(job, timestamp, *args)
+ def enqueue_at(job, timestamp)
raise NotImplementedError
end
end
@@ -16,8 +16,8 @@ module ActiveJob
class JobWrapper
include SuckerPunch::Job
- def perform(job, *args)
- job.new.execute(*args)
+ def perform(job_data)
+ Base.execute job_data
end
end
end
diff --git a/activejob/lib/active_job/queue_adapters/test_adapter.rb b/activejob/lib/active_job/queue_adapters/test_adapter.rb
new file mode 100644
index 0000000000..b9997efddf
--- /dev/null
+++ b/activejob/lib/active_job/queue_adapters/test_adapter.rb
@@ -0,0 +1,76 @@
+module ActiveJob
+ module QueueAdapters
+ class TestAdapter
+ attr_accessor(:perform_enqueued_jobs) { false }
+ attr_accessor(:perform_enqueued_at_jobs) { false }
+ delegate :name, to: :class
+
+ # Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
+ def enqueued_jobs
+ @enqueued_jobs ||= []
+ end
+
+ # Allows you to overwrite the default enqueued jobs store from an array to some
+ # other object. If you just want to clear the store,
+ # call ActiveJob::QueueAdapters::TestAdapter.enqueued_jobs.clear.
+ #
+ # If you place another object here, please make sure it responds to:
+ #
+ # * << (message)
+ # * clear
+ # * length
+ # * size
+ # * and other common Array methods
+ def enqueued_jobs=(val)
+ @enqueued_jobs = val
+ end
+
+ # Provides a store of all the performed jobs with the TestAdapter so you can check them.
+ def performed_jobs
+ @performed_jobs ||= []
+ end
+
+ # Allows you to overwrite the default performed jobs store from an array to some
+ # other object. If you just want to clear the store,
+ # call ActiveJob::QueueAdapters::TestAdapter.performed_jobs.clear.
+ #
+ # If you place another object here, please make sure it responds to:
+ #
+ # * << (message)
+ # * clear
+ # * length
+ # * size
+ # * and other common Array methods
+ def performed_jobs=(val)
+ @performed_jobs = val
+ end
+
+ def enqueue(job)
+ if perform_enqueued_jobs?
+ performed_jobs << {job: job.class, args: job.arguments, queue: job.queue_name}
+ job.perform_now
+ else
+ enqueued_jobs << {job: job.class, args: job.arguments, queue: job.queue_name}
+ end
+ end
+
+ def enqueue_at(job, timestamp)
+ if perform_enqueued_at_jobs?
+ performed_jobs << {job: job.class, args: job.arguments, queue: job.queue_name, at: timestamp}
+ job.perform_now
+ else
+ enqueued_jobs << {job: job.class, args: job.arguments, queue: job.queue_name, at: timestamp}
+ end
+ end
+
+ private
+ def perform_enqueued_jobs?
+ perform_enqueued_jobs
+ end
+
+ def perform_enqueued_at_jobs?
+ perform_enqueued_at_jobs
+ end
+ end
+ end
+end
diff --git a/activejob/lib/active_job/queue_name.rb b/activejob/lib/active_job/queue_name.rb
index 9698835b6e..45acb71605 100644
--- a/activejob/lib/active_job/queue_name.rb
+++ b/activejob/lib/active_job/queue_name.rb
@@ -6,16 +6,33 @@ module ActiveJob
mattr_accessor(:queue_name_prefix)
mattr_accessor(:default_queue_name) { "default" }
- def queue_as(part_name)
+ def queue_as(part_name=nil, &block)
+ if block_given?
+ self.queue_name = block
+ else
+ self.queue_name = queue_name_from_part(part_name)
+ end
+ end
+
+ def queue_name_from_part(part_name) #:nodoc:
queue_name = part_name.to_s.presence || default_queue_name
name_parts = [queue_name_prefix.presence, queue_name]
- self.queue_name = name_parts.compact.join('_')
+ name_parts.compact.join('_')
end
end
included do
- class_attribute :queue_name
+ class_attribute :queue_name, instance_accessor: false
self.queue_name = default_queue_name
end
+
+ # Returns the name of the queue the job will be run on
+ def queue_name
+ if @queue_name.is_a?(Proc)
+ @queue_name = self.class.queue_name_from_part(instance_exec(&@queue_name))
+ end
+ @queue_name
+ end
+
end
end
diff --git a/activejob/lib/active_job/test_case.rb b/activejob/lib/active_job/test_case.rb
new file mode 100644
index 0000000000..d894a7b5cd
--- /dev/null
+++ b/activejob/lib/active_job/test_case.rb
@@ -0,0 +1,7 @@
+require 'active_support/test_case'
+
+module ActiveJob
+ class TestCase < ActiveSupport::TestCase
+ include ActiveJob::TestHelper
+ end
+end
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
new file mode 100644
index 0000000000..fa0576669e
--- /dev/null
+++ b/activejob/lib/active_job/test_helper.rb
@@ -0,0 +1,196 @@
+module ActiveJob
+ # Provides helper methods for testing Active Job
+ module TestHelper
+ extend ActiveSupport::Concern
+
+ included do
+ def before_setup
+ @old_queue_adapter = queue_adapter
+ ActiveJob::Base.queue_adapter = :test
+ clear_enqueued_jobs
+ clear_performed_jobs
+ super
+ end
+
+ def after_teardown
+ super
+ ActiveJob::Base.queue_adapter = @old_queue_adapter
+ end
+
+ # Asserts that the number of enqueued jobs matches the given number.
+ #
+ # def test_jobs
+ # assert_enqueued_jobs 0
+ # HelloJob.perform_later('david')
+ # assert_enqueued_jobs 1
+ # HelloJob.perform_later('abdelkader')
+ # assert_enqueued_jobs 2
+ # end
+ #
+ # If a block is passed, that block should cause the specified number of
+ # jobs to be enqueued.
+ #
+ # def test_jobs_again
+ # assert_enqueued_jobs 1 do
+ # HelloJob.perform_later('cristian')
+ # end
+ #
+ # assert_enqueued_jobs 2 do
+ # HelloJob.perform_later('aaron')
+ # HelloJob.perform_later('rafael')
+ # end
+ # end
+ def assert_enqueued_jobs(number)
+ if block_given?
+ original_count = enqueued_jobs.size
+ yield
+ new_count = enqueued_jobs.size
+ assert_equal original_count + number, new_count,
+ "#{number} jobs expected, but #{new_count - original_count} were enqueued"
+ else
+ enqueued_jobs_size = enqueued_jobs.size
+ assert_equal number, enqueued_jobs_size, "#{number} jobs expected, but #{enqueued_jobs_size} were enqueued"
+ end
+ end
+
+ # Assert that no job have been enqueued.
+ #
+ # def test_jobs
+ # assert_no_enqueued_jobs
+ # HelloJob.perform_later('jeremy')
+ # assert_enqueued_jobs 1
+ # end
+ #
+ # If a block is passed, that block should not cause any job to be enqueued.
+ #
+ # def test_jobs_again
+ # assert_no_enqueued_jobs do
+ # # No job should be enqueued from this block
+ # end
+ # end
+ #
+ # Note: This assertion is simply a shortcut for:
+ #
+ # assert_enqueued_jobs 0
+ def assert_no_enqueued_jobs(&block)
+ assert_enqueued_jobs 0, &block
+ end
+
+ # Asserts that the number of performed jobs matches the given number.
+ #
+ # def test_jobs
+ # assert_performed_jobs 0
+ # HelloJob.perform_later('xavier')
+ # assert_performed_jobs 1
+ # HelloJob.perform_later('yves')
+ # assert_performed_jobs 2
+ # end
+ #
+ # If a block is passed, that block should cause the specified number of
+ # jobs to be performed.
+ #
+ # def test_jobs_again
+ # assert_performed_jobs 1 do
+ # HelloJob.perform_later('robin')
+ # end
+ #
+ # assert_performed_jobs 2 do
+ # HelloJob.perform_later('carlos')
+ # HelloJob.perform_later('sean')
+ # end
+ # end
+ def assert_performed_jobs(number)
+ if block_given?
+ original_count = performed_jobs.size
+ yield
+ new_count = performed_jobs.size
+ assert_equal original_count + number, new_count,
+ "#{number} jobs expected, but #{new_count - original_count} were performed"
+ else
+ performed_jobs_size = performed_jobs.size
+ assert_equal number, performed_jobs_size, "#{number} jobs expected, but #{performed_jobs_size} were performed"
+ end
+ end
+
+ # Asserts that no jobs have been performed.
+ #
+ # def test_jobs
+ # assert_no_performed_jobs
+ # HelloJob.perform_later('matthew')
+ # assert_performed_jobs 1
+ # end
+ #
+ # If a block is passed, that block should not cause any job to be performed.
+ #
+ # def test_jobs_again
+ # assert_no_performed_jobs do
+ # # No job should be performed from this block
+ # end
+ # end
+ #
+ # Note: This assertion is simply a shortcut for:
+ #
+ # assert_performed_jobs 0
+ def assert_no_performed_jobs(&block)
+ assert_performed_jobs 0, &block
+ end
+
+ # Asserts that the job passed in the block has been enqueued with the given arguments.
+ #
+ # def assert_enqueued_job
+ # assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low') do
+ # MyJob.perform_later(1,2,3)
+ # end
+ # end
+ def assert_enqueued_with(args = {}, &_block)
+ original_enqueued_jobs = enqueued_jobs.dup
+ clear_enqueued_jobs
+ args.assert_valid_keys(:job, :args, :at, :queue)
+ yield
+ matching_job = enqueued_jobs.any? do |job|
+ args.all? { |key, value| value == job[key] }
+ end
+ assert matching_job
+ ensure
+ queue_adapter.enqueued_jobs = original_enqueued_jobs + enqueued_jobs
+ end
+
+ # Asserts that the job passed in the block has been performed with the given arguments.
+ #
+ # def test_assert_performed_with
+ # assert_performed_with(job: MyJob, args: [1,2,3], queue: 'high') do
+ # MyJob.perform_later(1,2,3)
+ # end
+ # end
+ def assert_performed_with(args = {}, &_block)
+ original_performed_jobs = performed_jobs.dup
+ clear_performed_jobs
+ args.assert_valid_keys(:job, :args, :at, :queue)
+ yield
+ matching_job = performed_jobs.any? do |job|
+ args.all? { |key, value| value == job[key] }
+ end
+ assert matching_job, "No performed job found with #{args}"
+ ensure
+ queue_adapter.performed_jobs = original_performed_jobs + performed_jobs
+ end
+
+ def queue_adapter
+ ActiveJob::Base.queue_adapter
+ end
+
+ delegate :enqueued_jobs, :enqueued_jobs=,
+ :performed_jobs, :performed_jobs=,
+ to: :queue_adapter
+
+ private
+ def clear_enqueued_jobs
+ enqueued_jobs.clear
+ end
+
+ def clear_performed_jobs
+ performed_jobs.clear
+ end
+ end
+ end
+end
diff --git a/activejob/test/cases/callbacks_test.rb b/activejob/test/cases/callbacks_test.rb
index 9a0657ee89..9af2380767 100644
--- a/activejob/test/cases/callbacks_test.rb
+++ b/activejob/test/cases/callbacks_test.rb
@@ -5,7 +5,8 @@ require 'active_support/core_ext/object/inclusion'
class CallbacksTest < ActiveSupport::TestCase
test 'perform callbacks' do
- performed_callback_job = CallbackJob.new.tap { |j| j.execute("A-JOB-ID") }
+ performed_callback_job = CallbackJob.new("A-JOB-ID")
+ performed_callback_job.perform_now
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
@@ -13,7 +14,7 @@ class CallbacksTest < ActiveSupport::TestCase
end
test 'enqueue callbacks' do
- enqueued_callback_job = CallbackJob.enqueue
+ enqueued_callback_job = CallbackJob.perform_later
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
diff --git a/activejob/test/cases/job_serialization_test.rb b/activejob/test/cases/job_serialization_test.rb
index fc1b77744c..db22783030 100644
--- a/activejob/test/cases/job_serialization_test.rb
+++ b/activejob/test/cases/job_serialization_test.rb
@@ -9,7 +9,7 @@ class JobSerializationTest < ActiveSupport::TestCase
end
test 'serialize job with gid' do
- GidJob.enqueue @person
+ GidJob.perform_later @person
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 888c183a0b..9c56ee08b6 100644
--- a/activejob/test/cases/logging_test.rb
+++ b/activejob/test/cases/logging_test.rb
@@ -42,34 +42,43 @@ class AdapterTest < ActiveSupport::TestCase
def test_uses_active_job_as_tag
- HelloJob.enqueue "Cristian"
+ HelloJob.perform_later "Cristian"
assert_match(/\[ActiveJob\]/, @logger.messages)
end
+ def test_uses_job_name_as_tag
+ LoggingJob.perform_later "Dummy"
+ assert_match(/\[LoggingJob\]/, @logger.messages)
+ end
+
+ def test_uses_job_id_as_tag
+ LoggingJob.perform_later "Dummy"
+ assert_match(/\[LOGGING-JOB-ID\]/, @logger.messages)
+ end
+
+ def test_logs_correct_queue_name
+ original_queue_name = LoggingJob.queue_name
+ LoggingJob.queue_as :php_jobs
+ LoggingJob.perform_later("Dummy")
+ assert_match(/to .*?\(php_jobs\).*/, @logger.messages)
+ ensure
+ LoggingJob.queue_name = original_queue_name
+ end
+
def test_enqueue_job_logging
- HelloJob.enqueue "Cristian"
+ HelloJob.perform_later "Cristian"
assert_match(/Enqueued HelloJob \(Job ID: .*?\) to .*?:.*Cristian/, @logger.messages)
end
def test_perform_job_logging
- LoggingJob.enqueue "Dummy"
+ LoggingJob.perform_later "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
+ NestedJob.perform_later
assert_match(/\[LoggingJob\] \[.*?\]/, @logger.messages)
assert_match(/\[ActiveJob\] Enqueued NestedJob \(Job ID: .*\) to/, @logger.messages)
assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Performing NestedJob from/, @logger.messages)
@@ -81,14 +90,14 @@ class AdapterTest < ActiveSupport::TestCase
end
def test_enqueue_at_job_logging
- HelloJob.enqueue_at 1, "Cristian"
+ HelloJob.set(wait_until: 24.hours.from_now).perform_later "Cristian"
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"
+ HelloJob.set(wait: 2.seconds).perform_later "Cristian"
assert_match(/Enqueued HelloJob \(Job ID: .*\) to .*? at.*Cristian/, @logger.messages)
rescue NotImplementedError
skip
diff --git a/activejob/test/cases/parameters_test.rb b/activejob/test/cases/parameters_test.rb
index 78853c51e1..92f835af5d 100644
--- a/activejob/test/cases/parameters_test.rb
+++ b/activejob/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.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) ] ])
+ assert_equal [ { "a" => Person.find(5).to_gid.to_s }.with_indifferent_access ], ActiveJob::Arguments.serialize([ { a: Person.find(5) } ])
+ assert_equal [ [ Person.find(5).to_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.to_s ], ActiveJob::Arguments.serialize([ Person.find(5) ])
+ assert_equal [ Person.find(5).to_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.to_s ], ActiveJob::Arguments.serialize([ 3, Person.find(5) ])
+ assert_equal [ 3, Person.find(5).to_gid.to_s ], ActiveJob::Arguments.serialize([ 3, Person.find(5) ])
end
end
@@ -59,19 +59,19 @@ class ParameterDeserializationTest < ActiveSupport::TestCase
end
test 'should deserialize records with global id' do
- assert_equal [ Person.find(5) ], ActiveJob::Arguments.deserialize([ Person.find(5).gid ])
+ assert_equal [ Person.find(5) ], ActiveJob::Arguments.deserialize([ Person.find(5).to_gid ])
end
test 'should serialize values and records together' do
- assert_equal [ 3, Person.find(5) ], ActiveJob::Arguments.deserialize([ 3, Person.find(5).gid ])
+ assert_equal [ 3, Person.find(5) ], ActiveJob::Arguments.deserialize([ 3, Person.find(5).to_gid ])
end
test 'should dive deep when deserialising arrays' do
- assert_equal [ [ 3, Person.find(5) ] ], ActiveJob::Arguments.deserialize([ [ 3, Person.find(5).gid ] ])
+ assert_equal [ [ 3, Person.find(5) ] ], ActiveJob::Arguments.deserialize([ [ 3, Person.find(5).to_gid ] ])
end
test 'should dive deep when deserialising hashes' do
- assert_equal [ { "5" => Person.find(5) } ], ActiveJob::Arguments.deserialize([ { "5" => Person.find(5).gid } ])
+ assert_equal [ { "5" => Person.find(5) } ], ActiveJob::Arguments.deserialize([ { "5" => Person.find(5).to_gid } ])
end
end
diff --git a/activejob/test/cases/queue_naming_test.rb b/activejob/test/cases/queue_naming_test.rb
index fdfd1afceb..4052477543 100644
--- a/activejob/test/cases/queue_naming_test.rb
+++ b/activejob/test/cases/queue_naming_test.rb
@@ -8,20 +8,38 @@ class QueueNamingTest < ActiveSupport::TestCase
assert_equal "default", HelloJob.queue_name
end
- test 'name appended in job' do
+ test 'uses given queue name job' do
begin
+ original_queue_name = HelloJob.queue_name
HelloJob.queue_as :greetings
- LoggingJob.queue_as :bookkeeping
+ assert_equal "greetings", HelloJob.new.queue_name
+ ensure
+ HelloJob.queue_name = original_queue_name
+ end
+ end
- assert_equal "default", NestedJob.queue_name
- assert_equal "greetings", HelloJob.queue_name
- assert_equal "bookkeeping", LoggingJob.queue_name
+ test 'evals block given to queue_as to determine queue' do
+ begin
+ original_queue_name = HelloJob.queue_name
+ HelloJob.queue_as { :another }
+ assert_equal "another", HelloJob.new.queue_name
ensure
- HelloJob.queue_name = LoggingJob.queue_name = ActiveJob::Base.default_queue_name
+ HelloJob.queue_name = original_queue_name
end
end
- test 'should prefix the queue name' do
+ test 'can use arguments to determine queue_name in queue_as block' do
+ begin
+ original_queue_name = HelloJob.queue_name
+ HelloJob.queue_as { self.arguments.first=='1' ? :one : :two }
+ assert_equal "one", HelloJob.new('1').queue_name
+ assert_equal "two", HelloJob.new('3').queue_name
+ ensure
+ HelloJob.queue_name = original_queue_name
+ end
+ end
+
+ test 'queu_name_prefix prepended to the queue name' do
begin
original_queue_name_prefix = ActiveJob::Base.queue_name_prefix
original_queue_name = HelloJob.queue_name
@@ -35,4 +53,9 @@ class QueueNamingTest < ActiveSupport::TestCase
end
end
+ test 'uses queue passed to #set' do
+ job = HelloJob.set(queue: :some_queue).perform_later
+ assert_equal "some_queue", job.queue_name
+ end
+
end
diff --git a/activejob/test/cases/queuing_test.rb b/activejob/test/cases/queuing_test.rb
index f020316d7e..0eeabbf693 100644
--- a/activejob/test/cases/queuing_test.rb
+++ b/activejob/test/cases/queuing_test.rb
@@ -9,18 +9,18 @@ class QueuingTest < ActiveSupport::TestCase
end
test 'run queued job' do
- HelloJob.enqueue
+ HelloJob.perform_later
assert_equal "David says hello", JobBuffer.last_value
end
test 'run queued job with arguments' do
- HelloJob.enqueue "Jamie"
+ HelloJob.perform_later "Jamie"
assert_equal "Jamie says hello", JobBuffer.last_value
end
test 'run queued job later' do
begin
- result = HelloJob.enqueue_at 1.second.ago, "Jamie"
+ result = HelloJob.set(wait_until: 1.second.ago).perform_later "Jamie"
assert result
rescue NotImplementedError
skip
@@ -28,15 +28,15 @@ class QueuingTest < ActiveSupport::TestCase
end
test 'job returned by enqueue has the arguments available' do
- job = HelloJob.enqueue "Jamie"
+ job = HelloJob.perform_later "Jamie"
assert_equal [ "Jamie" ], job.arguments
end
- test 'job returned by enqueue_at has the timestamp available' do
+ test 'job returned by perform_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
+ job = HelloJob.set(wait_until: Time.utc(2014, 1, 1)).perform_later
+ assert_equal Time.utc(2014, 1, 1).to_f, job.scheduled_at
rescue NotImplementedError
skip
end
diff --git a/activejob/test/cases/rescue_test.rb b/activejob/test/cases/rescue_test.rb
index d9ea3c91d7..1b6c2e9fac 100644
--- a/activejob/test/cases/rescue_test.rb
+++ b/activejob/test/cases/rescue_test.rb
@@ -10,22 +10,27 @@ class RescueTest < ActiveSupport::TestCase
end
test 'rescue perform exception with retry' do
- job = RescueJob.new
- job.execute(SecureRandom.uuid, "david")
+ job = RescueJob.new("david")
+ job.perform_now
assert_equal [ "rescued from ArgumentError", "performed beautifully" ], JobBuffer.values
end
test 'let through unhandled perform exception' do
- job = RescueJob.new
+ job = RescueJob.new("other")
assert_raises(RescueJob::OtherError) do
- job.execute(SecureRandom.uuid, "other")
+ job.perform_now
end
end
test 'rescue from deserialization errors' do
- RescueJob.enqueue Person.new(404)
+ RescueJob.perform_later 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
+
+ test "should not wrap DeserializationError in DeserializationError" do
+ RescueJob.perform_later [Person.new(404)]
+ assert_includes JobBuffer.values, 'DeserializationError original exception was Person::RecordNotFound'
+ end
end
diff --git a/activejob/test/cases/test_case_test.rb b/activejob/test/cases/test_case_test.rb
new file mode 100644
index 0000000000..1d0fdbd22d
--- /dev/null
+++ b/activejob/test/cases/test_case_test.rb
@@ -0,0 +1,14 @@
+require 'helper'
+require 'jobs/hello_job'
+require 'jobs/logging_job'
+require 'jobs/nested_job'
+
+class ActiveJobTestCaseTest < ActiveJob::TestCase
+ def test_include_helper
+ assert_includes self.class.ancestors, ActiveJob::TestHelper
+ end
+
+ def test_set_test_adapter
+ assert_instance_of ActiveJob::QueueAdapters::TestAdapter, self.queue_adapter
+ end
+end
diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb
new file mode 100644
index 0000000000..eab540bb6c
--- /dev/null
+++ b/activejob/test/cases/test_helper_test.rb
@@ -0,0 +1,214 @@
+require 'helper'
+require 'active_support/core_ext/time'
+require 'active_support/core_ext/date'
+require 'jobs/hello_job'
+require 'jobs/logging_job'
+require 'jobs/nested_job'
+
+class EnqueuedJobsTest < ActiveJob::TestCase
+ setup { queue_adapter.perform_enqueued_at_jobs = true }
+
+ def test_assert_enqueued_jobs
+ assert_nothing_raised do
+ assert_enqueued_jobs 1 do
+ HelloJob.perform_later('david')
+ end
+ end
+ end
+
+ def test_repeated_enqueued_jobs_calls
+ assert_nothing_raised do
+ assert_enqueued_jobs 1 do
+ HelloJob.perform_later('abdelkader')
+ end
+ end
+
+ assert_nothing_raised do
+ assert_enqueued_jobs 2 do
+ HelloJob.perform_later('sean')
+ HelloJob.perform_later('yves')
+ end
+ end
+ end
+
+ def test_assert_enqueued_jobs_with_no_block
+ assert_nothing_raised do
+ HelloJob.perform_later('rafael')
+ assert_enqueued_jobs 1
+ end
+
+ assert_nothing_raised do
+ HelloJob.perform_later('aaron')
+ HelloJob.perform_later('matthew')
+ assert_enqueued_jobs 3
+ end
+ end
+
+ def test_assert_no_enqueued_jobs
+ assert_nothing_raised do
+ assert_no_enqueued_jobs do
+ # Scheduled jobs are being performed in this context
+ HelloJob.set(wait_until: Date.tomorrow.noon).perform_later('godfrey')
+ end
+ end
+ end
+
+ def test_assert_enqueued_jobs_too_few_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_jobs 2 do
+ HelloJob.perform_later('xavier')
+ end
+ end
+
+ assert_match(/2 .* but 1/, error.message)
+ end
+
+ def test_assert_enqueued_jobs_too_many_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_jobs 1 do
+ HelloJob.perform_later('cristian')
+ HelloJob.perform_later('guillermo')
+ end
+ end
+
+ assert_match(/1 .* but 2/, error.message)
+ end
+
+ def test_assert_no_enqueued_jobs_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_enqueued_jobs do
+ HelloJob.perform_later('jeremy')
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_enqueued_job
+ assert_enqueued_with(job: LoggingJob, queue: 'default') do
+ NestedJob.set(wait_until: Date.tomorrow.noon).perform_later
+ end
+ end
+
+ def test_assert_enqueued_job_failure
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_with(job: LoggingJob, queue: 'default') do
+ NestedJob.perform_later
+ end
+ end
+
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_with(job: NestedJob, queue: 'low') do
+ NestedJob.perform_later
+ end
+ end
+ end
+
+ def test_assert_enqueued_job_args
+ assert_raise ArgumentError do
+ assert_enqueued_with(class: LoggingJob) do
+ NestedJob.set(wait_until: Date.tomorrow.noon).perform_later
+ end
+ end
+ end
+end
+
+class PerformedJobsTest < ActiveJob::TestCase
+ setup { queue_adapter.perform_enqueued_jobs = true }
+
+ def test_assert_performed_jobs
+ assert_nothing_raised do
+ assert_performed_jobs 1 do
+ HelloJob.perform_later('david')
+ end
+ end
+ end
+
+ def test_repeated_performed_jobs_calls
+ assert_nothing_raised do
+ assert_performed_jobs 1 do
+ HelloJob.perform_later('abdelkader')
+ end
+ end
+
+ assert_nothing_raised do
+ assert_performed_jobs 2 do
+ HelloJob.perform_later('sean')
+ HelloJob.perform_later('yves')
+ end
+ end
+ end
+
+ def test_assert_performed_jobs_with_no_block
+ assert_nothing_raised do
+ HelloJob.perform_later('rafael')
+ assert_performed_jobs 1
+ end
+
+ assert_nothing_raised do
+ HelloJob.perform_later('aaron')
+ HelloJob.perform_later('matthew')
+ assert_performed_jobs 3
+ end
+ end
+
+ def test_assert_no_performed_jobs
+ assert_nothing_raised do
+ assert_no_performed_jobs do
+ # Scheduled jobs are being enqueued in this context
+ HelloJob.set(wait_until: Date.tomorrow.noon).perform_later('godfrey')
+ end
+ end
+ end
+
+ def test_assert_performed_jobs_too_few_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_jobs 2 do
+ HelloJob.perform_later('xavier')
+ end
+ end
+
+ assert_match(/2 .* but 1/, error.message)
+ end
+
+ def test_assert_performed_jobs_too_many_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_jobs 1 do
+ HelloJob.perform_later('cristian')
+ HelloJob.perform_later('guillermo')
+ end
+ end
+
+ assert_match(/1 .* but 2/, error.message)
+ end
+
+ def test_assert_no_performed_jobs_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_performed_jobs do
+ HelloJob.perform_later('jeremy')
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_performed_job
+ assert_performed_with(job: NestedJob, queue: 'default') do
+ NestedJob.perform_later
+ end
+ end
+
+ def test_assert_performed_job_failure
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_with(job: LoggingJob, at: Date.tomorrow.noon, queue: 'default') do
+ NestedJob.set(wait_until: Date.tomorrow.noon).perform_later
+ end
+ end
+
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_with(job: NestedJob, at: Date.tomorrow.noon, queue: 'low') do
+ NestedJob.set(queue: 'low', wait_until: Date.tomorrow.noon).perform_later
+ end
+ end
+ end
+end
diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb
index ca67700273..ce22833b11 100644
--- a/activejob/test/helper.rb
+++ b/activejob/test/helper.rb
@@ -1,6 +1,7 @@
require File.expand_path('../../../load_paths', __FILE__)
require 'active_job'
+require 'support/job_buffer'
GlobalID.app = 'aj'
@@ -10,41 +11,20 @@ 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?)
+# Sidekiq doesn't work with MRI 1.9.3
+exit if sidekiq? && ruby_193?
-require "adapters/#{@adapter}"
+if ENV['AJ_INTEGRATION_TESTS']
+ require 'support/integration/helper'
+else
+ require "adapters/#{@adapter}"
+end
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
+ActiveSupport::TestCase.test_order = :random
diff --git a/activejob/test/integration/queuing_test.rb b/activejob/test/integration/queuing_test.rb
new file mode 100644
index 0000000000..779dedb53f
--- /dev/null
+++ b/activejob/test/integration/queuing_test.rb
@@ -0,0 +1,46 @@
+require 'helper'
+require 'jobs/logging_job'
+require 'active_support/core_ext/numeric/time'
+
+class QueuingTest < ActiveSupport::TestCase
+ test 'should run jobs enqueued on a listenting queue' do
+ TestJob.perform_later @id
+ wait_for_jobs_to_finish_for(5.seconds)
+ assert job_executed
+ end
+
+ test 'should not run jobs queued on a non-listenting queue' do
+ begin
+ skip if adapter_is?(:inline) || adapter_is?(:sucker_punch)
+ old_queue = TestJob.queue_name
+ TestJob.queue_as :some_other_queue
+ TestJob.perform_later @id
+ wait_for_jobs_to_finish_for(2.seconds)
+ assert_not job_executed
+ ensure
+ TestJob.queue_name = old_queue
+ end
+ end
+
+ test 'should not run job enqueued in the future' do
+ begin
+ TestJob.set(wait: 10.minutes).perform_later @id
+ wait_for_jobs_to_finish_for(5.seconds)
+ assert_not job_executed
+ rescue NotImplementedError
+ skip
+ end
+ end
+
+ test 'should run job enqueued in the future at the specified time' do
+ begin
+ TestJob.set(wait: 3.seconds).perform_later @id
+ wait_for_jobs_to_finish_for(2.seconds)
+ assert_not job_executed
+ wait_for_jobs_to_finish_for(10.seconds)
+ assert job_executed
+ rescue NotImplementedError
+ skip
+ end
+ end
+end
diff --git a/activejob/test/jobs/gid_job.rb b/activejob/test/jobs/gid_job.rb
index 35c2366ec4..e485bfa2dd 100644
--- a/activejob/test/jobs/gid_job.rb
+++ b/activejob/test/jobs/gid_job.rb
@@ -1,3 +1,5 @@
+require_relative '../support/job_buffer'
+
class GidJob < ActiveJob::Base
def perform(person)
JobBuffer.add("Person with ID: #{person.id}")
diff --git a/activejob/test/jobs/hello_job.rb b/activejob/test/jobs/hello_job.rb
index 4c6256af0d..022fa58e4a 100644
--- a/activejob/test/jobs/hello_job.rb
+++ b/activejob/test/jobs/hello_job.rb
@@ -1,3 +1,5 @@
+require_relative '../support/job_buffer'
+
class HelloJob < ActiveJob::Base
def perform(greeter = "David")
JobBuffer.add("#{greeter} says hello")
diff --git a/activejob/test/jobs/nested_job.rb b/activejob/test/jobs/nested_job.rb
index fd66f68991..8c4ec549a6 100644
--- a/activejob/test/jobs/nested_job.rb
+++ b/activejob/test/jobs/nested_job.rb
@@ -1,6 +1,6 @@
class NestedJob < ActiveJob::Base
def perform
- LoggingJob.enqueue "NestedJob"
+ LoggingJob.perform_later "NestedJob"
end
def job_id
diff --git a/activejob/test/jobs/rescue_job.rb b/activejob/test/jobs/rescue_job.rb
index e9cb37d1c4..f1b9c9349e 100644
--- a/activejob/test/jobs/rescue_job.rb
+++ b/activejob/test/jobs/rescue_job.rb
@@ -1,10 +1,12 @@
+require_relative '../support/job_buffer'
+
class RescueJob < ActiveJob::Base
class OtherError < StandardError; end
rescue_from(ArgumentError) do
JobBuffer.add('rescued from ArgumentError')
arguments[0] = "DIFFERENT!"
- retry_now
+ retry_job
end
rescue_from(ActiveJob::DeserializationError) do |e|
diff --git a/activejob/test/support/integration/adapters/backburner.rb b/activejob/test/support/integration/adapters/backburner.rb
new file mode 100644
index 0000000000..0cda36a273
--- /dev/null
+++ b/activejob/test/support/integration/adapters/backburner.rb
@@ -0,0 +1,38 @@
+module BackburnerJobsManager
+ def setup
+ ActiveJob::Base.queue_adapter = :backburner
+ Backburner.configure do |config|
+ config.logger = Rails.logger
+ end
+ unless can_run?
+ puts "Cannot run integration tests for backburner. To be able to run integration tests for backburner you need to install and start beanstalkd.\n"
+ exit
+ end
+ end
+
+ def clear_jobs
+ tube.clear
+ end
+
+ def start_workers
+ @thread = Thread.new { Backburner.work "integration-tests" } # backburner dasherizes the queue name
+ end
+
+ def stop_workers
+ @thread.kill
+ end
+
+ def tube
+ @tube ||= Beaneater::Tube.new(Backburner::Worker.connection, "backburner.worker.queue.integration-tests") # backburner dasherizes the queue name
+ end
+
+ def can_run?
+ begin
+ Backburner::Worker.connection.send :connect!
+ rescue => e
+ return false
+ end
+ true
+ end
+end
+
diff --git a/activejob/test/support/integration/adapters/delayed_job.rb b/activejob/test/support/integration/adapters/delayed_job.rb
new file mode 100644
index 0000000000..dbd0d1a4db
--- /dev/null
+++ b/activejob/test/support/integration/adapters/delayed_job.rb
@@ -0,0 +1,20 @@
+require 'delayed_job'
+require 'delayed_job_active_record'
+
+module DelayedJobJobsManager
+ def setup
+ ActiveJob::Base.queue_adapter = :delayed_job
+ end
+ def clear_jobs
+ Delayed::Job.delete_all
+ end
+
+ def start_workers
+ @worker = Delayed::Worker.new(quiet: false, sleep_delay: 0.5, queues: %w(integration_tests))
+ @thread = Thread.new { @worker.start }
+ end
+
+ def stop_workers
+ @worker.stop
+ end
+end
diff --git a/activejob/test/support/integration/adapters/inline.rb b/activejob/test/support/integration/adapters/inline.rb
new file mode 100644
index 0000000000..83c38f706f
--- /dev/null
+++ b/activejob/test/support/integration/adapters/inline.rb
@@ -0,0 +1,15 @@
+module InlineJobsManager
+ def setup
+ ActiveJob::Base.queue_adapter = :inline
+ end
+
+ def clear_jobs
+ end
+
+ def start_workers
+ end
+
+ def stop_workers
+ end
+end
+
diff --git a/activejob/test/support/integration/adapters/qu.rb b/activejob/test/support/integration/adapters/qu.rb
new file mode 100644
index 0000000000..e913f04a24
--- /dev/null
+++ b/activejob/test/support/integration/adapters/qu.rb
@@ -0,0 +1,38 @@
+module QuJobsManager
+ def setup
+ require 'qu-rails'
+ require 'qu-redis'
+ 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
+ unless can_run?
+ puts "Cannot run integration tests for qu. To be able to run integration tests for qu you need to install and start redis.\n"
+ exit
+ end
+ end
+
+ def clear_jobs
+ Qu.clear "integration_tests"
+ end
+
+ def start_workers
+ @thread = Thread.new { Qu::Worker.new("integration_tests").start }
+ end
+
+ def stop_workers
+ @thread.kill
+ end
+
+ def can_run?
+ begin
+ Qu.backend.connection.client.connect
+ rescue => e
+ return false
+ end
+ true
+ end
+end
diff --git a/activejob/test/support/integration/adapters/que.rb b/activejob/test/support/integration/adapters/que.rb
new file mode 100644
index 0000000000..a5b9b3ec0a
--- /dev/null
+++ b/activejob/test/support/integration/adapters/que.rb
@@ -0,0 +1,37 @@
+module QueJobsManager
+ def setup
+ require 'sequel'
+ 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
+ rescue Sequel::DatabaseConnectionError
+ puts "Cannot run integration tests for que. To be able to run integration tests for que you need to install and start postgresql.\n"
+ exit
+ end
+
+ def clear_jobs
+ Que.clear!
+ end
+
+ def start_workers
+ @thread = Thread.new do
+ loop do
+ Que::Job.work("integration_tests")
+ 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
new file mode 100644
index 0000000000..81d1935132
--- /dev/null
+++ b/activejob/test/support/integration/adapters/queue_classic.rb
@@ -0,0 +1,45 @@
+module QueueClassicJobsManager
+ def setup
+ ENV['QC_DATABASE_URL'] ||= 'postgres://localhost/active_jobs_qc_int_test'
+ ENV['QC_LISTEN_TIME'] = "0.5"
+ 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}
+ ActiveJob::Base.queue_adapter = :queue_classic
+ QC::Setup.create
+ rescue PG::ConnectionBad
+ puts "Cannot run integration tests for queue_classic. To be able to run integration tests for queue_classic you need to install and start postgresql.\n"
+ exit
+ end
+
+ def clear_jobs
+ QC::Queue.new("integration_tests").delete_all
+ retried = false
+ rescue => e
+ puts "Got exception while trying to clear jobs: #{e.inspect}"
+ if retried
+ puts "Already retried. Raising exception"
+ raise e
+ else
+ puts "Retrying"
+ retried = true
+ QC::Conn.connection = QC::Conn.connect
+ retry
+ end
+ end
+
+ def start_workers
+ @pid = fork do
+ QC::Conn.connection = QC::Conn.connect
+ worker = QC::Worker.new(q_name: 'integration_tests')
+ 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
new file mode 100644
index 0000000000..03ffd3fd62
--- /dev/null
+++ b/activejob/test/support/integration/adapters/resque.rb
@@ -0,0 +1,47 @@
+module ResqueJobsManager
+ def setup
+ 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
+ unless can_run?
+ puts "Cannot run integration tests for resque. To be able to run integration tests for resque you need to install and start redis.\n"
+ exit
+ end
+ end
+
+ 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
+ @resque_thread = Thread.new do
+ Resque::Worker.new("integration_tests").work(0.5)
+ end
+ @scheduler_thread = Thread.new do
+ Resque::Scheduler.configure do |c|
+ c.poll_sleep_amount = 0.5
+ c.dynamic = true
+ c.verbose = true
+ c.logfile = nil
+ end
+ Resque::Scheduler.master_lock.release!
+ Resque::Scheduler.run
+ end
+ end
+
+ def stop_workers
+ @resque_thread.kill
+ @scheduler_thread.kill
+ end
+
+ def can_run?
+ begin
+ Resque.redis.client.connect
+ rescue => e
+ return false
+ end
+ true
+ end
+end
diff --git a/activejob/test/support/integration/adapters/sidekiq.rb b/activejob/test/support/integration/adapters/sidekiq.rb
new file mode 100644
index 0000000000..b3c3dcff22
--- /dev/null
+++ b/activejob/test/support/integration/adapters/sidekiq.rb
@@ -0,0 +1,58 @@
+require 'sidekiq/cli'
+require 'sidekiq/api'
+
+module SidekiqJobsManager
+
+ def setup
+ ActiveJob::Base.queue_adapter = :sidekiq
+ unless can_run?
+ puts "Cannot run integration tests for sidekiq. To be able to run integration tests for sidekiq you need to install and start redis.\n"
+ exit
+ end
+ end
+
+ def clear_jobs
+ Sidekiq::ScheduledSet.new.clear
+ Sidekiq::Queue.new("integration_tests").clear
+ end
+
+ def start_workers
+ fork do
+ sidekiq = Sidekiq::CLI.instance
+ logfile = Rails.root.join("log/sidekiq.log").to_s
+ pidfile = Rails.root.join("tmp/sidekiq.pid").to_s
+ sidekiq.parse([ "--require", Rails.root.to_s,
+ "--queue", "integration_tests",
+ "--logfile", logfile,
+ "--pidfile", pidfile,
+ "--environment", "test",
+ "--concurrency", "1",
+ "--timeout", "1",
+ "--daemon",
+ "--verbose"
+ ])
+ require 'celluloid'
+ require 'sidekiq/scheduled'
+ Sidekiq.poll_interval = 0.5
+ Sidekiq::Scheduled.const_set :INITIAL_WAIT, 1
+ sidekiq.run
+ end
+ sleep 1
+ end
+
+ def stop_workers
+ pidfile = Rails.root.join("tmp/sidekiq.pid").to_s
+ Process.kill 'TERM', File.open(pidfile).read.to_i
+ FileUtils.rm_f pidfile
+ rescue
+ end
+
+ def can_run?
+ begin
+ Sidekiq.redis { |conn| conn.connect }
+ rescue => e
+ return false
+ end
+ true
+ end
+end
diff --git a/activejob/test/support/integration/adapters/sneakers.rb b/activejob/test/support/integration/adapters/sneakers.rb
new file mode 100644
index 0000000000..f21bb38a32
--- /dev/null
+++ b/activejob/test/support/integration/adapters/sneakers.rb
@@ -0,0 +1,90 @@
+require 'sneakers/runner'
+require 'sneakers/publisher'
+require 'timeout'
+
+module Sneakers
+ class Publisher
+ def safe_ensure_connected
+ @mutex.synchronize do
+ ensure_connection! unless connected?
+ end
+ end
+ end
+end
+
+
+module SneakersJobsManager
+ def setup
+ 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
+ unless can_run?
+ puts "Cannot run integration tests for sneakers. To be able to run integration tests for sneakers you need to install and start rabbitmq.\n"
+ exit
+ end
+ end
+
+ def clear_jobs
+ bunny_queue.purge
+ end
+
+ def start_workers
+ @pid = fork do
+ queues = %w(integration_tests)
+ workers = queues.map do |q|
+ worker_klass = "ActiveJobWorker"+Digest::MD5.hexdigest(q)
+ Sneakers.const_set(worker_klass, Class.new(ActiveJob::QueueAdapters::SneakersAdapter::JobWrapper) do
+ from_queue q
+ end)
+ end
+ Sneakers::Runner.new(workers).run
+ end
+ begin
+ Timeout.timeout(10) do
+ while bunny_queue.status[:consumer_count] == 0
+ sleep 0.5
+ end
+ end
+ rescue Timeout::Error
+ stop_workers
+ raise "Failed to start sneakers worker"
+ end
+ end
+
+ def stop_workers
+ Process.kill 'TERM', @pid
+ Process.kill 'TERM', File.open(Rails.root.join("tmp/sneakers.pid").to_s).read.to_i
+ rescue
+ end
+
+ def can_run?
+ begin
+ bunny_publisher
+ rescue => e
+ return false
+ end
+ true
+ end
+
+ protected
+ def bunny_publisher
+ @bunny_publisher ||= begin
+ p = ActiveJob::QueueAdapters::SneakersAdapter::JobWrapper.send(:publisher)
+ p.safe_ensure_connected
+ p
+ end
+ end
+
+ def bunny_queue
+ @queue ||= bunny_publisher.exchange.channel.queue "integration_tests", durable: true
+ end
+
+end
diff --git a/activejob/test/support/integration/adapters/sucker_punch.rb b/activejob/test/support/integration/adapters/sucker_punch.rb
new file mode 100644
index 0000000000..691ba35c90
--- /dev/null
+++ b/activejob/test/support/integration/adapters/sucker_punch.rb
@@ -0,0 +1,5 @@
+module SuckerPunchJobsManager
+ def setup
+ ActiveJob::Base.queue_adapter = :sucker_punch
+ end
+end
diff --git a/activejob/test/support/integration/dummy_app_template.rb b/activejob/test/support/integration/dummy_app_template.rb
new file mode 100644
index 0000000000..28aae0f884
--- /dev/null
+++ b/activejob/test/support/integration/dummy_app_template.rb
@@ -0,0 +1,21 @@
+if ENV['AJADAPTER'] == 'delayed_job'
+ generate "delayed_job:active_record"
+ rake("db:migrate")
+end
+
+initializer 'activejob.rb', <<-CODE
+require "#{File.expand_path("../jobs_manager.rb", __FILE__)}"
+JobsManager.current_manager.setup
+CODE
+
+file 'app/jobs/test_job.rb', <<-CODE
+class TestJob < ActiveJob::Base
+ queue_as :integration_tests
+
+ def perform(x)
+ File.open(Rails.root.join("tmp/\#{x}"), "w+") do |f|
+ f.write x
+ end
+ end
+end
+CODE
diff --git a/activejob/test/support/integration/helper.rb b/activejob/test/support/integration/helper.rb
new file mode 100644
index 0000000000..ccd5036b36
--- /dev/null
+++ b/activejob/test/support/integration/helper.rb
@@ -0,0 +1,32 @@
+puts "\n\n"
+puts "*** Running integration tests for #{ENV['AJADAPTER']} ***"
+puts "\n\n"
+
+ENV["RAILS_ENV"] = "test"
+ActiveJob::Base.queue_name_prefix = nil
+
+require 'rails/generators/rails/app/app_generator'
+
+dummy_app_path = Dir.mktmpdir + "/dummy"
+dummy_app_template = File.expand_path("../dummy_app_template.rb", __FILE__)
+args = Rails::Generators::ARGVScrubber.new(["new", dummy_app_path, "--skip-gemfile", "--skip-bundle",
+ "--skip-git", "--skip-spring", "-d", "sqlite3", "--skip-javascript", "--force", "--quite",
+ "--template", dummy_app_template]).prepare!
+Rails::Generators::AppGenerator.start args
+
+require "#{dummy_app_path}/config/environment.rb"
+
+ActiveRecord::Migrator.migrations_paths = [ Rails.root.join('db/migrate').to_s ]
+require 'rails/test_help'
+
+Rails.backtrace_cleaner.remove_silencers!
+
+require_relative 'test_case_helpers'
+ActiveSupport::TestCase.send(:include, TestCaseHelpers)
+
+JobsManager.current_manager.start_workers
+
+Minitest.after_run do
+ JobsManager.current_manager.stop_workers
+ JobsManager.current_manager.clear_jobs
+end
diff --git a/activejob/test/support/integration/jobs_manager.rb b/activejob/test/support/integration/jobs_manager.rb
new file mode 100644
index 0000000000..4df34aaeb1
--- /dev/null
+++ b/activejob/test/support/integration/jobs_manager.rb
@@ -0,0 +1,27 @@
+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
+ ActiveJob::Base.queue_adapter = nil
+ end
+
+ def clear_jobs
+ 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
new file mode 100644
index 0000000000..ee2f6aebea
--- /dev/null
+++ b/activejob/test/support/integration/test_case_helpers.rb
@@ -0,0 +1,48 @@
+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
+ @id = "AJ-#{SecureRandom.uuid}"
+ end
+
+ teardown do
+ clear_jobs
+ end
+ end
+
+ protected
+
+ def jobs_manager
+ JobsManager.current_manager
+ end
+
+ def clear_jobs
+ jobs_manager.clear_jobs
+ end
+
+ def adapter_is?(adapter)
+ ActiveJob::Base.queue_adapter.name.split("::").last.gsub(/Adapter$/, '').underscore==adapter.to_s
+ end
+
+ def wait_for_jobs_to_finish_for(seconds=60)
+ begin
+ Timeout.timeout(seconds) do
+ while !job_executed do
+ sleep 0.25
+ end
+ end
+ rescue Timeout::Error
+ end
+ end
+
+ def job_executed
+ Dummy::Application.root.join("tmp/#{@id}").exist?
+ end
+end
diff --git a/activejob/test/support/job_buffer.rb b/activejob/test/support/job_buffer.rb
new file mode 100644
index 0000000000..620cb5288d
--- /dev/null
+++ b/activejob/test/support/job_buffer.rb
@@ -0,0 +1,19 @@
+module JobBuffer
+ class << self
+ def clear
+ values.clear
+ end
+
+ def add(value)
+ values << value
+ end
+
+ def values
+ @values ||= []
+ end
+
+ def last_value
+ values.last
+ end
+ end
+end
diff --git a/activejob/test/support/queue_classic/inline.rb b/activejob/test/support/queue_classic/inline.rb
index 5e9c295e01..5743d5bbb5 100644
--- a/activejob/test/support/queue_classic/inline.rb
+++ b/activejob/test/support/queue_classic/inline.rb
@@ -7,5 +7,17 @@ module QC
receiver = eval(receiver_str)
receiver.send(message, *args)
end
+
+ def enqueue_in(seconds, method, *args)
+ receiver_str, _, message = method.rpartition('.')
+ receiver = eval(receiver_str)
+ receiver.send(message, *args)
+ end
+
+ def enqueue_at(not_before, method, *args)
+ receiver_str, _, message = method.rpartition('.')
+ receiver = eval(receiver_str)
+ receiver.send(message, *args)
+ end
end
end