aboutsummaryrefslogtreecommitdiffstats
path: root/activejob
diff options
context:
space:
mode:
Diffstat (limited to 'activejob')
-rw-r--r--activejob/CHANGELOG.md69
-rw-r--r--activejob/MIT-LICENSE2
-rw-r--r--activejob/README.md6
-rw-r--r--activejob/Rakefile5
-rw-r--r--activejob/activejob.gemspec2
-rw-r--r--activejob/lib/active_job.rb3
-rw-r--r--activejob/lib/active_job/arguments.rb34
-rw-r--r--activejob/lib/active_job/base.rb2
-rw-r--r--activejob/lib/active_job/core.rb27
-rw-r--r--activejob/lib/active_job/exceptions.rb28
-rw-r--r--activejob/lib/active_job/gem_version.rb6
-rw-r--r--activejob/lib/active_job/logging.rb15
-rw-r--r--activejob/lib/active_job/queue_adapter.rb30
-rw-r--r--activejob/lib/active_job/queue_adapters.rb4
-rw-r--r--activejob/lib/active_job/queue_adapters/inline_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters/qu_adapter.rb46
-rw-r--r--activejob/lib/active_job/queue_adapters/test_adapter.rb6
-rw-r--r--activejob/lib/active_job/railtie.rb13
-rw-r--r--activejob/lib/active_job/serializers.rb63
-rw-r--r--activejob/lib/active_job/serializers/date_serializer.rb21
-rw-r--r--activejob/lib/active_job/serializers/date_time_serializer.rb21
-rw-r--r--activejob/lib/active_job/serializers/duration_serializer.rb24
-rw-r--r--activejob/lib/active_job/serializers/object_serializer.rb54
-rw-r--r--activejob/lib/active_job/serializers/symbol_serializer.rb21
-rw-r--r--activejob/lib/active_job/serializers/time_serializer.rb21
-rw-r--r--activejob/lib/active_job/serializers/time_with_zone_serializer.rb21
-rw-r--r--activejob/lib/active_job/test_helper.rb44
-rw-r--r--activejob/lib/active_job/timezones.rb13
-rw-r--r--activejob/lib/active_job/translation.rb2
-rw-r--r--activejob/lib/rails/generators/job/job_generator.rb4
-rw-r--r--activejob/test/adapters/qu.rb5
-rw-r--r--activejob/test/cases/argument_serialization_test.rb56
-rw-r--r--activejob/test/cases/exceptions_test.rb25
-rw-r--r--activejob/test/cases/job_serialization_test.rb19
-rw-r--r--activejob/test/cases/logging_test.rb23
-rw-r--r--activejob/test/cases/serializers_test.rb98
-rw-r--r--activejob/test/cases/test_helper_test.rb160
-rw-r--r--activejob/test/cases/timezones_test.rb24
-rw-r--r--activejob/test/integration/queuing_test.rb18
-rw-r--r--activejob/test/jobs/retry_job.rb11
-rw-r--r--activejob/test/jobs/timezone_dependent_job.rb22
-rw-r--r--activejob/test/support/integration/adapters/qu.rb40
-rw-r--r--activejob/test/support/integration/dummy_app_template.rb1
-rw-r--r--activejob/test/support/integration/test_case_helpers.rb4
-rw-r--r--activejob/test/support/queue_classic/inline.rb7
-rw-r--r--activejob/test/support/sneakers/inline.rb3
46 files changed, 926 insertions, 199 deletions
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md
index 4453f845f4..8526741383 100644
--- a/activejob/CHANGELOG.md
+++ b/activejob/CHANGELOG.md
@@ -1,12 +1,71 @@
-## Rails 5.2.0.beta2 (November 28, 2017) ##
+* Move `enqueue`/`enqueue_at` notifications to an around callback.
-* No changes.
+ Improves timing accuracy over the old after callback by including
+ time spent writing to the adapter's IO implementation.
+ *Zach Kemp*
-## Rails 5.2.0.beta1 (November 27, 2017) ##
+* Allow `queue` option to `assert_no_enqueued_jobs`.
-* Support redis-rb 4.0.
+ Example:
+ ```
+ def test_no_logging
+ assert_no_enqueued_jobs queue: 'default' do
+ LoggingJob.set(queue: :some_queue).perform_later
+ end
+ end
+ ```
+
+ *bogdanvlviv*
+
+* Allow call `assert_enqueued_with` with no block.
+
+ Example:
+ ```
+ 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
+ ```
+
+ *bogdanvlviv*
+
+* Allow passing multiple exceptions to `retry_on`, and `discard_on`.
+
+ *George Claghorn*
+
+* Pass the error instance as the second parameter of block executed by `discard_on`.
+
+ Fixes #32853.
+
+ *Yuji Yaginuma*
+
+* Remove support for Qu gem.
+
+ Reasons are that the Qu gem wasn't compatible since Rails 5.1,
+ gem development was stopped in 2014 and maintainers have
+ confirmed its demise. See issue #32273
+
+ *Alberto Almagro*
+
+* Add support for timezones to Active Job.
+
+ Record what was the current timezone in effect when the job was
+ enqueued and then restore when the job is executed in same way
+ that the current locale is recorded and restored.
+
+ *Andrew White*
+
+* Rails 6 requires Ruby 2.4.1 or newer.
*Jeremy Daer*
-Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/activejob/CHANGELOG.md) for previous changes.
+* Add support to define custom argument serializers.
+
+ *Evgenii Pecherkin*, *Rafael Mendonça França*
+
+
+Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activejob/CHANGELOG.md) for previous changes.
diff --git a/activejob/MIT-LICENSE b/activejob/MIT-LICENSE
index daa726b9f0..274211f710 100644
--- a/activejob/MIT-LICENSE
+++ b/activejob/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014-2017 David Heinemeier Hansson
+Copyright (c) 2014-2018 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activejob/README.md b/activejob/README.md
index f1ebb76e08..d49fcfe3c2 100644
--- a/activejob/README.md
+++ b/activejob/README.md
@@ -88,6 +88,12 @@ Active Job has built-in adapters for multiple queueing backends (Sidekiq,
Resque, Delayed Job and others). To get an up-to-date list of the adapters
see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
+**Please note:** We are not accepting pull requests for new adapters. We
+encourage library authors to provide an ActiveJob adapter as part of
+their gem, or as a stand-alone gem. For discussion about this see the
+following PRs: [23311](https://github.com/rails/rails/issues/23311#issuecomment-176275718),
+[21406](https://github.com/rails/rails/pull/21406#issuecomment-138813484), and [#32285](https://github.com/rails/rails/pull/32285).
+
## Auxiliary gems
* [activejob-stats](https://github.com/seuros/activejob-stats)
diff --git a/activejob/Rakefile b/activejob/Rakefile
index 77f3e7f8ff..0f88b22e8d 100644
--- a/activejob/Rakefile
+++ b/activejob/Rakefile
@@ -2,7 +2,6 @@
require "rake/testtask"
-# TODO: add qu back to the list after it support Rails 5.1
ACTIVEJOB_ADAPTERS = %w(async inline delayed_job que queue_classic resque sidekiq sneakers sucker_punch backburner test)
ACTIVEJOB_ADAPTERS.delete("queue_classic") if defined?(JRUBY_VERSION)
@@ -39,7 +38,7 @@ namespace :test do
t.libs << "test"
t.test_files = FileList["test/cases/**/*_test.rb"]
t.verbose = true
- t.warning = false
+ t.warning = true
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
end
@@ -57,7 +56,7 @@ namespace :test do
t.libs << "test"
t.test_files = FileList["test/integration/**/*_test.rb"]
t.verbose = true
- t.warning = false
+ t.warning = true
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
end
end
diff --git a/activejob/activejob.gemspec b/activejob/activejob.gemspec
index 71e32f695b..be6292f737 100644
--- a/activejob/activejob.gemspec
+++ b/activejob/activejob.gemspec
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
s.summary = "Job framework with pluggable queues."
s.description = "Declare job classes that can be run by a variety of queueing backends."
- s.required_ruby_version = ">= 2.2.2"
+ s.required_ruby_version = ">= 2.4.1"
s.license = "MIT"
diff --git a/activejob/lib/active_job.rb b/activejob/lib/active_job.rb
index 1f5bd7b3e9..01fab4d918 100644
--- a/activejob/lib/active_job.rb
+++ b/activejob/lib/active_job.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
#--
-# Copyright (c) 2014-2017 David Heinemeier Hansson
+# Copyright (c) 2014-2018 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -33,6 +33,7 @@ module ActiveJob
autoload :Base
autoload :QueueAdapters
+ autoload :Serializers
autoload :ConfiguredJob
autoload :TestCase
autoload :TestHelper
diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb
index de11e7fcb1..86bb0c5540 100644
--- a/activejob/lib/active_job/arguments.rb
+++ b/activejob/lib/active_job/arguments.rb
@@ -14,8 +14,8 @@ module ActiveJob
end
# Raised when an unsupported argument type is set as a job argument. We
- # currently support NilClass, Integer, Fixnum, Float, String, TrueClass, FalseClass,
- # Bignum, BigDecimal, and objects that can be represented as GlobalIDs (ex: Active Record).
+ # currently support NilClass, Integer, Float, String, TrueClass, FalseClass,
+ # BigDecimal, and objects that can be represented as GlobalIDs (ex: Active Record).
# 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.
@@ -25,7 +25,6 @@ module ActiveJob
extend self
# :nodoc:
TYPE_WHITELIST = [ NilClass, String, Integer, Float, BigDecimal, TrueClass, FalseClass ]
- TYPE_WHITELIST.push(Fixnum, Bignum) unless 1.class == Integer
# Serializes a set of arguments. Whitelisted types are returned
# as-is. Arrays/Hashes are serialized element by element.
@@ -44,13 +43,24 @@ module ActiveJob
end
private
+
# :nodoc:
GLOBALID_KEY = "_aj_globalid".freeze
# :nodoc:
SYMBOL_KEYS_KEY = "_aj_symbol_keys".freeze
# :nodoc:
WITH_INDIFFERENT_ACCESS_KEY = "_aj_hash_with_indifferent_access".freeze
- private_constant :GLOBALID_KEY, :SYMBOL_KEYS_KEY, :WITH_INDIFFERENT_ACCESS_KEY
+ # :nodoc:
+ OBJECT_SERIALIZER_KEY = "_aj_serialized"
+
+ # :nodoc:
+ RESERVED_KEYS = [
+ GLOBALID_KEY, GLOBALID_KEY.to_sym,
+ SYMBOL_KEYS_KEY, SYMBOL_KEYS_KEY.to_sym,
+ OBJECT_SERIALIZER_KEY, OBJECT_SERIALIZER_KEY.to_sym,
+ WITH_INDIFFERENT_ACCESS_KEY, WITH_INDIFFERENT_ACCESS_KEY.to_sym,
+ ]
+ private_constant :RESERVED_KEYS
def serialize_argument(argument)
case argument
@@ -70,7 +80,7 @@ module ActiveJob
result[SYMBOL_KEYS_KEY] = symbol_keys
result
else
- raise SerializationError.new("Unsupported argument type: #{argument.class.name}")
+ Serializers.serialize(argument)
end
end
@@ -85,6 +95,8 @@ module ActiveJob
when Hash
if serialized_global_id?(argument)
deserialize_global_id argument
+ elsif custom_serialized?(argument)
+ Serializers.deserialize(argument)
else
deserialize_hash(argument)
end
@@ -101,6 +113,10 @@ module ActiveJob
GlobalID::Locator.locate hash[GLOBALID_KEY]
end
+ def custom_serialized?(hash)
+ hash.key?(OBJECT_SERIALIZER_KEY)
+ end
+
def serialize_hash(argument)
argument.each_with_object({}) do |(key, value), hash|
hash[serialize_hash_key(key)] = serialize_argument(value)
@@ -117,14 +133,6 @@ module ActiveJob
result
end
- # :nodoc:
- RESERVED_KEYS = [
- GLOBALID_KEY, GLOBALID_KEY.to_sym,
- SYMBOL_KEYS_KEY, SYMBOL_KEYS_KEY.to_sym,
- WITH_INDIFFERENT_ACCESS_KEY, WITH_INDIFFERENT_ACCESS_KEY.to_sym,
- ]
- private_constant :RESERVED_KEYS
-
def serialize_hash_key(key)
case key
when *RESERVED_KEYS
diff --git a/activejob/lib/active_job/base.rb b/activejob/lib/active_job/base.rb
index ae112abb2c..95b1062701 100644
--- a/activejob/lib/active_job/base.rb
+++ b/activejob/lib/active_job/base.rb
@@ -9,6 +9,7 @@ require "active_job/execution"
require "active_job/callbacks"
require "active_job/exceptions"
require "active_job/logging"
+require "active_job/timezones"
require "active_job/translation"
module ActiveJob #:nodoc:
@@ -67,6 +68,7 @@ module ActiveJob #:nodoc:
include Callbacks
include Exceptions
include Logging
+ include Timezones
include Translation
ActiveSupport.run_load_hooks(:active_job, self)
diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb
index 879746fc01..61d402cfca 100644
--- a/activejob/lib/active_job/core.rb
+++ b/activejob/lib/active_job/core.rb
@@ -31,6 +31,9 @@ module ActiveJob
# I18n.locale to be used during the job.
attr_accessor :locale
+
+ # Timezone to be used during the job.
+ attr_accessor :timezone
end
# These methods will be included into any Active Job object, adding
@@ -85,9 +88,10 @@ module ActiveJob
"provider_job_id" => provider_job_id,
"queue_name" => queue_name,
"priority" => priority,
- "arguments" => serialize_arguments(arguments),
+ "arguments" => serialize_arguments_if_needed(arguments),
"executions" => executions,
- "locale" => I18n.locale.to_s
+ "locale" => I18n.locale.to_s,
+ "timezone" => Time.zone.try(:name)
}
end
@@ -125,22 +129,35 @@ module ActiveJob
self.serialized_arguments = job_data["arguments"]
self.executions = job_data["executions"]
self.locale = job_data["locale"] || I18n.locale.to_s
+ self.timezone = job_data["timezone"] || Time.zone.try(:name)
end
private
+ def serialize_arguments_if_needed(arguments)
+ if arguments_serialized?
+ @serialized_arguments
+ else
+ serialize_arguments(arguments)
+ end
+ end
+
def deserialize_arguments_if_needed
- if defined?(@serialized_arguments) && @serialized_arguments.present?
+ if arguments_serialized?
@arguments = deserialize_arguments(@serialized_arguments)
@serialized_arguments = nil
end
end
- def serialize_arguments(serialized_args)
- Arguments.serialize(serialized_args)
+ def serialize_arguments(arguments)
+ Arguments.serialize(arguments)
end
def deserialize_arguments(serialized_args)
Arguments.deserialize(serialized_args)
end
+
+ def arguments_serialized?
+ defined?(@serialized_arguments) && @serialized_arguments
+ end
end
end
diff --git a/activejob/lib/active_job/exceptions.rb b/activejob/lib/active_job/exceptions.rb
index 8b4a88ba6a..1e57dbcb1c 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,16 @@ 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|
+ def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil)
+ rescue_from(*exceptions) do |error|
if executions < attempts
- logger.error "Retrying #{self.class} in #{wait} seconds, due to a #{exception}. The original exception was #{error.cause.inspect}."
+ logger.error "Retrying #{self.class} in #{wait} seconds, due to a #{error.class}. The original exception was #{error.cause.inspect}."
retry_job wait: determine_delay(wait), queue: queue, priority: priority
else
if block_given?
yield self, error
else
- logger.error "Stopped retrying #{self.class} due to a #{exception}, which reoccurred on #{executions} attempts. The original exception was #{error.cause.inspect}."
+ logger.error "Stopped retrying #{self.class} due to a #{error.class}, which reoccurred on #{executions} attempts. The original exception was #{error.cause.inspect}."
raise error
end
end
@@ -61,18 +61,28 @@ module ActiveJob
# Discard the job with no attempts to retry, if the exception is raised. This is useful when the subject of the job,
# like an Active Record, is no longer available, and the job is thus no longer relevant.
#
+ # You can also pass a block that'll be invoked. This block is yielded with the job instance as the first and the error instance as the second parameter.
+ #
# ==== Example
#
# class SearchIndexingJob < ActiveJob::Base
# discard_on ActiveJob::DeserializationError
+ # discard_on(CustomAppException) do |job, error|
+ # ExceptionNotifier.caught(error)
+ # end
#
# def perform(record)
# # Will raise ActiveJob::DeserializationError if the record can't be deserialized
+ # # Might raise CustomAppException for something domain specific
# end
# end
- def discard_on(exception)
- rescue_from exception do |error|
- 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|
+ if block_given?
+ yield self, error
+ else
+ logger.error "Discarded #{self.class} due to a #{error.class}. The original exception was #{error.cause.inspect}."
+ end
end
end
end
diff --git a/activejob/lib/active_job/gem_version.rb b/activejob/lib/active_job/gem_version.rb
index 49dfd4095e..770f70dc5e 100644
--- a/activejob/lib/active_job/gem_version.rb
+++ b/activejob/lib/active_job/gem_version.rb
@@ -7,10 +7,10 @@ module ActiveJob
end
module VERSION
- MAJOR = 5
- MINOR = 2
+ MAJOR = 6
+ MINOR = 0
TINY = 0
- PRE = "beta2"
+ PRE = "alpha"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activejob/lib/active_job/logging.rb b/activejob/lib/active_job/logging.rb
index f53b7eaee5..9ffd60ad53 100644
--- a/activejob/lib/active_job/logging.rb
+++ b/activejob/lib/active_job/logging.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/hash/transform_values"
require "active_support/core_ext/string/filters"
require "active_support/tagged_logging"
require "active_support/logger"
@@ -12,13 +11,13 @@ module ActiveJob
included do
cattr_accessor :logger, default: ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
- around_enqueue do |_, block, _|
+ around_enqueue do |_, block|
tag_logger do
block.call
end
end
- around_perform do |job, block, _|
+ around_perform do |job, block|
tag_logger(job.class.name, job.job_id) do
payload = { adapter: job.class.queue_adapter, job: job }
ActiveSupport::Notifications.instrument("perform_start.active_job", payload.dup)
@@ -28,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
diff --git a/activejob/lib/active_job/queue_adapter.rb b/activejob/lib/active_job/queue_adapter.rb
index dd05800baf..006a683b85 100644
--- a/activejob/lib/active_job/queue_adapter.rb
+++ b/activejob/lib/active_job/queue_adapter.rb
@@ -29,28 +29,22 @@ module ActiveJob
# Specify the backend queue provider. The default queue adapter
# is the +:async+ queue. See QueueAdapters for more
# information.
- def queue_adapter=(name_or_adapter_or_class)
- interpret_adapter(name_or_adapter_or_class)
- end
-
- private
-
- def interpret_adapter(name_or_adapter_or_class)
- case name_or_adapter_or_class
- when Symbol, String
- assign_adapter(name_or_adapter_or_class.to_s,
- ActiveJob::QueueAdapters.lookup(name_or_adapter_or_class).new)
+ def queue_adapter=(name_or_adapter)
+ case name_or_adapter
+ when Symbol, String
+ queue_adapter = ActiveJob::QueueAdapters.lookup(name_or_adapter).new
+ assign_adapter(name_or_adapter.to_s, queue_adapter)
+ else
+ if queue_adapter?(name_or_adapter)
+ adapter_name = "#{name_or_adapter.class.name.demodulize.remove('Adapter').underscore}"
+ assign_adapter(adapter_name, name_or_adapter)
else
- if queue_adapter?(name_or_adapter_or_class)
- adapter_name = "#{name_or_adapter_or_class.class.name.demodulize.remove('Adapter').underscore}"
- assign_adapter(adapter_name,
- name_or_adapter_or_class)
- else
- raise ArgumentError
- end
+ raise ArgumentError
end
end
+ end
+ private
def assign_adapter(adapter_name, queue_adapter)
self._queue_adapter_name = adapter_name
self._queue_adapter = queue_adapter
diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb
index c1a1d3c510..00c7b407b1 100644
--- a/activejob/lib/active_job/queue_adapters.rb
+++ b/activejob/lib/active_job/queue_adapters.rb
@@ -7,7 +7,6 @@ module ActiveJob
#
# * {Backburner}[https://github.com/nesquena/backburner]
# * {Delayed Job}[https://github.com/collectiveidea/delayed_job]
- # * {Qu}[https://github.com/bkeepers/qu]
# * {Que}[https://github.com/chanks/que]
# * {queue_classic}[https://github.com/QueueClassic/queue_classic]
# * {Resque}[https://github.com/resque/resque]
@@ -16,6 +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}[link:files/activejob/README_md.html] for more details.
#
# === Backends Features
#
@@ -23,7 +23,6 @@ module ActiveJob
# |-------------------|-------|--------|------------|------------|---------|---------|
# | Backburner | Yes | Yes | Yes | Yes | Job | Global |
# | Delayed Job | Yes | Yes | Yes | Job | Global | Global |
- # | Qu | Yes | Yes | No | No | No | Global |
# | Que | Yes | Yes | Yes | Job | No | Job |
# | queue_classic | Yes | Yes | Yes* | No | No | No |
# | Resque | Yes | Yes | Yes (Gem) | Queue | Global | Yes |
@@ -114,7 +113,6 @@ module ActiveJob
autoload :InlineAdapter
autoload :BackburnerAdapter
autoload :DelayedJobAdapter
- autoload :QuAdapter
autoload :QueAdapter
autoload :QueueClassicAdapter
autoload :ResqueAdapter
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/qu_adapter.rb b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
deleted file mode 100644
index bd7003e177..0000000000
--- a/activejob/lib/active_job/queue_adapters/qu_adapter.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require "qu"
-
-module ActiveJob
- module QueueAdapters
- # == Qu adapter for Active Job
- #
- # Qu is a Ruby library for queuing and processing background jobs. It is
- # heavily inspired by delayed_job and Resque. Qu was created to overcome
- # some shortcomings in the existing queuing libraries.
- # The advantages of Qu are: Multiple backends (redis, mongo), jobs are
- # requeued when worker is killed, resque-like API.
- #
- # Read more about Qu {here}[https://github.com/bkeepers/qu].
- #
- # To use Qu set the queue_adapter config to +:qu+.
- #
- # Rails.application.config.active_job.queue_adapter = :qu
- class QuAdapter
- def enqueue(job, *args) #:nodoc:
- qu_job = Qu::Payload.new(klass: JobWrapper, args: [job.serialize]).tap do |payload|
- payload.instance_variable_set(:@queue, job.queue_name)
- end.push
-
- # qu_job can be nil depending on the configured backend
- job.provider_job_id = qu_job.id unless qu_job.nil?
- qu_job
- end
-
- def enqueue_at(job, timestamp, *args) #:nodoc:
- raise NotImplementedError, "This queueing backend does not support scheduling jobs. To see what features are supported go to http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html"
- end
-
- class JobWrapper < Qu::Job #:nodoc:
- def initialize(job_data)
- @job_data = job_data
- end
-
- def perform
- Base.execute @job_data
- end
- end
- 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..3aa25425eb 100644
--- a/activejob/lib/active_job/queue_adapters/test_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/test_adapter.rb
@@ -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
diff --git a/activejob/lib/active_job/railtie.rb b/activejob/lib/active_job/railtie.rb
index 7b0742a6d2..d0294854d3 100644
--- a/activejob/lib/active_job/railtie.rb
+++ b/activejob/lib/active_job/railtie.rb
@@ -7,17 +7,28 @@ module ActiveJob
# = Active Job Railtie
class Railtie < Rails::Railtie # :nodoc:
config.active_job = ActiveSupport::OrderedOptions.new
+ config.active_job.custom_serializers = []
initializer "active_job.logger" do
ActiveSupport.on_load(:active_job) { self.logger = ::Rails.logger }
end
+ initializer "active_job.custom_serializers" do |app|
+ config.after_initialize do
+ custom_serializers = app.config.active_job.delete(:custom_serializers)
+ ActiveJob::Serializers.add_serializers custom_serializers
+ end
+ end
+
initializer "active_job.set_configs" do |app|
options = app.config.active_job
options.queue_adapter ||= :async
ActiveSupport.on_load(:active_job) do
- options.each { |k, v| send("#{k}=", v) }
+ options.each do |k, v|
+ k = "#{k}="
+ send(k, v) if respond_to? k
+ end
end
end
diff --git a/activejob/lib/active_job/serializers.rb b/activejob/lib/active_job/serializers.rb
new file mode 100644
index 0000000000..a5d90f48b8
--- /dev/null
+++ b/activejob/lib/active_job/serializers.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require "set"
+
+module ActiveJob
+ # The <tt>ActiveJob::Serializers</tt> module is used to store a list of known serializers
+ # and to add new ones. It also has helpers to serialize/deserialize objects.
+ module Serializers # :nodoc:
+ extend ActiveSupport::Autoload
+
+ autoload :ObjectSerializer
+ autoload :SymbolSerializer
+ autoload :DurationSerializer
+ autoload :DateTimeSerializer
+ autoload :DateSerializer
+ autoload :TimeWithZoneSerializer
+ autoload :TimeSerializer
+
+ mattr_accessor :_additional_serializers
+ self._additional_serializers = Set.new
+
+ class << self
+ # Returns serialized representative of the passed object.
+ # Will look up through all known serializers.
+ # Raises <tt>ActiveJob::SerializationError</tt> if it can't find a proper serializer.
+ def serialize(argument)
+ serializer = serializers.detect { |s| s.serialize?(argument) }
+ raise SerializationError.new("Unsupported argument type: #{argument.class.name}") unless serializer
+ serializer.serialize(argument)
+ end
+
+ # Returns deserialized object.
+ # Will look up through all known serializers.
+ # If no serializer found will raise <tt>ArgumentError</tt>.
+ def deserialize(argument)
+ serializer_name = argument[Arguments::OBJECT_SERIALIZER_KEY]
+ raise ArgumentError, "Serializer name is not present in the argument: #{argument.inspect}" unless serializer_name
+
+ serializer = serializer_name.safe_constantize
+ raise ArgumentError, "Serializer #{serializer_name} is not known" unless serializer
+
+ serializer.deserialize(argument)
+ end
+
+ # Returns list of known serializers.
+ def serializers
+ self._additional_serializers
+ end
+
+ # Adds new serializers to a list of known serializers.
+ def add_serializers(*new_serializers)
+ self._additional_serializers += new_serializers.flatten
+ end
+ end
+
+ add_serializers SymbolSerializer,
+ DurationSerializer,
+ DateTimeSerializer,
+ DateSerializer,
+ TimeWithZoneSerializer,
+ TimeSerializer
+ end
+end
diff --git a/activejob/lib/active_job/serializers/date_serializer.rb b/activejob/lib/active_job/serializers/date_serializer.rb
new file mode 100644
index 0000000000..e995d30faa
--- /dev/null
+++ b/activejob/lib/active_job/serializers/date_serializer.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module ActiveJob
+ module Serializers
+ class DateSerializer < ObjectSerializer # :nodoc:
+ def serialize(date)
+ super("value" => date.iso8601)
+ end
+
+ def deserialize(hash)
+ Date.iso8601(hash["value"])
+ end
+
+ private
+
+ def klass
+ Date
+ end
+ end
+ end
+end
diff --git a/activejob/lib/active_job/serializers/date_time_serializer.rb b/activejob/lib/active_job/serializers/date_time_serializer.rb
new file mode 100644
index 0000000000..fe780a1978
--- /dev/null
+++ b/activejob/lib/active_job/serializers/date_time_serializer.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module ActiveJob
+ module Serializers
+ class DateTimeSerializer < ObjectSerializer # :nodoc:
+ def serialize(time)
+ super("value" => time.iso8601)
+ end
+
+ def deserialize(hash)
+ DateTime.iso8601(hash["value"])
+ end
+
+ private
+
+ def klass
+ DateTime
+ end
+ end
+ end
+end
diff --git a/activejob/lib/active_job/serializers/duration_serializer.rb b/activejob/lib/active_job/serializers/duration_serializer.rb
new file mode 100644
index 0000000000..715fe27a5c
--- /dev/null
+++ b/activejob/lib/active_job/serializers/duration_serializer.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module ActiveJob
+ module Serializers
+ class DurationSerializer < ObjectSerializer # :nodoc:
+ def serialize(duration)
+ super("value" => duration.value, "parts" => Arguments.serialize(duration.parts))
+ end
+
+ def deserialize(hash)
+ value = hash["value"]
+ parts = Arguments.deserialize(hash["parts"])
+
+ klass.new(value, parts)
+ end
+
+ private
+
+ def klass
+ ActiveSupport::Duration
+ end
+ end
+ end
+end
diff --git a/activejob/lib/active_job/serializers/object_serializer.rb b/activejob/lib/active_job/serializers/object_serializer.rb
new file mode 100644
index 0000000000..6d280969be
--- /dev/null
+++ b/activejob/lib/active_job/serializers/object_serializer.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module ActiveJob
+ module Serializers
+ # Base class for serializing and deserializing custom objects.
+ #
+ # Example:
+ #
+ # class MoneySerializer < ActiveJob::Serializers::ObjectSerializer
+ # def serialize(money)
+ # super("amount" => money.amount, "currency" => money.currency)
+ # end
+ #
+ # def deserialize(hash)
+ # Money.new(hash["amount"], hash["currency"])
+ # end
+ #
+ # private
+ #
+ # def klass
+ # Money
+ # end
+ # end
+ class ObjectSerializer
+ include Singleton
+
+ class << self
+ delegate :serialize?, :serialize, :deserialize, to: :instance
+ end
+
+ # Determines if an argument should be serialized by a serializer.
+ def serialize?(argument)
+ argument.is_a?(klass)
+ end
+
+ # Serializes an argument to a JSON primitive type.
+ def serialize(hash)
+ { Arguments::OBJECT_SERIALIZER_KEY => self.class.name }.merge!(hash)
+ end
+
+ # Deserializes an argument from a JSON primitive type.
+ def deserialize(_argument)
+ raise NotImplementedError
+ end
+
+ private
+
+ # The class of the object that will be serialized.
+ def klass # :doc:
+ raise NotImplementedError
+ end
+ end
+ end
+end
diff --git a/activejob/lib/active_job/serializers/symbol_serializer.rb b/activejob/lib/active_job/serializers/symbol_serializer.rb
new file mode 100644
index 0000000000..7e1f9553a2
--- /dev/null
+++ b/activejob/lib/active_job/serializers/symbol_serializer.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module ActiveJob
+ module Serializers
+ class SymbolSerializer < ObjectSerializer # :nodoc:
+ def serialize(argument)
+ super("value" => argument.to_s)
+ end
+
+ def deserialize(argument)
+ argument["value"].to_sym
+ end
+
+ private
+
+ def klass
+ Symbol
+ end
+ end
+ end
+end
diff --git a/activejob/lib/active_job/serializers/time_serializer.rb b/activejob/lib/active_job/serializers/time_serializer.rb
new file mode 100644
index 0000000000..fe20772f35
--- /dev/null
+++ b/activejob/lib/active_job/serializers/time_serializer.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module ActiveJob
+ module Serializers
+ class TimeSerializer < ObjectSerializer # :nodoc:
+ def serialize(time)
+ super("value" => time.iso8601)
+ end
+
+ def deserialize(hash)
+ Time.iso8601(hash["value"])
+ end
+
+ private
+
+ def klass
+ Time
+ end
+ end
+ end
+end
diff --git a/activejob/lib/active_job/serializers/time_with_zone_serializer.rb b/activejob/lib/active_job/serializers/time_with_zone_serializer.rb
new file mode 100644
index 0000000000..43017fc75b
--- /dev/null
+++ b/activejob/lib/active_job/serializers/time_with_zone_serializer.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module ActiveJob
+ module Serializers
+ class TimeWithZoneSerializer < ObjectSerializer # :nodoc:
+ def serialize(time)
+ super("value" => time.iso8601)
+ end
+
+ def deserialize(hash)
+ Time.iso8601(hash["value"]).in_time_zone
+ end
+
+ private
+
+ def klass
+ ActiveSupport::TimeWithZone
+ end
+ end
+ end
+end
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index 1cd2c40c15..04cde28a96 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -159,11 +159,19 @@ module ActiveJob
# end
# end
#
+ # It can be asserted that no jobs are enqueued to a specific queue:
+ #
+ # 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.
@@ -286,7 +294,18 @@ module ActiveJob
assert_performed_jobs 0, only: only, except: except, &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
+ #
+ # If a block is passed, that block should 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,14 +317,23 @@ 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] }
+
+ 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|
+ serialized_args.all? { |key, value| value == enqueued_job[key] }
+ end
+
assert matching_job, "No enqueued job found with #{expected}"
instantiate_job(matching_job)
end
diff --git a/activejob/lib/active_job/timezones.rb b/activejob/lib/active_job/timezones.rb
new file mode 100644
index 0000000000..ac018eb752
--- /dev/null
+++ b/activejob/lib/active_job/timezones.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module ActiveJob
+ module Timezones #:nodoc:
+ extend ActiveSupport::Concern
+
+ included do
+ around_perform do |job, block|
+ Time.use_zone(job.timezone, &block)
+ end
+ end
+ end
+end
diff --git a/activejob/lib/active_job/translation.rb b/activejob/lib/active_job/translation.rb
index fb45c80d67..0fd9b9fc06 100644
--- a/activejob/lib/active_job/translation.rb
+++ b/activejob/lib/active_job/translation.rb
@@ -5,7 +5,7 @@ module ActiveJob
extend ActiveSupport::Concern
included do
- around_perform do |job, block, _|
+ around_perform do |job, block|
I18n.with_locale(job.locale, &block)
end
end
diff --git a/activejob/lib/rails/generators/job/job_generator.rb b/activejob/lib/rails/generators/job/job_generator.rb
index 69b4fe7d26..03346a7f12 100644
--- a/activejob/lib/rails/generators/job/job_generator.rb
+++ b/activejob/lib/rails/generators/job/job_generator.rb
@@ -28,6 +28,10 @@ module Rails # :nodoc:
end
private
+ def file_name
+ @_file_name ||= super.sub(/_job\z/i, "")
+ end
+
def application_job_file_name
@application_job_file_name ||= if mountable_engine?
"app/jobs/#{namespaced_path}/application_job.rb"
diff --git a/activejob/test/adapters/qu.rb b/activejob/test/adapters/qu.rb
deleted file mode 100644
index 5b471fa347..0000000000
--- a/activejob/test/adapters/qu.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-require "qu-immediate"
-
-ActiveJob::Base.queue_adapter = :qu
diff --git a/activejob/test/cases/argument_serialization_test.rb b/activejob/test/cases/argument_serialization_test.rb
index 7e7f854da0..e5f1f087fe 100644
--- a/activejob/test/cases/argument_serialization_test.rb
+++ b/activejob/test/cases/argument_serialization_test.rb
@@ -13,6 +13,9 @@ class ArgumentSerializationTest < ActiveSupport::TestCase
[ nil, 1, 1.0, 1_000_000_000_000_000_000_000,
"a", true, false, BigDecimal(5),
+ :a, 1.day, Date.new(2001, 2, 3), Time.new(2002, 10, 31, 2, 2, 2, "+02:00"),
+ DateTime.new(2001, 2, 3, 4, 5, 6, "+03:00"),
+ ActiveSupport::TimeWithZone.new(Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone["UTC"]),
[ 1, "a" ],
{ "a" => 1 }
].each do |arg|
@@ -21,7 +24,7 @@ class ArgumentSerializationTest < ActiveSupport::TestCase
end
end
- [ :a, Object.new, self, Person.find("5").to_gid ].each do |arg|
+ [ Object.new, self, Person.find("5").to_gid ].each do |arg|
test "does not serialize #{arg.class}" do
assert_raises ActiveJob::SerializationError do
ActiveJob::Arguments.serialize [ arg ]
@@ -46,6 +49,49 @@ class ArgumentSerializationTest < ActiveSupport::TestCase
assert_arguments_roundtrip([a: 1, "b" => 2])
end
+ test "serialize a hash" do
+ symbol_key = { a: 1 }
+ string_key = { "a" => 1 }
+ indifferent_access = { a: 1 }.with_indifferent_access
+
+ assert_equal(
+ { "a" => 1, "_aj_symbol_keys" => ["a"] },
+ ActiveJob::Arguments.serialize([symbol_key]).first
+ )
+ assert_equal(
+ { "a" => 1, "_aj_symbol_keys" => [] },
+ ActiveJob::Arguments.serialize([string_key]).first
+ )
+ assert_equal(
+ { "a" => 1, "_aj_hash_with_indifferent_access" => true },
+ ActiveJob::Arguments.serialize([indifferent_access]).first
+ )
+ end
+
+ test "deserialize a hash" do
+ symbol_key = { "a" => 1, "_aj_symbol_keys" => ["a"] }
+ string_key = { "a" => 1, "_aj_symbol_keys" => [] }
+ another_string_key = { "a" => 1 }
+ indifferent_access = { "a" => 1, "_aj_hash_with_indifferent_access" => true }
+
+ assert_equal(
+ { a: 1 },
+ ActiveJob::Arguments.deserialize([symbol_key]).first
+ )
+ assert_equal(
+ { "a" => 1 },
+ ActiveJob::Arguments.deserialize([string_key]).first
+ )
+ assert_equal(
+ { "a" => 1 },
+ ActiveJob::Arguments.deserialize([another_string_key]).first
+ )
+ assert_equal(
+ { "a" => 1 },
+ ActiveJob::Arguments.deserialize([indifferent_access]).first
+ )
+ end
+
test "should maintain hash with indifferent access" do
symbol_key = { a: 1 }
string_key = { "a" => 1 }
@@ -56,6 +102,14 @@ class ArgumentSerializationTest < ActiveSupport::TestCase
assert_instance_of ActiveSupport::HashWithIndifferentAccess, perform_round_trip([indifferent_access]).first
end
+ test "should maintain time with zone" do
+ Time.use_zone "Alaska" do
+ time_with_zone = Time.new(2002, 10, 31, 2, 2, 2).in_time_zone
+ assert_instance_of ActiveSupport::TimeWithZone, perform_round_trip([time_with_zone]).first
+ assert_arguments_unchanged time_with_zone
+ end
+ end
+
test "should disallow non-string/symbol hash keys" do
assert_raises ActiveJob::SerializationError do
ActiveJob::Arguments.serialize [ { 1 => 2 } ]
diff --git a/activejob/test/cases/exceptions_test.rb b/activejob/test/cases/exceptions_test.rb
index 22fed0a808..47d4e3c0c2 100644
--- a/activejob/test/cases/exceptions_test.rb
+++ b/activejob/test/cases/exceptions_test.rb
@@ -58,6 +58,13 @@ class ExceptionsTest < ActiveJob::TestCase
end
end
+ test "custom handling of discarded job" do
+ perform_enqueued_jobs do
+ RetryJob.perform_later "CustomDiscardableError", 2
+ assert_equal "Dealt with a job that was discarded in a custom way. Message: CustomDiscardableError", JobBuffer.last_value
+ end
+ end
+
test "custom handling of job that exceeds retry attempts" do
perform_enqueued_jobs do
RetryJob.perform_later "CustomCatchError", 6
@@ -106,4 +113,22 @@ class ExceptionsTest < ActiveJob::TestCase
end
end
end
+
+ test "successfully retry job throwing one of two retryable exceptions" do
+ perform_enqueued_jobs do
+ RetryJob.perform_later "SecondRetryableErrorOfTwo", 3
+
+ assert_equal [
+ "Raised SecondRetryableErrorOfTwo for the 1st time",
+ "Raised SecondRetryableErrorOfTwo for the 2nd time",
+ "Successfully completed job" ], JobBuffer.values
+ end
+ end
+
+ test "discard job throwing one of two discardable exceptions" do
+ perform_enqueued_jobs do
+ RetryJob.perform_later "SecondDiscardableErrorOfTwo", 2
+ assert_equal [ "Raised SecondDiscardableErrorOfTwo for the 1st time" ], JobBuffer.values
+ end
+ end
end
diff --git a/activejob/test/cases/job_serialization_test.rb b/activejob/test/cases/job_serialization_test.rb
index 440051c427..86f3651564 100644
--- a/activejob/test/cases/job_serialization_test.rb
+++ b/activejob/test/cases/job_serialization_test.rb
@@ -23,16 +23,16 @@ class JobSerializationTest < ActiveSupport::TestCase
test "serialize and deserialize are symmetric" do
# Round trip a job in memory only
- h1 = HelloJob.new
- h1.deserialize(h1.serialize)
+ h1 = HelloJob.new("Rafael")
+ h2 = HelloJob.deserialize(h1.serialize)
+ assert_equal h1.serialize, h2.serialize
# Now verify it's identical to a JSON round trip.
# We don't want any non-native JSON elements in the job hash,
# like symbols.
- payload = JSON.dump(h1.serialize)
- h2 = HelloJob.new
- h2.deserialize(JSON.load(payload))
- assert_equal h1.serialize, h2.serialize
+ payload = JSON.dump(h2.serialize)
+ h3 = HelloJob.deserialize(JSON.load(payload))
+ assert_equal h2.serialize, h3.serialize
end
test "deserialize sets locale" do
@@ -54,4 +54,11 @@ class JobSerializationTest < ActiveSupport::TestCase
job.provider_job_id = "some value set by adapter"
assert_equal job.provider_job_id, job.serialize["provider_job_id"]
end
+
+ test "serialize stores the current timezone" do
+ Time.use_zone "Hawaii" do
+ job = HelloJob.new
+ assert_equal "Hawaii", job.serialize["timezone"]
+ end
+ end
end
diff --git a/activejob/test/cases/logging_test.rb b/activejob/test/cases/logging_test.rb
index 1f8c4a5573..a1107a07fd 100644
--- a/activejob/test/cases/logging_test.rb
+++ b/activejob/test/cases/logging_test.rb
@@ -45,6 +45,14 @@ class LoggingTest < ActiveSupport::TestCase
ActiveJob::Base.logger = logger
end
+ def subscribed
+ [].tap do |events|
+ ActiveSupport::Notifications.subscribed(-> (*args) { events << args }, /enqueue.*\.active_job/) do
+ yield
+ end
+ end
+ end
+
def test_uses_active_job_as_tag
HelloJob.perform_later "Cristian"
assert_match(/\[ActiveJob\]/, @logger.messages)
@@ -86,8 +94,11 @@ class LoggingTest < ActiveSupport::TestCase
end
def test_enqueue_job_logging
- HelloJob.perform_later "Cristian"
+ events = subscribed { HelloJob.perform_later "Cristian" }
assert_match(/Enqueued HelloJob \(Job ID: .*?\) to .*?:.*Cristian/, @logger.messages)
+ assert_equal(events.count, 1)
+ key, * = events.first
+ assert_equal(key, "enqueue.active_job")
end
def test_perform_job_logging
@@ -110,15 +121,21 @@ class LoggingTest < ActiveSupport::TestCase
end
def test_enqueue_at_job_logging
- HelloJob.set(wait_until: 24.hours.from_now).perform_later "Cristian"
+ events = subscribed { HelloJob.set(wait_until: 24.hours.from_now).perform_later "Cristian" }
assert_match(/Enqueued HelloJob \(Job ID: .*\) to .*? at.*Cristian/, @logger.messages)
+ assert_equal(events.count, 1)
+ key, * = events.first
+ assert_equal(key, "enqueue_at.active_job")
rescue NotImplementedError
skip
end
def test_enqueue_in_job_logging
- HelloJob.set(wait: 2.seconds).perform_later "Cristian"
+ events = subscribed { HelloJob.set(wait: 2.seconds).perform_later "Cristian" }
assert_match(/Enqueued HelloJob \(Job ID: .*\) to .*? at.*Cristian/, @logger.messages)
+ assert_equal(events.count, 1)
+ key, * = events.first
+ assert_equal(key, "enqueue_at.active_job")
rescue NotImplementedError
skip
end
diff --git a/activejob/test/cases/serializers_test.rb b/activejob/test/cases/serializers_test.rb
new file mode 100644
index 0000000000..bee0c061bd
--- /dev/null
+++ b/activejob/test/cases/serializers_test.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+require "helper"
+require "active_job/serializers"
+
+class SerializersTest < ActiveSupport::TestCase
+ class DummyValueObject
+ attr_accessor :value
+
+ def initialize(value)
+ @value = value
+ end
+
+ def ==(other)
+ self.value == other.value
+ end
+ end
+
+ class DummySerializer < ActiveJob::Serializers::ObjectSerializer
+ def serialize(object)
+ super({ "value" => object.value })
+ end
+
+ def deserialize(hash)
+ DummyValueObject.new(hash["value"])
+ end
+
+ private
+
+ def klass
+ DummyValueObject
+ end
+ end
+
+ setup do
+ @value_object = DummyValueObject.new 123
+ @original_serializers = ActiveJob::Serializers.serializers
+ end
+
+ teardown do
+ ActiveJob::Serializers._additional_serializers = @original_serializers
+ end
+
+ test "can't serialize unknown object" do
+ assert_raises ActiveJob::SerializationError do
+ ActiveJob::Serializers.serialize @value_object
+ end
+ end
+
+ test "will serialize objects with serializers registered" do
+ ActiveJob::Serializers.add_serializers DummySerializer
+
+ assert_equal(
+ { "_aj_serialized" => "SerializersTest::DummySerializer", "value" => 123 },
+ ActiveJob::Serializers.serialize(@value_object)
+ )
+ end
+
+ test "won't deserialize unknown hash" do
+ hash = { "_dummy_serializer" => 123, "_aj_symbol_keys" => [] }
+ error = assert_raises(ArgumentError) do
+ ActiveJob::Serializers.deserialize(hash)
+ end
+ assert_equal(
+ 'Serializer name is not present in the argument: {"_dummy_serializer"=>123, "_aj_symbol_keys"=>[]}',
+ error.message
+ )
+ end
+
+ test "won't deserialize unknown serializer" do
+ hash = { "_aj_serialized" => "DoNotExist", "value" => 123 }
+ error = assert_raises(ArgumentError) do
+ ActiveJob::Serializers.deserialize(hash)
+ end
+ assert_equal(
+ "Serializer DoNotExist is not known",
+ error.message
+ )
+ end
+
+ test "will deserialize know serialized objects" do
+ ActiveJob::Serializers.add_serializers DummySerializer
+ hash = { "_aj_serialized" => "SerializersTest::DummySerializer", "value" => 123 }
+ assert_equal DummyValueObject.new(123), ActiveJob::Serializers.deserialize(hash)
+ end
+
+ test "adds new serializer" do
+ ActiveJob::Serializers.add_serializers DummySerializer
+ assert ActiveJob::Serializers.serializers.include?(DummySerializer)
+ end
+
+ test "can't add serializer with the same key twice" do
+ ActiveJob::Serializers.add_serializers DummySerializer
+ assert_no_difference(-> { ActiveJob::Serializers.serializers.size }) do
+ ActiveJob::Serializers.add_serializers DummySerializer
+ end
+ end
+end
diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb
index 66bcd8f3a0..d0a21a5da3 100644
--- a/activejob/test/cases/test_helper_test.rb
+++ b/activejob/test/cases/test_helper_test.rb
@@ -389,13 +389,91 @@ class EnqueuedJobsTest < ActiveJob::TestCase
assert_match(/`:only` and `:except`/, error.message)
end
- def test_assert_enqueued_job
+ def test_assert_no_enqueued_jobs_with_queue_option
+ assert_nothing_raised do
+ assert_no_enqueued_jobs queue: :default do
+ HelloJob.set(queue: :other_queue).perform_later
+ LoggingJob.set(queue: :other_queue).perform_later
+ end
+ end
+ end
+
+ def test_assert_no_enqueued_jobs_with_queue_option_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_enqueued_jobs queue: :other_queue do
+ HelloJob.set(queue: :other_queue).perform_later
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_no_enqueued_jobs_with_only_and_queue_option
+ assert_nothing_raised do
+ assert_no_enqueued_jobs only: HelloJob, queue: :some_queue do
+ HelloJob.set(queue: :other_queue).perform_later
+ HelloJob.set(queue: :other_queue).perform_later
+ LoggingJob.set(queue: :some_queue).perform_later
+ end
+ end
+ end
+
+ def test_assert_no_enqueued_jobs_with_only_and_queue_option_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_enqueued_jobs only: HelloJob, queue: :some_queue do
+ HelloJob.set(queue: :other_queue).perform_later
+ HelloJob.set(queue: :some_queue).perform_later
+ LoggingJob.set(queue: :some_queue).perform_later
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_no_enqueued_jobs_with_except_and_queue_option
+ assert_nothing_raised do
+ assert_no_enqueued_jobs except: LoggingJob, queue: :some_queue do
+ HelloJob.set(queue: :other_queue).perform_later
+ HelloJob.set(queue: :other_queue).perform_later
+ LoggingJob.set(queue: :some_queue).perform_later
+ end
+ end
+ end
+
+ def test_assert_no_enqueued_jobs_with_except_and_queue_option_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_enqueued_jobs except: LoggingJob, queue: :some_queue do
+ HelloJob.set(queue: :other_queue).perform_later
+ HelloJob.set(queue: :some_queue).perform_later
+ LoggingJob.set(queue: :some_queue).perform_later
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_no_enqueued_jobs_with_only_and_except_and_queue_option
+ error = assert_raise ArgumentError do
+ assert_no_enqueued_jobs only: HelloJob, except: HelloJob, queue: :some_queue do
+ HelloJob.set(queue: :other_queue).perform_later
+ end
+ end
+
+ assert_match(/`:only` and `:except`/, error.message)
+ end
+
+ def test_assert_enqueued_with
assert_enqueued_with(job: LoggingJob, queue: "default") do
LoggingJob.set(wait_until: Date.tomorrow.noon).perform_later
end
end
- def test_assert_enqueued_job_returns
+ def test_assert_enqueued_with_with_no_block
+ LoggingJob.set(wait_until: Date.tomorrow.noon).perform_later
+ assert_enqueued_with(job: LoggingJob, queue: "default")
+ end
+
+ def test_assert_enqueued_with_returns
job = assert_enqueued_with(job: LoggingJob) do
LoggingJob.set(wait_until: 5.minutes.from_now).perform_later(1, 2, 3)
end
@@ -406,13 +484,28 @@ class EnqueuedJobsTest < ActiveJob::TestCase
assert_equal [1, 2, 3], job.arguments
end
- def test_assert_enqueued_job_failure
+ def test_assert_enqueued_with_with_no_block_returns
+ LoggingJob.set(wait_until: 5.minutes.from_now).perform_later(1, 2, 3)
+ job = assert_enqueued_with(job: LoggingJob)
+
+ assert_instance_of LoggingJob, job
+ assert_in_delta 5.minutes.from_now, job.scheduled_at, 1
+ assert_equal "default", job.queue_name
+ assert_equal [1, 2, 3], job.arguments
+ end
+
+ def test_assert_enqueued_with_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
+ LoggingJob.perform_later
+ assert_enqueued_with(job: LoggingJob) {}
+ end
+
error = assert_raise ActiveSupport::TestCase::Assertion do
assert_enqueued_with(job: NestedJob, queue: "low") do
NestedJob.perform_later
@@ -422,7 +515,21 @@ class EnqueuedJobsTest < ActiveJob::TestCase
assert_equal 'No enqueued job found with {:job=>NestedJob, :queue=>"low"}', error.message
end
- def test_assert_enqueued_job_args
+ def test_assert_enqueued_with_with_no_block_failure
+ assert_raise ActiveSupport::TestCase::Assertion do
+ NestedJob.perform_later
+ assert_enqueued_with(job: LoggingJob, queue: "default")
+ end
+
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ NestedJob.perform_later
+ assert_enqueued_with(job: NestedJob, queue: "low")
+ end
+
+ assert_equal 'No enqueued job found with {:job=>NestedJob, :queue=>"low"}', error.message
+ end
+
+ def test_assert_enqueued_with_args
assert_raise ArgumentError do
assert_enqueued_with(class: LoggingJob) do
NestedJob.set(wait_until: Date.tomorrow.noon).perform_later
@@ -430,20 +537,38 @@ class EnqueuedJobsTest < ActiveJob::TestCase
end
end
- def test_assert_enqueued_job_with_at_option
+ def test_assert_enqueued_with_with_no_block_args
+ assert_raise ArgumentError do
+ NestedJob.set(wait_until: Date.tomorrow.noon).perform_later
+ assert_enqueued_with(class: LoggingJob)
+ end
+ end
+
+ def test_assert_enqueued_with_with_at_option
assert_enqueued_with(job: HelloJob, at: Date.tomorrow.noon) do
HelloJob.set(wait_until: Date.tomorrow.noon).perform_later
end
end
- def test_assert_enqueued_job_with_global_id_args
+ def test_assert_enqueued_with_with_no_block_with_at_option
+ HelloJob.set(wait_until: Date.tomorrow.noon).perform_later
+ assert_enqueued_with(job: HelloJob, at: Date.tomorrow.noon)
+ end
+
+ def test_assert_enqueued_with_with_global_id_args
ricardo = Person.new(9)
assert_enqueued_with(job: HelloJob, args: [ricardo]) do
HelloJob.perform_later(ricardo)
end
end
- def test_assert_enqueued_job_failure_with_global_id_args
+ def test_assert_enqueued_with_with_no_block_with_global_id_args
+ ricardo = Person.new(9)
+ HelloJob.perform_later(ricardo)
+ assert_enqueued_with(job: HelloJob, args: [ricardo])
+ end
+
+ def test_assert_enqueued_with_failure_with_global_id_args
ricardo = Person.new(9)
wilma = Person.new(11)
error = assert_raise ActiveSupport::TestCase::Assertion do
@@ -455,7 +580,18 @@ class EnqueuedJobsTest < ActiveJob::TestCase
assert_equal "No enqueued job found with {:job=>HelloJob, :args=>[#{wilma.inspect}]}", error.message
end
- def test_assert_enqueued_job_does_not_change_jobs_count
+ def test_assert_enqueued_with_failure_with_no_block_with_global_id_args
+ ricardo = Person.new(9)
+ wilma = Person.new(11)
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ HelloJob.perform_later(ricardo)
+ assert_enqueued_with(job: HelloJob, args: [wilma])
+ end
+
+ assert_equal "No enqueued job found with {:job=>HelloJob, :args=>[#{wilma.inspect}]}", error.message
+ end
+
+ def test_assert_enqueued_with_does_not_change_jobs_count
HelloJob.perform_later
assert_enqueued_with(job: HelloJob) do
HelloJob.perform_later
@@ -463,6 +599,14 @@ class EnqueuedJobsTest < ActiveJob::TestCase
assert_equal 2, queue_adapter.enqueued_jobs.count
end
+
+ def test_assert_enqueued_with_with_no_block_does_not_change_jobs_count
+ HelloJob.perform_later
+ HelloJob.perform_later
+ assert_enqueued_with(job: HelloJob)
+
+ assert_equal 2, queue_adapter.enqueued_jobs.count
+ end
end
class PerformedJobsTest < ActiveJob::TestCase
diff --git a/activejob/test/cases/timezones_test.rb b/activejob/test/cases/timezones_test.rb
new file mode 100644
index 0000000000..e2095b020d
--- /dev/null
+++ b/activejob/test/cases/timezones_test.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require "helper"
+require "jobs/timezone_dependent_job"
+
+class TimezonesTest < ActiveSupport::TestCase
+ setup do
+ JobBuffer.clear
+ end
+
+ test "it performs the job in the given timezone" do
+ job = TimezoneDependentJob.new("2018-01-01T00:00:00Z")
+ job.timezone = "London"
+ job.perform_now
+
+ assert_equal "Happy New Year!", JobBuffer.last_value
+
+ job = TimezoneDependentJob.new("2018-01-01T00:00:00Z")
+ job.timezone = "Eastern Time (US & Canada)"
+ job.perform_now
+
+ assert_equal "Just 5 hours to go", JobBuffer.last_value
+ end
+end
diff --git a/activejob/test/integration/queuing_test.rb b/activejob/test/integration/queuing_test.rb
index 32ef485c45..32afb5ca62 100644
--- a/activejob/test/integration/queuing_test.rb
+++ b/activejob/test/integration/queuing_test.rb
@@ -82,7 +82,7 @@ class QueuingTest < ActiveSupport::TestCase
end
test "should supply a provider_job_id when available for immediate jobs" do
- skip unless adapter_is?(:async, :delayed_job, :sidekiq, :qu, :que, :queue_classic)
+ skip unless adapter_is?(:async, :delayed_job, :sidekiq, :que, :queue_classic)
test_job = TestJob.perform_later @id
assert test_job.provider_job_id, "Provider job id should be set by provider"
end
@@ -110,6 +110,22 @@ class QueuingTest < ActiveSupport::TestCase
end
end
+ test "current timezone is kept while running perform_later" do
+ skip if adapter_is?(:inline)
+
+ begin
+ current_zone = Time.zone
+ Time.zone = "Hawaii"
+
+ TestJob.perform_later @id
+ wait_for_jobs_to_finish_for(5.seconds)
+ assert job_executed
+ assert_equal "Hawaii", job_executed_in_timezone
+ ensure
+ Time.zone = current_zone
+ end
+ end
+
test "should run job with higher priority first" do
skip unless adapter_is?(:delayed_job, :que)
diff --git a/activejob/test/jobs/retry_job.rb b/activejob/test/jobs/retry_job.rb
index 9aa99d9a21..1383fffd7d 100644
--- a/activejob/test/jobs/retry_job.rb
+++ b/activejob/test/jobs/retry_job.rb
@@ -4,21 +4,30 @@ require_relative "../support/job_buffer"
require "active_support/core_ext/integer/inflections"
class DefaultsError < StandardError; end
+class FirstRetryableErrorOfTwo < StandardError; end
+class SecondRetryableErrorOfTwo < StandardError; end
class LongWaitError < StandardError; end
class ShortWaitTenAttemptsError < StandardError; end
class ExponentialWaitTenAttemptsError < StandardError; end
class CustomWaitTenAttemptsError < StandardError; end
class CustomCatchError < StandardError; end
class DiscardableError < StandardError; end
+class FirstDiscardableErrorOfTwo < StandardError; end
+class SecondDiscardableErrorOfTwo < StandardError; end
+class CustomDiscardableError < StandardError; end
class RetryJob < ActiveJob::Base
retry_on DefaultsError
+ retry_on FirstRetryableErrorOfTwo, SecondRetryableErrorOfTwo
retry_on LongWaitError, wait: 1.hour, attempts: 10
retry_on ShortWaitTenAttemptsError, wait: 1.second, attempts: 10
retry_on ExponentialWaitTenAttemptsError, wait: :exponentially_longer, attempts: 10
retry_on CustomWaitTenAttemptsError, wait: ->(executions) { executions * 2 }, attempts: 10
- retry_on(CustomCatchError) { |job, exception| JobBuffer.add("Dealt with a job that failed to retry in a custom way after #{job.arguments.second} attempts. Message: #{exception.message}") }
+ retry_on(CustomCatchError) { |job, error| JobBuffer.add("Dealt with a job that failed to retry in a custom way after #{job.arguments.second} attempts. Message: #{error.message}") }
+
discard_on DiscardableError
+ discard_on FirstDiscardableErrorOfTwo, SecondDiscardableErrorOfTwo
+ discard_on(CustomDiscardableError) { |job, error| JobBuffer.add("Dealt with a job that was discarded in a custom way. Message: #{error.message}") }
def perform(raising, attempts)
if executions < attempts
diff --git a/activejob/test/jobs/timezone_dependent_job.rb b/activejob/test/jobs/timezone_dependent_job.rb
new file mode 100644
index 0000000000..41f473d533
--- /dev/null
+++ b/activejob/test/jobs/timezone_dependent_job.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require_relative "../support/job_buffer"
+
+class TimezoneDependentJob < ActiveJob::Base
+ def perform(now)
+ now = now.in_time_zone
+ new_year = localtime(2018, 1, 1)
+
+ if now >= new_year
+ JobBuffer.add("Happy New Year!")
+ else
+ JobBuffer.add("Just #{(new_year - now).div(3600)} hours to go")
+ end
+ end
+
+ private
+
+ def localtime(*args)
+ Time.zone ? Time.zone.local(*args) : Time.utc(*args)
+ end
+end
diff --git a/activejob/test/support/integration/adapters/qu.rb b/activejob/test/support/integration/adapters/qu.rb
deleted file mode 100644
index 67db03e279..0000000000
--- a/activejob/test/support/integration/adapters/qu.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-module QuJobsManager
- def setup
- require "qu-rails"
- require "qu-redis"
- ActiveJob::Base.queue_adapter = :qu
- ENV["REDISTOGO_URL"] = "redis://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
- return false
- end
- true
- end
-end
diff --git a/activejob/test/support/integration/dummy_app_template.rb b/activejob/test/support/integration/dummy_app_template.rb
index 7ea78c3350..b56dd3e591 100644
--- a/activejob/test/support/integration/dummy_app_template.rb
+++ b/activejob/test/support/integration/dummy_app_template.rb
@@ -21,6 +21,7 @@ class TestJob < ActiveJob::Base
File.open(Rails.root.join("tmp/\#{x}.new"), "wb+") do |f|
f.write Marshal.dump({
"locale" => I18n.locale.to_s || "en",
+ "timezone" => Time.zone.try(:name) || "UTC",
"executed_at" => Time.now.to_r
})
end
diff --git a/activejob/test/support/integration/test_case_helpers.rb b/activejob/test/support/integration/test_case_helpers.rb
index f02a32a38e..3d9b265b66 100644
--- a/activejob/test/support/integration/test_case_helpers.rb
+++ b/activejob/test/support/integration/test_case_helpers.rb
@@ -62,4 +62,8 @@ module TestCaseHelpers
def job_executed_in_locale(id = @id)
job_data(id)["locale"]
end
+
+ def job_executed_in_timezone(id = @id)
+ job_data(id)["timezone"]
+ end
end
diff --git a/activejob/test/support/queue_classic/inline.rb b/activejob/test/support/queue_classic/inline.rb
index ca3cd4581b..0695a34c27 100644
--- a/activejob/test/support/queue_classic/inline.rb
+++ b/activejob/test/support/queue_classic/inline.rb
@@ -1,22 +1,23 @@
# frozen_string_literal: true
require "queue_classic"
+require "active_support/core_ext/module/redefine_method"
module QC
class Queue
- def enqueue(method, *args)
+ redefine_method(:enqueue) do |method, *args|
receiver_str, _, message = method.rpartition(".")
receiver = eval(receiver_str)
receiver.send(message, *args)
end
- def enqueue_in(seconds, method, *args)
+ redefine_method(:enqueue_in) do |seconds, method, *args|
receiver_str, _, message = method.rpartition(".")
receiver = eval(receiver_str)
receiver.send(message, *args)
end
- def enqueue_at(not_before, method, *args)
+ redefine_method(:enqueue_at) do |not_before, method, *args|
receiver_str, _, message = method.rpartition(".")
receiver = eval(receiver_str)
receiver.send(message, *args)
diff --git a/activejob/test/support/sneakers/inline.rb b/activejob/test/support/sneakers/inline.rb
index 92b69ee3bc..e772c68c6e 100644
--- a/activejob/test/support/sneakers/inline.rb
+++ b/activejob/test/support/sneakers/inline.rb
@@ -1,11 +1,12 @@
# frozen_string_literal: true
require "sneakers"
+require "active_support/core_ext/module/redefine_method"
module Sneakers
module Worker
module ClassMethods
- def enqueue(msg)
+ redefine_method(:enqueue) do |msg|
worker = new(nil, nil, {})
worker.work(*msg)
end