aboutsummaryrefslogtreecommitdiffstats
path: root/activejob/lib/active_job
diff options
context:
space:
mode:
Diffstat (limited to 'activejob/lib/active_job')
-rw-r--r--activejob/lib/active_job/arguments.rb59
-rw-r--r--activejob/lib/active_job/base.rb3
-rw-r--r--activejob/lib/active_job/callbacks.rb5
-rw-r--r--activejob/lib/active_job/core.rb51
-rw-r--r--activejob/lib/active_job/enqueuing.rb31
-rw-r--r--activejob/lib/active_job/exceptions.rb45
-rw-r--r--activejob/lib/active_job/execution.rb8
-rw-r--r--activejob/lib/active_job/logging.rb42
-rw-r--r--activejob/lib/active_job/queue_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters.rb10
-rw-r--r--activejob/lib/active_job/queue_adapters/async_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters/backburner_adapter.rb4
-rw-r--r--activejob/lib/active_job/queue_adapters/inline_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters/test_adapter.rb30
-rw-r--r--activejob/lib/active_job/queue_name.rb2
-rw-r--r--activejob/lib/active_job/railtie.rb4
-rw-r--r--activejob/lib/active_job/serializers.rb1
-rw-r--r--activejob/lib/active_job/test_helper.rb335
18 files changed, 477 insertions, 159 deletions
diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb
index 86bb0c5540..92eb58aaaf 100644
--- a/activejob/lib/active_job/arguments.rb
+++ b/activejob/lib/active_job/arguments.rb
@@ -14,28 +14,30 @@ module ActiveJob
end
# Raised when an unsupported argument type is set as a job argument. We
- # currently support NilClass, Integer, Float, String, TrueClass, FalseClass,
- # BigDecimal, and objects that can be represented as GlobalIDs (ex: Active Record).
+ # currently support String, Integer, Float, NilClass, TrueClass, FalseClass,
+ # BigDecimal, Symbol, Date, Time, DateTime, ActiveSupport::TimeWithZone,
+ # ActiveSupport::Duration, Hash, ActiveSupport::HashWithIndifferentAccess,
+ # Array or GlobalID::Identification instances, although this can be extended
+ # by adding custom serializers.
# Raised if you set the key for a Hash something else than a string or
# a symbol. Also raised when trying to serialize an object which can't be
- # identified with a Global ID - such as an unpersisted Active Record model.
+ # identified with a GlobalID - such as an unpersisted Active Record model.
class SerializationError < ArgumentError; end
module Arguments
extend self
- # :nodoc:
- TYPE_WHITELIST = [ NilClass, String, Integer, Float, BigDecimal, TrueClass, FalseClass ]
-
- # Serializes a set of arguments. Whitelisted types are returned
- # as-is. Arrays/Hashes are serialized element by element.
- # All other types are serialized using GlobalID.
+ # Serializes a set of arguments. Intrinsic types that can safely be
+ # serialized without mutation are returned as-is. Arrays/Hashes are
+ # serialized element by element. All other types are serialized using
+ # GlobalID.
def serialize(arguments)
arguments.map { |argument| serialize_argument(argument) }
end
- # Deserializes a set of arguments. Whitelisted types are returned
- # as-is. Arrays/Hashes are deserialized element by element.
- # All other types are deserialized using GlobalID.
+ # Deserializes a set of arguments. Intrinsic types that can safely be
+ # deserialized without mutation are returned as-is. Arrays/Hashes are
+ # deserialized element by element. All other types are deserialized using
+ # GlobalID.
def deserialize(arguments)
arguments.map { |argument| deserialize_argument(argument) }
rescue
@@ -45,11 +47,13 @@ module ActiveJob
private
# :nodoc:
- GLOBALID_KEY = "_aj_globalid".freeze
+ PERMITTED_TYPES = [ NilClass, String, Integer, Float, BigDecimal, TrueClass, FalseClass ]
+ # :nodoc:
+ GLOBALID_KEY = "_aj_globalid"
# :nodoc:
- SYMBOL_KEYS_KEY = "_aj_symbol_keys".freeze
+ SYMBOL_KEYS_KEY = "_aj_symbol_keys"
# :nodoc:
- WITH_INDIFFERENT_ACCESS_KEY = "_aj_hash_with_indifferent_access".freeze
+ WITH_INDIFFERENT_ACCESS_KEY = "_aj_hash_with_indifferent_access"
# :nodoc:
OBJECT_SERIALIZER_KEY = "_aj_serialized"
@@ -60,25 +64,25 @@ module ActiveJob
OBJECT_SERIALIZER_KEY, OBJECT_SERIALIZER_KEY.to_sym,
WITH_INDIFFERENT_ACCESS_KEY, WITH_INDIFFERENT_ACCESS_KEY.to_sym,
]
- private_constant :RESERVED_KEYS
+ private_constant :PERMITTED_TYPES, :RESERVED_KEYS, :GLOBALID_KEY, :SYMBOL_KEYS_KEY, :WITH_INDIFFERENT_ACCESS_KEY
def serialize_argument(argument)
case argument
- when *TYPE_WHITELIST
+ when *PERMITTED_TYPES
argument
when GlobalID::Identification
convert_to_global_id_hash(argument)
when Array
argument.map { |arg| serialize_argument(arg) }
when ActiveSupport::HashWithIndifferentAccess
- result = serialize_hash(argument)
- result[WITH_INDIFFERENT_ACCESS_KEY] = serialize_argument(true)
- result
+ serialize_indifferent_hash(argument)
when Hash
symbol_keys = argument.each_key.grep(Symbol).map(&:to_s)
result = serialize_hash(argument)
result[SYMBOL_KEYS_KEY] = symbol_keys
result
+ when -> (arg) { arg.respond_to?(:permitted?) }
+ serialize_indifferent_hash(argument.to_h)
else
Serializers.serialize(argument)
end
@@ -87,8 +91,8 @@ module ActiveJob
def deserialize_argument(argument)
case argument
when String
- GlobalID::Locator.locate(argument) || argument
- when *TYPE_WHITELIST
+ argument
+ when *PERMITTED_TYPES
argument
when Array
argument.map { |arg| deserialize_argument(arg) }
@@ -144,8 +148,17 @@ module ActiveJob
end
end
+ def serialize_indifferent_hash(indifferent_hash)
+ result = serialize_hash(indifferent_hash)
+ result[WITH_INDIFFERENT_ACCESS_KEY] = serialize_argument(true)
+ result
+ end
+
def transform_symbol_keys(hash, symbol_keys)
- hash.transform_keys do |key|
+ # NOTE: HashWithIndifferentAccess#transform_keys always
+ # returns stringified keys with indifferent access
+ # so we call #to_h here to ensure keys are symbolized.
+ hash.to_h.transform_keys do |key|
if symbol_keys.include?(key)
key.to_sym
else
diff --git a/activejob/lib/active_job/base.rb b/activejob/lib/active_job/base.rb
index 2b2a59e969..ed41fac4b8 100644
--- a/activejob/lib/active_job/base.rb
+++ b/activejob/lib/active_job/base.rb
@@ -40,7 +40,7 @@ module ActiveJob #:nodoc:
# Records that are passed in are serialized/deserialized using Global
# ID. More information can be found in Arguments.
#
- # To enqueue a job to be performed as soon as the queueing system is free:
+ # To enqueue a job to be performed as soon as the queuing system is free:
#
# ProcessPhotoJob.perform_later(photo)
#
@@ -60,7 +60,6 @@ module ActiveJob #:nodoc:
# * SerializationError - Error class for serialization errors.
class Base
include Core
- include Serializers
include QueueAdapter
include QueueName
include QueuePriority
diff --git a/activejob/lib/active_job/callbacks.rb b/activejob/lib/active_job/callbacks.rb
index 334b24fb3b..6b82ea6cab 100644
--- a/activejob/lib/active_job/callbacks.rb
+++ b/activejob/lib/active_job/callbacks.rb
@@ -29,6 +29,9 @@ module ActiveJob
included do
define_callbacks :perform
define_callbacks :enqueue
+
+ class_attribute :return_false_on_aborted_enqueue, instance_accessor: false, instance_predicate: false
+ self.return_false_on_aborted_enqueue = false
end
# These methods will be included into any Active Job object, adding
@@ -130,7 +133,7 @@ module ActiveJob
set_callback(:enqueue, :after, *filters, &blk)
end
- # Defines a callback that will get called around the enqueueing
+ # Defines a callback that will get called around the enqueuing
# of the job.
#
# class VideoProcessJob < ActiveJob::Base
diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb
index 61d402cfca..4ab62f89b0 100644
--- a/activejob/lib/active_job/core.rb
+++ b/activejob/lib/active_job/core.rb
@@ -6,35 +6,39 @@ module ActiveJob
module Core
extend ActiveSupport::Concern
- included do
- # Job arguments
- attr_accessor :arguments
- attr_writer :serialized_arguments
+ # Job arguments
+ attr_accessor :arguments
+ attr_writer :serialized_arguments
- # Timestamp when the job should be performed
- attr_accessor :scheduled_at
+ # Timestamp when the job should be performed
+ attr_accessor :scheduled_at
- # Job Identifier
- attr_accessor :job_id
+ # Job Identifier
+ attr_accessor :job_id
- # Queue in which the job will reside.
- attr_writer :queue_name
+ # Queue in which the job will reside.
+ attr_writer :queue_name
- # Priority that the job will have (lower is more priority).
- attr_writer :priority
+ # Priority that the job will have (lower is more priority).
+ attr_writer :priority
- # ID optionally provided by adapter
- attr_accessor :provider_job_id
+ # ID optionally provided by adapter
+ attr_accessor :provider_job_id
- # Number of times this job has been executed (which increments on every retry, like after an exception).
- attr_accessor :executions
+ # Number of times this job has been executed (which increments on every retry, like after an exception).
+ attr_accessor :executions
- # I18n.locale to be used during the job.
- attr_accessor :locale
+ # Hash that contains the number of times this job handled errors for each specific retry_on declaration.
+ # Keys are the string representation of the exceptions listed in the retry_on declaration,
+ # while its associated value holds the number of executions where the corresponding retry_on
+ # declaration handled one of its listed exceptions.
+ attr_accessor :exception_executions
- # Timezone to be used during the job.
- attr_accessor :timezone
- end
+ # I18n.locale to be used during the job.
+ attr_accessor :locale
+
+ # Timezone to be used during the job.
+ attr_accessor :timezone
# These methods will be included into any Active Job object, adding
# helpers for de/serialization and creation of job instances.
@@ -77,10 +81,11 @@ module ActiveJob
@queue_name = self.class.queue_name
@priority = self.class.priority
@executions = 0
+ @exception_executions = Hash.new(0)
end
# Returns a hash with the job data that can safely be passed to the
- # queueing adapter.
+ # queuing adapter.
def serialize
{
"job_class" => self.class.name,
@@ -90,6 +95,7 @@ module ActiveJob
"priority" => priority,
"arguments" => serialize_arguments_if_needed(arguments),
"executions" => executions,
+ "exception_executions" => exception_executions,
"locale" => I18n.locale.to_s,
"timezone" => Time.zone.try(:name)
}
@@ -128,6 +134,7 @@ module ActiveJob
self.priority = job_data["priority"]
self.serialized_arguments = job_data["arguments"]
self.executions = job_data["executions"]
+ self.exception_executions = job_data["exception_executions"]
self.locale = job_data["locale"] || I18n.locale.to_s
self.timezone = job_data["timezone"] || Time.zone.try(:name)
end
diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb
index 53cb98fc71..ce118c1e8a 100644
--- a/activejob/lib/active_job/enqueuing.rb
+++ b/activejob/lib/active_job/enqueuing.rb
@@ -9,10 +9,12 @@ module ActiveJob
# Includes the +perform_later+ method for job initialization.
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
- # GlobalID::Identification instances. Arbitrary Ruby objects
- # are not supported.
+ # Push a job onto the queue. By default the arguments must be either String,
+ # Integer, Float, NilClass, TrueClass, FalseClass, BigDecimal, Symbol, Date,
+ # Time, DateTime, ActiveSupport::TimeWithZone, ActiveSupport::Duration,
+ # Hash, ActiveSupport::HashWithIndifferentAccess, Array or
+ # GlobalID::Identification instances, although this can be extended by adding
+ # custom serializers.
#
# Returns an instance of the job class queued with arguments available in
# Job#arguments.
@@ -46,14 +48,33 @@ module ActiveJob
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]
self.priority = options[:priority].to_i if options[:priority]
+ successfully_enqueued = false
+
run_callbacks :enqueue do
if scheduled_at
self.class.queue_adapter.enqueue_at self, scheduled_at
else
self.class.queue_adapter.enqueue self
end
+
+ successfully_enqueued = true
+ end
+
+ if successfully_enqueued
+ self
+ else
+ if self.class.return_false_on_aborted_enqueue
+ false
+ else
+ ActiveSupport::Deprecation.warn(
+ "Rails 6.0 will return false when the enqueing is aborted. Make sure your code doesn't depend on it" \
+ " returning the instance of the job and set `config.active_job.return_false_on_aborted_enqueue = true`" \
+ " to remove the deprecations."
+ )
+
+ self
+ end
end
- self
end
end
end
diff --git a/activejob/lib/active_job/exceptions.rb b/activejob/lib/active_job/exceptions.rb
index 31bbb18d7f..53984a4e49 100644
--- a/activejob/lib/active_job/exceptions.rb
+++ b/activejob/lib/active_job/exceptions.rb
@@ -30,8 +30,8 @@ module ActiveJob
# class RemoteServiceJob < ActiveJob::Base
# retry_on CustomAppException # defaults to 3s wait, 5 attempts
# retry_on AnotherCustomAppException, wait: ->(executions) { executions * 2 }
- # retry_on(YetAnotherCustomAppException) do |job, exception|
- # ExceptionNotifier.caught(exception)
+ # retry_on(YetAnotherCustomAppException) do |job, error|
+ # ExceptionNotifier.caught(error)
# end
# retry_on ActiveRecord::Deadlocked, wait: 5.seconds, attempts: 3
# retry_on Net::OpenTimeout, wait: :exponentially_longer, attempts: 10
@@ -42,16 +42,19 @@ module ActiveJob
# # Might raise Net::OpenTimeout when the remote service is down
# end
# end
- def retry_on(exception, wait: 3.seconds, attempts: 5, queue: nil, priority: nil)
- rescue_from exception do |error|
- if executions < attempts
- logger.error "Retrying #{self.class} in #{wait} seconds, due to a #{exception}. The original exception was #{error.cause.inspect}."
- retry_job wait: determine_delay(wait), queue: queue, priority: priority
+ def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil)
+ rescue_from(*exceptions) do |error|
+ exception_executions[exceptions.to_s] += 1
+
+ if exception_executions[exceptions.to_s] < attempts
+ retry_job wait: determine_delay(wait), queue: queue, priority: priority, error: error
else
if block_given?
- yield self, error
+ instrument :retry_stopped, error: error do
+ yield self, error
+ end
else
- logger.error "Stopped retrying #{self.class} due to a #{exception}, which reoccurred on #{executions} attempts. The original exception was #{error.cause.inspect}."
+ instrument :retry_stopped, error: error
raise error
end
end
@@ -67,8 +70,8 @@ module ActiveJob
#
# class SearchIndexingJob < ActiveJob::Base
# discard_on ActiveJob::DeserializationError
- # discard_on(CustomAppException) do |job, exception|
- # ExceptionNotifier.caught(exception)
+ # discard_on(CustomAppException) do |job, error|
+ # ExceptionNotifier.caught(error)
# end
#
# def perform(record)
@@ -76,12 +79,10 @@ module ActiveJob
# # Might raise CustomAppException for something domain specific
# end
# end
- def discard_on(exception)
- rescue_from exception do |error|
- if block_given?
- yield self, error
- else
- logger.error "Discarded #{self.class} due to a #{exception}. The original exception was #{error.cause.inspect}."
+ def discard_on(*exceptions)
+ rescue_from(*exceptions) do |error|
+ instrument :discard, error: error do
+ yield self, error if block_given?
end
end
end
@@ -109,7 +110,9 @@ module ActiveJob
# end
# end
def retry_job(options = {})
- enqueue options
+ instrument :enqueue_retry, options.slice(:error, :wait) do
+ enqueue options
+ end
end
private
@@ -130,5 +133,11 @@ module ActiveJob
raise "Couldn't determine a delay based on #{seconds_or_duration_or_algorithm.inspect}"
end
end
+
+ def instrument(name, error: nil, wait: nil, &block)
+ payload = { job: self, adapter: self.class.queue_adapter, error: error, wait: wait }
+
+ ActiveSupport::Notifications.instrument("#{name}.active_job", payload, &block)
+ end
end
end
diff --git a/activejob/lib/active_job/execution.rb b/activejob/lib/active_job/execution.rb
index d75be376ec..e96dbcd4c9 100644
--- a/activejob/lib/active_job/execution.rb
+++ b/activejob/lib/active_job/execution.rb
@@ -26,16 +26,16 @@ module ActiveJob
end
end
- # Performs the job immediately. The job is not sent to the queueing adapter
+ # Performs the job immediately. The job is not sent to the queuing adapter
# but directly executed by blocking the execution of others until it's finished.
#
# MyJob.new(*args).perform_now
def perform_now
+ # Guard against jobs that were persisted before we started counting executions by zeroing out nil counters
+ self.executions = (executions || 0) + 1
+
deserialize_arguments_if_needed
run_callbacks :perform do
- # Guard against jobs that were persisted before we started counting executions by zeroing out nil counters
- self.executions = (executions || 0) + 1
-
perform(*arguments)
end
rescue => exception
diff --git a/activejob/lib/active_job/logging.rb b/activejob/lib/active_job/logging.rb
index 3312857ac7..416be83c24 100644
--- a/activejob/lib/active_job/logging.rb
+++ b/activejob/lib/active_job/logging.rb
@@ -27,13 +27,13 @@ module ActiveJob
end
end
- after_enqueue do |job|
+ around_enqueue do |job, block|
if job.scheduled_at
- ActiveSupport::Notifications.instrument "enqueue_at.active_job",
- adapter: job.class.queue_adapter, job: job
+ ActiveSupport::Notifications.instrument("enqueue_at.active_job",
+ adapter: job.class.queue_adapter, job: job, &block)
else
- ActiveSupport::Notifications.instrument "enqueue.active_job",
- adapter: job.class.queue_adapter, job: job
+ ActiveSupport::Notifications.instrument("enqueue.active_job",
+ adapter: job.class.queue_adapter, job: job, &block)
end
end
end
@@ -88,6 +88,38 @@ module ActiveJob
end
end
+ def enqueue_retry(event)
+ job = event.payload[:job]
+ ex = event.payload[:error]
+ wait = event.payload[:wait]
+
+ info do
+ if ex
+ "Retrying #{job.class} in #{wait.to_i} seconds, due to a #{ex.class}."
+ else
+ "Retrying #{job.class} in #{wait.to_i} seconds."
+ end
+ end
+ end
+
+ def retry_stopped(event)
+ job = event.payload[:job]
+ ex = event.payload[:error]
+
+ error do
+ "Stopped retrying #{job.class} due to a #{ex.class}, which reoccurred on #{job.executions} attempts."
+ end
+ end
+
+ def discard(event)
+ job = event.payload[:job]
+ ex = event.payload[:error]
+
+ error do
+ "Discarded #{job.class} due to a #{ex.class}."
+ end
+ end
+
private
def queue_name(event)
event.payload[:adapter].class.name.demodulize.remove("Adapter") + "(#{event.payload[:job].queue_name})"
diff --git a/activejob/lib/active_job/queue_adapter.rb b/activejob/lib/active_job/queue_adapter.rb
index 006a683b85..954bfd1dd1 100644
--- a/activejob/lib/active_job/queue_adapter.rb
+++ b/activejob/lib/active_job/queue_adapter.rb
@@ -22,6 +22,8 @@ module ActiveJob
_queue_adapter
end
+ # Returns string denoting the name of the configured queue adapter.
+ # By default returns +"async"+.
def queue_adapter_name
_queue_adapter_name
end
diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb
index 7854467cc1..525e79e302 100644
--- a/activejob/lib/active_job/queue_adapters.rb
+++ b/activejob/lib/active_job/queue_adapters.rb
@@ -3,7 +3,7 @@
module ActiveJob
# == Active Job adapters
#
- # Active Job has adapters for the following queueing backends:
+ # Active Job has adapters for the following queuing backends:
#
# * {Backburner}[https://github.com/nesquena/backburner]
# * {Delayed Job}[https://github.com/collectiveidea/delayed_job]
@@ -15,7 +15,7 @@ module ActiveJob
# * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch]
# * {Active Job Async Job}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html]
# * {Active Job Inline}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html]
- # * Please Note: We are not accepting pull requests for new adapters. See the README for more details.
+ # * Please Note: We are not accepting pull requests for new adapters. See the {README}[link:files/activejob/README_md.html] for more details.
#
# === Backends Features
#
@@ -52,7 +52,7 @@ module ActiveJob
#
# No: The adapter will run jobs at the next opportunity and cannot use perform_later.
#
- # N/A: The adapter does not support queueing.
+ # N/A: The adapter does not support queuing.
#
# NOTE:
# queue_classic supports job scheduling since version 3.1.
@@ -74,7 +74,7 @@ module ActiveJob
#
# No: Does not allow the priority of jobs to be configured.
#
- # N/A: The adapter does not support queueing, and therefore sorting them.
+ # N/A: The adapter does not support queuing, and therefore sorting them.
#
# ==== Timeout
#
@@ -121,7 +121,7 @@ module ActiveJob
autoload :SuckerPunchAdapter
autoload :TestAdapter
- ADAPTER = "Adapter".freeze
+ ADAPTER = "Adapter"
private_constant :ADAPTER
class << self
diff --git a/activejob/lib/active_job/queue_adapters/async_adapter.rb b/activejob/lib/active_job/queue_adapters/async_adapter.rb
index ebf6f384e3..53a7e3d53e 100644
--- a/activejob/lib/active_job/queue_adapters/async_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/async_adapter.rb
@@ -31,7 +31,7 @@ module ActiveJob
# jobs. Since jobs share a single thread pool, long-running jobs will block
# short-lived jobs. Fine for dev/test; bad for production.
class AsyncAdapter
- # See {Concurrent::ThreadPoolExecutor}[https://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ThreadPoolExecutor.html] for executor options.
+ # See {Concurrent::ThreadPoolExecutor}[https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ThreadPoolExecutor.html] for executor options.
def initialize(**executor_options)
@scheduler = Scheduler.new(**executor_options)
end
diff --git a/activejob/lib/active_job/queue_adapters/backburner_adapter.rb b/activejob/lib/active_job/queue_adapters/backburner_adapter.rb
index 0ba93c6e0b..7dc49310ac 100644
--- a/activejob/lib/active_job/queue_adapters/backburner_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/backburner_adapter.rb
@@ -16,12 +16,12 @@ module ActiveJob
# Rails.application.config.active_job.queue_adapter = :backburner
class BackburnerAdapter
def enqueue(job) #:nodoc:
- Backburner::Worker.enqueue JobWrapper, [ job.serialize ], queue: job.queue_name
+ Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority)
end
def enqueue_at(job, timestamp) #:nodoc:
delay = timestamp - Time.current.to_f
- Backburner::Worker.enqueue JobWrapper, [ job.serialize ], queue: job.queue_name, delay: delay
+ Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority, delay: delay)
end
class JobWrapper #:nodoc:
diff --git a/activejob/lib/active_job/queue_adapters/inline_adapter.rb b/activejob/lib/active_job/queue_adapters/inline_adapter.rb
index 3d0b590212..ca04dc943c 100644
--- a/activejob/lib/active_job/queue_adapters/inline_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/inline_adapter.rb
@@ -16,7 +16,7 @@ module ActiveJob
end
def enqueue_at(*) #:nodoc:
- raise NotImplementedError, "Use a queueing backend to enqueue jobs in the future. Read more at http://guides.rubyonrails.org/active_job_basics.html"
+ raise NotImplementedError, "Use a queueing backend to enqueue jobs in the future. Read more at https://guides.rubyonrails.org/active_job_basics.html"
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
index 885f9ff01c..c134257ebc 100644
--- a/activejob/lib/active_job/queue_adapters/test_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/test_adapter.rb
@@ -12,7 +12,7 @@ module ActiveJob
#
# Rails.application.config.active_job.queue_adapter = :test
class TestAdapter
- attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject)
+ attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue)
attr_writer(:enqueued_jobs, :performed_jobs)
# Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
@@ -29,14 +29,14 @@ module ActiveJob
return if filtered?(job)
job_data = job_to_hash(job)
- enqueue_or_perform(perform_enqueued_jobs, job, job_data)
+ perform_or_enqueue(perform_enqueued_jobs, job, job_data)
end
def enqueue_at(job, timestamp) #:nodoc:
return if filtered?(job)
job_data = job_to_hash(job, at: timestamp)
- enqueue_or_perform(perform_enqueued_at_jobs, job, job_data)
+ perform_or_enqueue(perform_enqueued_at_jobs, job, job_data)
end
private
@@ -44,7 +44,7 @@ module ActiveJob
{ job: job.class, args: job.serialize.fetch("arguments"), queue: job.queue_name }.merge!(extras)
end
- def enqueue_or_perform(perform, job, job_data)
+ def perform_or_enqueue(perform, job, job_data)
if perform
performed_jobs << job_data
Base.execute job.serialize
@@ -54,14 +54,28 @@ module ActiveJob
end
def filtered?(job)
+ filtered_queue?(job) || filtered_job_class?(job)
+ end
+
+ def filtered_queue?(job)
+ if queue
+ job.queue_name != queue.to_s
+ end
+ end
+
+ def filtered_job_class?(job)
if filter
- !Array(filter).include?(job.class)
+ !filter_as_proc(filter).call(job)
elsif reject
- Array(reject).include?(job.class)
- else
- false
+ filter_as_proc(reject).call(job)
end
end
+
+ def filter_as_proc(filter)
+ return filter if filter.is_a?(Proc)
+
+ ->(job) { Array(filter).include?(job.class) }
+ end
end
end
end
diff --git a/activejob/lib/active_job/queue_name.rb b/activejob/lib/active_job/queue_name.rb
index 9dc6bc7f2e..7bb1e35181 100644
--- a/activejob/lib/active_job/queue_name.rb
+++ b/activejob/lib/active_job/queue_name.rb
@@ -34,7 +34,7 @@ module ActiveJob
end
included do
- class_attribute :queue_name, instance_accessor: false, default: default_queue_name
+ class_attribute :queue_name, instance_accessor: false, default: -> { self.class.default_queue_name }
class_attribute :queue_name_delimiter, instance_accessor: false, default: "_"
end
diff --git a/activejob/lib/active_job/railtie.rb b/activejob/lib/active_job/railtie.rb
index d0294854d3..ecc0908d5f 100644
--- a/activejob/lib/active_job/railtie.rb
+++ b/activejob/lib/active_job/railtie.rb
@@ -30,6 +30,10 @@ module ActiveJob
send(k, v) if respond_to? k
end
end
+
+ ActiveSupport.on_load(:action_dispatch_integration_test) do
+ include ActiveJob::TestHelper
+ end
end
initializer "active_job.set_reloader_hook" do |app|
diff --git a/activejob/lib/active_job/serializers.rb b/activejob/lib/active_job/serializers.rb
index df66e66659..a5d90f48b8 100644
--- a/activejob/lib/active_job/serializers.rb
+++ b/activejob/lib/active_job/serializers.rb
@@ -7,7 +7,6 @@ module ActiveJob
# and to add new ones. It also has helpers to serialize/deserialize objects.
module Serializers # :nodoc:
extend ActiveSupport::Autoload
- extend ActiveSupport::Concern
autoload :ObjectSerializer
autoload :SymbolSerializer
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index 1cd2c40c15..e0a71f4da8 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -52,7 +52,7 @@ module ActiveJob
queue_adapter_changed_jobs.each { |klass| klass.disable_test_adapter }
end
- # Specifies the queue adapter to use with all active job test helpers.
+ # Specifies the queue adapter to use with all Active Job test helpers.
#
# Returns an instance of the queue adapter and defaults to
# <tt>ActiveJob::QueueAdapters::TestAdapter</tt>.
@@ -75,7 +75,7 @@ module ActiveJob
# assert_enqueued_jobs 2
# end
#
- # If a block is passed, that block will cause the specified number of
+ # If a block is passed, asserts that the block will cause the specified number of
# jobs to be enqueued.
#
# def test_jobs_again
@@ -89,7 +89,7 @@ module ActiveJob
# end
# end
#
- # The number of times a specific job was enqueued can be asserted.
+ # Asserts the number of times a specific job was enqueued by passing +:only+ option.
#
# def test_logging_job
# assert_enqueued_jobs 1, only: LoggingJob do
@@ -98,7 +98,7 @@ module ActiveJob
# end
# end
#
- # The number of times a job except specific class was enqueued can be asserted.
+ # Asserts the number of times a job except specific class was enqueued by passing +:except+ option.
#
# def test_logging_job
# assert_enqueued_jobs 1, except: HelloJob do
@@ -107,7 +107,10 @@ module ActiveJob
# end
# end
#
- # The number of times a job is enqueued to a specific queue can also be asserted.
+ # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
+ # a hash containing the job's class and it's argument are passed as argument.
+ #
+ # Asserts the number of times a job is enqueued to a specific queue by passing +:queue+ option.
#
# def test_logging_job
# assert_enqueued_jobs 2, queue: 'default' do
@@ -117,14 +120,18 @@ module ActiveJob
# end
def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil)
if block_given?
- original_count = enqueued_jobs_size(only: only, except: except, queue: queue)
+ original_count = enqueued_jobs_with(only: only, except: except, queue: queue)
+
yield
- new_count = enqueued_jobs_size(only: only, except: except, queue: queue)
- assert_equal number, new_count - original_count, "#{number} jobs expected, but #{new_count - original_count} were enqueued"
+
+ new_count = enqueued_jobs_with(only: only, except: except, queue: queue)
+
+ actual_count = new_count - original_count
else
- actual_count = enqueued_jobs_size(only: only, except: except, queue: queue)
- assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
+ actual_count = enqueued_jobs_with(only: only, except: except, queue: queue)
end
+
+ assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
end
# Asserts that no jobs have been enqueued.
@@ -135,7 +142,7 @@ module ActiveJob
# assert_enqueued_jobs 1
# end
#
- # If a block is passed, that block should not cause any job to be enqueued.
+ # If a block is passed, asserts that the block will not cause any job to be enqueued.
#
# def test_jobs_again
# assert_no_enqueued_jobs do
@@ -143,7 +150,7 @@ module ActiveJob
# end
# end
#
- # It can be asserted that no jobs of a specific kind are enqueued:
+ # Asserts that no jobs of a specific kind are enqueued by passing +:only+ option.
#
# def test_no_logging
# assert_no_enqueued_jobs only: LoggingJob do
@@ -151,7 +158,7 @@ module ActiveJob
# end
# end
#
- # It can be asserted that no jobs except specific class are enqueued:
+ # Asserts that no jobs except specific class are enqueued by passing +:except+ option.
#
# def test_no_logging
# assert_no_enqueued_jobs except: HelloJob do
@@ -159,16 +166,27 @@ module ActiveJob
# end
# end
#
+ # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
+ # a hash containing the job's class and it's argument are passed as argument.
+ #
+ # Asserts that no jobs are enqueued to a specific queue by passing +:queue+ option
+ #
+ # def test_no_logging
+ # assert_no_enqueued_jobs queue: 'default' do
+ # LoggingJob.set(queue: :some_queue).perform_later
+ # end
+ # end
+ #
# Note: This assertion is simply a shortcut for:
#
# assert_enqueued_jobs 0, &block
- def assert_no_enqueued_jobs(only: nil, except: nil, &block)
- assert_enqueued_jobs 0, only: only, except: except, &block
+ def assert_no_enqueued_jobs(only: nil, except: nil, queue: nil, &block)
+ assert_enqueued_jobs 0, only: only, except: except, queue: queue, &block
end
# Asserts that the number of performed jobs matches the given number.
# If no block is passed, <tt>perform_enqueued_jobs</tt>
- # must be called around the job call.
+ # must be called around or after the job call.
#
# def test_jobs
# assert_performed_jobs 0
@@ -178,13 +196,14 @@ module ActiveJob
# end
# assert_performed_jobs 1
#
- # perform_enqueued_jobs do
- # HelloJob.perform_later('yves')
- # assert_performed_jobs 2
- # end
+ # HelloJob.perform_later('yves')
+ #
+ # perform_enqueued_jobs
+ #
+ # assert_performed_jobs 2
# end
#
- # If a block is passed, that block should cause the specified number of
+ # If a block is passed, asserts that the block will cause the specified number of
# jobs to be performed.
#
# def test_jobs_again
@@ -198,7 +217,7 @@ module ActiveJob
# end
# end
#
- # The block form supports filtering. If the :only option is specified,
+ # This method also supports filtering. If the +:only+ option is specified,
# then only the listed job(s) will be performed.
#
# def test_hello_job
@@ -208,7 +227,7 @@ module ActiveJob
# end
# end
#
- # Also if the :except option is specified,
+ # Also if the +:except+ option is specified,
# then the job(s) except specific class will be performed.
#
# def test_hello_job
@@ -229,17 +248,42 @@ module ActiveJob
# end
# end
# end
- def assert_performed_jobs(number, only: nil, except: nil)
+ #
+ # A proc may also be specified. When passed a Proc, the job's instance will be passed as argument.
+ #
+ # def test_hello_and_logging_jobs
+ # assert_nothing_raised do
+ # assert_performed_jobs(1, only: ->(job) { job.is_a?(HelloJob) }) do
+ # HelloJob.perform_later('jeremy')
+ # LoggingJob.perform_later('stewie')
+ # RescueJob.perform_later('david')
+ # end
+ # end
+ # end
+ #
+ # If the +:queue+ option is specified,
+ # then only the job(s) enqueued to a specific queue will be performed.
+ #
+ # def test_assert_performed_jobs_with_queue_option
+ # assert_performed_jobs 1, queue: :some_queue do
+ # HelloJob.set(queue: :some_queue).perform_later("jeremy")
+ # HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ # end
+ # end
+ def assert_performed_jobs(number, only: nil, except: nil, queue: nil, &block)
if block_given?
original_count = performed_jobs.size
- perform_enqueued_jobs(only: only, except: except) { yield }
+
+ perform_enqueued_jobs(only: only, except: except, queue: queue, &block)
+
new_count = performed_jobs.size
- assert_equal number, new_count - original_count,
- "#{number} jobs expected, but #{new_count - original_count} were performed"
+
+ performed_jobs_size = new_count - original_count
else
- performed_jobs_size = performed_jobs.size
- assert_equal number, performed_jobs_size, "#{number} jobs expected, but #{performed_jobs_size} were performed"
+ performed_jobs_size = performed_jobs_with(only: only, except: except, queue: queue)
end
+
+ assert_equal number, performed_jobs_size, "#{number} jobs expected, but #{performed_jobs_size} were performed"
end
# Asserts that no jobs have been performed.
@@ -253,7 +297,7 @@ module ActiveJob
# end
# end
#
- # If a block is passed, that block should not cause any job to be performed.
+ # If a block is passed, asserts that the block will not cause any job to be performed.
#
# def test_jobs_again
# assert_no_performed_jobs do
@@ -261,7 +305,7 @@ module ActiveJob
# end
# end
#
- # The block form supports filtering. If the :only option is specified,
+ # The block form supports filtering. If the +:only+ option is specified,
# then only the listed job(s) will not be performed.
#
# def test_no_logging
@@ -270,7 +314,7 @@ module ActiveJob
# end
# end
#
- # Also if the :except option is specified,
+ # Also if the +:except+ option is specified,
# then the job(s) except specific class will not be performed.
#
# def test_no_logging
@@ -279,14 +323,53 @@ module ActiveJob
# end
# end
#
+ # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
+ # an instance of the job will be passed as argument.
+ #
+ # If the +:queue+ option is specified,
+ # then only the job(s) enqueued to a specific queue will not be performed.
+ #
+ # def test_assert_no_performed_jobs_with_queue_option
+ # assert_no_performed_jobs queue: :some_queue do
+ # HelloJob.set(queue: :other_queue).perform_later("jeremy")
+ # end
+ # end
+ #
# Note: This assertion is simply a shortcut for:
#
# assert_performed_jobs 0, &block
- def assert_no_performed_jobs(only: nil, except: nil, &block)
- assert_performed_jobs 0, only: only, except: except, &block
+ def assert_no_performed_jobs(only: nil, except: nil, queue: nil, &block)
+ assert_performed_jobs 0, only: only, except: except, queue: queue, &block
end
- # Asserts that the job passed in the block has been enqueued with the given arguments.
+ # Asserts that the job has been enqueued with the given arguments.
+ #
+ # def test_assert_enqueued_with
+ # MyJob.perform_later(1,2,3)
+ # assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low')
+ #
+ # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
+ # assert_enqueued_with(job: MyJob, at: Date.tomorrow.noon)
+ # end
+ #
+ #
+ # The +args+ argument also accepts a proc which will get passed the actual
+ # job's arguments. Your proc needs to returns a boolean value determining if
+ # the job's arguments matches your expectation. This is useful to check only
+ # for a subset of arguments.
+ #
+ # def test_assert_enqueued_with
+ # expected_args = ->(job_args) do
+ # assert job_args.first.key?(:foo)
+ # end
+ #
+ # MyJob.perform_later(foo: 'bar', other_arg: 'No need to check in the test')
+ # assert_enqueued_with(job: MyJob, args: expected_args, queue: 'low')
+ # end
+ #
+ #
+ # If a block is passed, asserts that the block will cause the job to be
+ # enqueued with the given arguments.
#
# def test_assert_enqueued_with
# assert_enqueued_with(job: MyJob, args: [1,2,3], queue: 'low') do
@@ -298,19 +381,70 @@ module ActiveJob
# end
# end
def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil)
- original_enqueued_jobs_count = enqueued_jobs.count
expected = { job: job, args: args, at: at, queue: queue }.compact
- serialized_args = serialize_args_for_assertion(expected)
- yield
- in_block_jobs = enqueued_jobs.drop(original_enqueued_jobs_count)
- matching_job = in_block_jobs.find do |in_block_job|
- serialized_args.all? { |key, value| value == in_block_job[key] }
+ expected_args = prepare_args_for_assertion(expected)
+
+ if block_given?
+ original_enqueued_jobs_count = enqueued_jobs.count
+
+ yield
+
+ jobs = enqueued_jobs.drop(original_enqueued_jobs_count)
+ else
+ jobs = enqueued_jobs
end
+
+ matching_job = jobs.find do |enqueued_job|
+ deserialized_job = deserialize_args_for_assertion(enqueued_job)
+
+ expected_args.all? do |key, value|
+ if value.respond_to?(:call)
+ value.call(deserialized_job[key])
+ else
+ value == deserialized_job[key]
+ end
+ end
+ end
+
assert matching_job, "No enqueued job found with #{expected}"
instantiate_job(matching_job)
end
- # Asserts that the job passed in the block has been performed with the given arguments.
+ # Asserts that the job has been performed with the given arguments.
+ #
+ # def test_assert_performed_with
+ # MyJob.perform_later(1,2,3)
+ #
+ # perform_enqueued_jobs
+ #
+ # assert_performed_with(job: MyJob, args: [1,2,3], queue: 'high')
+ #
+ # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
+ #
+ # perform_enqueued_jobs
+ #
+ # assert_performed_with(job: MyJob, at: Date.tomorrow.noon)
+ # end
+ #
+ # The +args+ argument also accepts a proc which will get passed the actual
+ # job's arguments. Your proc needs to returns a boolean value determining if
+ # the job's arguments matches your expectation. This is useful to check only
+ # for a subset of arguments.
+ #
+ # def test_assert_performed_with
+ # expected_args = ->(job_args) do
+ # assert job_args.first.key?(:foo)
+ # end
+ # MyJob.perform_later(foo: 'bar', other_arg: 'No need to check in the test')
+ #
+ # perform_enqueued_jobs
+ #
+ # assert_performed_with(job: MyJob, args: expected_args, queue: 'high')
+ # end
+ #
+ # If a block is passed, that block performs all of the jobs that were
+ # enqueued throughout the duration of the block and asserts that
+ # the job has been performed with the given arguments in the block.
#
# def test_assert_performed_with
# assert_performed_with(job: MyJob, args: [1,2,3], queue: 'high') do
@@ -321,20 +455,39 @@ module ActiveJob
# MyJob.set(wait_until: Date.tomorrow.noon).perform_later
# end
# end
- def assert_performed_with(job: nil, args: nil, at: nil, queue: nil)
- original_performed_jobs_count = performed_jobs.count
+ def assert_performed_with(job: nil, args: nil, at: nil, queue: nil, &block)
expected = { job: job, args: args, at: at, queue: queue }.compact
- serialized_args = serialize_args_for_assertion(expected)
- perform_enqueued_jobs { yield }
- in_block_jobs = performed_jobs.drop(original_performed_jobs_count)
- matching_job = in_block_jobs.find do |in_block_job|
- serialized_args.all? { |key, value| value == in_block_job[key] }
+ expected_args = prepare_args_for_assertion(expected)
+
+ if block_given?
+ original_performed_jobs_count = performed_jobs.count
+
+ perform_enqueued_jobs(&block)
+
+ jobs = performed_jobs.drop(original_performed_jobs_count)
+ else
+ jobs = performed_jobs
end
+
+ matching_job = jobs.find do |enqueued_job|
+ deserialized_job = deserialize_args_for_assertion(enqueued_job)
+
+ expected_args.all? do |key, value|
+ if value.respond_to?(:call)
+ value.call(deserialized_job[key])
+ else
+ value == deserialized_job[key]
+ end
+ end
+ end
+
assert matching_job, "No performed job found with #{expected}"
instantiate_job(matching_job)
end
- # Performs all enqueued jobs in the duration of the block.
+ # Performs all enqueued jobs. If a block is given, performs all of the jobs
+ # that were enqueued throughout the duration of the block. If a block is
+ # not given, performs all of the enqueued jobs up to this point in the test.
#
# def test_perform_enqueued_jobs
# perform_enqueued_jobs do
@@ -343,6 +496,14 @@ module ActiveJob
# assert_performed_jobs 1
# end
#
+ # def test_perform_enqueued_jobs_without_block
+ # MyJob.perform_later(1, 2, 3)
+ #
+ # perform_enqueued_jobs
+ #
+ # assert_performed_jobs 1
+ # end
+ #
# This method also supports filtering. If the +:only+ option is specified,
# then only the listed job(s) will be performed.
#
@@ -365,24 +526,45 @@ module ActiveJob
# assert_performed_jobs 1
# end
#
- def perform_enqueued_jobs(only: nil, except: nil)
+ # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
+ # an instance of the job will be passed as argument.
+ #
+ # If the +:queue+ option is specified,
+ # then only the job(s) enqueued to a specific queue will be performed.
+ #
+ # def test_perform_enqueued_jobs_with_queue
+ # perform_enqueued_jobs queue: :some_queue do
+ # MyJob.set(queue: :some_queue).perform_later(1, 2, 3) # will be performed
+ # HelloJob.set(queue: :other_queue).perform_later(1, 2, 3) # will not be performed
+ # end
+ # assert_performed_jobs 1
+ # end
+ #
+ def perform_enqueued_jobs(only: nil, except: nil, queue: nil)
+ return flush_enqueued_jobs(only: only, except: except, queue: queue) unless block_given?
+
validate_option(only: only, except: except)
+
old_perform_enqueued_jobs = queue_adapter.perform_enqueued_jobs
old_perform_enqueued_at_jobs = queue_adapter.perform_enqueued_at_jobs
old_filter = queue_adapter.filter
old_reject = queue_adapter.reject
+ old_queue = queue_adapter.queue
begin
queue_adapter.perform_enqueued_jobs = true
queue_adapter.perform_enqueued_at_jobs = true
queue_adapter.filter = only
queue_adapter.reject = except
+ queue_adapter.queue = queue
+
yield
ensure
queue_adapter.perform_enqueued_jobs = old_perform_enqueued_jobs
queue_adapter.perform_enqueued_at_jobs = old_perform_enqueued_at_jobs
queue_adapter.filter = old_filter
queue_adapter.reject = old_reject
+ queue_adapter.queue = old_queue
end
end
@@ -404,31 +586,64 @@ module ActiveJob
performed_jobs.clear
end
- def enqueued_jobs_size(only: nil, except: nil, queue: nil)
+ def jobs_with(jobs, only: nil, except: nil, queue: nil)
validate_option(only: only, except: except)
- enqueued_jobs.count do |job|
+
+ jobs.count do |job|
job_class = job.fetch(:job)
+
if only
- next false unless Array(only).include?(job_class)
+ next false unless filter_as_proc(only).call(job)
elsif except
- next false if Array(except).include?(job_class)
+ next false if filter_as_proc(except).call(job)
end
+
if queue
next false unless queue.to_s == job.fetch(:queue, job_class.queue_name)
end
+
+ yield job if block_given?
+
true
end
end
- def serialize_args_for_assertion(args)
- args.dup.tap do |serialized_args|
- serialized_args[:args] = ActiveJob::Arguments.serialize(serialized_args[:args]) if serialized_args[:args]
- serialized_args[:at] = serialized_args[:at].to_f if serialized_args[:at]
+ def filter_as_proc(filter)
+ return filter if filter.is_a?(Proc)
+
+ ->(job) { Array(filter).include?(job.fetch(:job)) }
+ end
+
+ def enqueued_jobs_with(only: nil, except: nil, queue: nil, &block)
+ jobs_with(enqueued_jobs, only: only, except: except, queue: queue, &block)
+ end
+
+ def performed_jobs_with(only: nil, except: nil, queue: nil, &block)
+ jobs_with(performed_jobs, only: only, except: except, queue: queue, &block)
+ end
+
+ def flush_enqueued_jobs(only: nil, except: nil, queue: nil)
+ enqueued_jobs_with(only: only, except: except, queue: queue) do |payload|
+ instantiate_job(payload).perform_now
+ queue_adapter.performed_jobs << payload
+ end
+ end
+
+ def prepare_args_for_assertion(args)
+ args.dup.tap do |arguments|
+ arguments[:at] = arguments[:at].to_f if arguments[:at]
+ end
+ end
+
+ def deserialize_args_for_assertion(job)
+ job.dup.tap do |new_job|
+ new_job[:args] = ActiveJob::Arguments.deserialize(new_job[:args]) if new_job[:args]
end
end
def instantiate_job(payload)
- job = payload[:job].new(*payload[:args])
+ args = ActiveJob::Arguments.deserialize(payload[:args])
+ job = payload[:job].new(*args)
job.scheduled_at = Time.at(payload[:at]) if payload.key?(:at)
job.queue_name = payload[:queue]
job