activejob 7.0.8 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -214
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +2 -2
  5. data/lib/active_job/arguments.rb +18 -32
  6. data/lib/active_job/base.rb +1 -1
  7. data/lib/active_job/callbacks.rb +3 -8
  8. data/lib/active_job/configured_job.rb +4 -0
  9. data/lib/active_job/core.rb +11 -6
  10. data/lib/active_job/deprecator.rb +7 -0
  11. data/lib/active_job/enqueue_after_transaction_commit.rb +28 -0
  12. data/lib/active_job/enqueuing.rb +71 -12
  13. data/lib/active_job/exceptions.rb +44 -7
  14. data/lib/active_job/execution.rb +5 -2
  15. data/lib/active_job/gem_version.rb +3 -3
  16. data/lib/active_job/instrumentation.rb +18 -10
  17. data/lib/active_job/log_subscriber.rb +80 -8
  18. data/lib/active_job/logging.rb +16 -2
  19. data/lib/active_job/queue_adapter.rb +18 -6
  20. data/lib/active_job/queue_adapters/abstract_adapter.rb +27 -0
  21. data/lib/active_job/queue_adapters/async_adapter.rb +3 -3
  22. data/lib/active_job/queue_adapters/backburner_adapter.rb +8 -4
  23. data/lib/active_job/queue_adapters/delayed_job_adapter.rb +10 -2
  24. data/lib/active_job/queue_adapters/inline_adapter.rb +6 -2
  25. data/lib/active_job/queue_adapters/queue_classic_adapter.rb +13 -5
  26. data/lib/active_job/queue_adapters/resque_adapter.rb +2 -2
  27. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +43 -15
  28. data/lib/active_job/queue_adapters/sneakers_adapter.rb +2 -2
  29. data/lib/active_job/queue_adapters/sucker_punch_adapter.rb +4 -4
  30. data/lib/active_job/queue_adapters/test_adapter.rb +13 -5
  31. data/lib/active_job/queue_adapters.rb +9 -7
  32. data/lib/active_job/queue_priority.rb +18 -1
  33. data/lib/active_job/railtie.rb +38 -7
  34. data/lib/active_job/serializers/big_decimal_serializer.rb +22 -0
  35. data/lib/active_job/serializers/duration_serializer.rb +4 -2
  36. data/lib/active_job/serializers/object_serializer.rb +2 -0
  37. data/lib/active_job/serializers/time_with_zone_serializer.rb +11 -2
  38. data/lib/active_job/serializers.rb +7 -3
  39. data/lib/active_job/test_helper.rb +60 -19
  40. data/lib/active_job/version.rb +1 -1
  41. data/lib/active_job.rb +34 -4
  42. data/lib/rails/generators/job/USAGE +19 -0
  43. data/lib/rails/generators/job/job_generator.rb +6 -2
  44. data/lib/rails/generators/job/templates/job.rb.tt +1 -1
  45. metadata +14 -10
  46. data/lib/active_job/queue_adapters/que_adapter.rb +0 -61
@@ -1,14 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ gem "sidekiq", ">= 4.1.0"
3
4
  require "sidekiq"
4
5
 
5
6
  module ActiveJob
6
7
  module QueueAdapters
7
- # == Sidekiq adapter for Active Job
8
+ # = Sidekiq adapter for Active Job
8
9
  #
9
10
  # Simple, efficient background processing for Ruby. Sidekiq uses threads to
10
11
  # handle many jobs at the same time in the same process. It does not
11
- # require Rails but will integrate tightly with it to make background
12
+ # require \Rails but will integrate tightly with it to make background
12
13
  # processing dead simple.
13
14
  #
14
15
  # Read more about Sidekiq {here}[http://sidekiq.org].
@@ -16,23 +17,50 @@ module ActiveJob
16
17
  # To use Sidekiq set the queue_adapter config to +:sidekiq+.
17
18
  #
18
19
  # Rails.application.config.active_job.queue_adapter = :sidekiq
19
- class SidekiqAdapter
20
+ class SidekiqAdapter < AbstractAdapter
20
21
  def enqueue(job) # :nodoc:
21
- # Sidekiq::Client does not support symbols as keys
22
- job.provider_job_id = Sidekiq::Client.push \
23
- "class" => JobWrapper,
24
- "wrapped" => job.class,
25
- "queue" => job.queue_name,
26
- "args" => [ job.serialize ]
22
+ job.provider_job_id = JobWrapper.set(
23
+ wrapped: job.class,
24
+ queue: job.queue_name
25
+ ).perform_async(job.serialize)
27
26
  end
28
27
 
29
28
  def enqueue_at(job, timestamp) # :nodoc:
30
- job.provider_job_id = Sidekiq::Client.push \
31
- "class" => JobWrapper,
32
- "wrapped" => job.class,
33
- "queue" => job.queue_name,
34
- "args" => [ job.serialize ],
35
- "at" => timestamp
29
+ job.provider_job_id = JobWrapper.set(
30
+ wrapped: job.class,
31
+ queue: job.queue_name,
32
+ ).perform_at(timestamp, job.serialize)
33
+ end
34
+
35
+ def enqueue_all(jobs) # :nodoc:
36
+ enqueued_count = 0
37
+ jobs.group_by(&:class).each do |job_class, same_class_jobs|
38
+ same_class_jobs.group_by(&:queue_name).each do |queue, same_class_and_queue_jobs|
39
+ immediate_jobs, scheduled_jobs = same_class_and_queue_jobs.partition { |job| job.scheduled_at.nil? }
40
+
41
+ if immediate_jobs.any?
42
+ jids = Sidekiq::Client.push_bulk(
43
+ "class" => JobWrapper,
44
+ "wrapped" => job_class,
45
+ "queue" => queue,
46
+ "args" => immediate_jobs.map { |job| [job.serialize] },
47
+ )
48
+ enqueued_count += jids.compact.size
49
+ end
50
+
51
+ if scheduled_jobs.any?
52
+ jids = Sidekiq::Client.push_bulk(
53
+ "class" => JobWrapper,
54
+ "wrapped" => job_class,
55
+ "queue" => queue,
56
+ "args" => scheduled_jobs.map { |job| [job.serialize] },
57
+ "at" => scheduled_jobs.map { |job| job.scheduled_at&.to_f }
58
+ )
59
+ enqueued_count += jids.compact.size
60
+ end
61
+ end
62
+ end
63
+ enqueued_count
36
64
  end
37
65
 
38
66
  class JobWrapper # :nodoc:
@@ -5,7 +5,7 @@ require "monitor"
5
5
 
6
6
  module ActiveJob
7
7
  module QueueAdapters
8
- # == Sneakers adapter for Active Job
8
+ # = Sneakers adapter for Active Job
9
9
  #
10
10
  # A high-performance RabbitMQ background processing framework for Ruby.
11
11
  # Sneakers is being used in production for both I/O and CPU intensive
@@ -17,7 +17,7 @@ module ActiveJob
17
17
  # To use Sneakers set the queue_adapter config to +:sneakers+.
18
18
  #
19
19
  # Rails.application.config.active_job.queue_adapter = :sneakers
20
- class SneakersAdapter
20
+ class SneakersAdapter < AbstractAdapter
21
21
  def initialize
22
22
  @monitor = Monitor.new
23
23
  end
@@ -4,20 +4,20 @@ require "sucker_punch"
4
4
 
5
5
  module ActiveJob
6
6
  module QueueAdapters
7
- # == Sucker Punch adapter for Active Job
7
+ # = Sucker Punch adapter for Active Job
8
8
  #
9
9
  # Sucker Punch is a single-process Ruby asynchronous processing library.
10
10
  # This reduces the cost of hosting on a service like Heroku along
11
11
  # with the memory footprint of having to maintain additional jobs if
12
12
  # hosting on a dedicated server. All queues can run within a
13
- # single application (e.g. Rails, Sinatra, etc.) process.
13
+ # single application (e.g. \Rails, Sinatra, etc.) process.
14
14
  #
15
15
  # Read more about Sucker Punch {here}[https://github.com/brandonhilkert/sucker_punch].
16
16
  #
17
17
  # To use Sucker Punch set the queue_adapter config to +:sucker_punch+.
18
18
  #
19
19
  # Rails.application.config.active_job.queue_adapter = :sucker_punch
20
- class SuckerPunchAdapter
20
+ class SuckerPunchAdapter < AbstractAdapter
21
21
  def enqueue(job) # :nodoc:
22
22
  if JobWrapper.respond_to?(:perform_async)
23
23
  # sucker_punch 2.0 API
@@ -33,7 +33,7 @@ module ActiveJob
33
33
  delay = timestamp - Time.current.to_f
34
34
  JobWrapper.perform_in delay, job.serialize
35
35
  else
36
- raise NotImplementedError, "sucker_punch 1.0 does not support `enqueued_at`. Please upgrade to version ~> 2.0.0 to enable this behavior."
36
+ raise NotImplementedError, "sucker_punch 1.0 does not support `enqueue_at`. Please upgrade to version ~> 2.0.0 to enable this behavior."
37
37
  end
38
38
  end
39
39
 
@@ -2,19 +2,27 @@
2
2
 
3
3
  module ActiveJob
4
4
  module QueueAdapters
5
- # == Test adapter for Active Job
5
+ # = Test adapter for Active Job
6
6
  #
7
7
  # The test adapter should be used only in testing. Along with
8
8
  # ActiveJob::TestCase and ActiveJob::TestHelper
9
- # it makes a great tool to test your Rails application.
9
+ # it makes a great tool to test your \Rails application.
10
10
  #
11
11
  # To use the test adapter set +queue_adapter+ config to +:test+.
12
12
  #
13
13
  # Rails.application.config.active_job.queue_adapter = :test
14
- class TestAdapter
15
- attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue, :at)
14
+ class TestAdapter < AbstractAdapter
15
+ attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue, :at, :enqueue_after_transaction_commit)
16
16
  attr_writer(:enqueued_jobs, :performed_jobs)
17
17
 
18
+ def initialize(enqueue_after_transaction_commit: true)
19
+ @enqueue_after_transaction_commit = enqueue_after_transaction_commit
20
+ end
21
+
22
+ def enqueue_after_transaction_commit? # :nodoc:
23
+ @enqueue_after_transaction_commit
24
+ end
25
+
18
26
  # Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
19
27
  def enqueued_jobs
20
28
  @enqueued_jobs ||= []
@@ -59,7 +67,7 @@ module ActiveJob
59
67
  end
60
68
 
61
69
  def filtered_time?(job)
62
- job.scheduled_at > at.to_f if at && job.scheduled_at
70
+ job.scheduled_at > at if at && job.scheduled_at
63
71
  end
64
72
 
65
73
  def filtered_queue?(job)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveJob
4
- # == Active Job adapters
4
+ # = Active Job adapters
5
5
  #
6
6
  # Active Job has adapters for the following queuing backends:
7
7
  #
@@ -13,10 +13,14 @@ module ActiveJob
13
13
  # * {Sidekiq}[https://sidekiq.org]
14
14
  # * {Sneakers}[https://github.com/jondot/sneakers]
15
15
  # * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch]
16
- # * {Active Job Async Job}[https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html]
17
- # * {Active Job Inline}[https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html]
18
16
  # * Please Note: We are not accepting pull requests for new adapters. See the {README}[link:files/activejob/README_md.html] for more details.
19
17
  #
18
+ # For testing and development Active Job has three built-in adapters:
19
+ #
20
+ # * {Active Job Async}[https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html]
21
+ # * {Active Job Inline}[https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html]
22
+ # * {Active Job Test}[https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/TestAdapter.html]
23
+ #
20
24
  # === Backends Features
21
25
  #
22
26
  # | | Async | Queues | Delayed | Priorities | Timeout | Retries |
@@ -31,6 +35,7 @@ module ActiveJob
31
35
  # | Sucker Punch | Yes | Yes | Yes | No | No | No |
32
36
  # | Active Job Async | Yes | Yes | Yes | No | No | No |
33
37
  # | Active Job Inline | No | Yes | N/A | N/A | N/A | N/A |
38
+ # | Active Job Test | No | Yes | N/A | N/A | N/A | N/A |
34
39
  #
35
40
  # ==== Async
36
41
  #
@@ -106,13 +111,10 @@ module ActiveJob
106
111
  # N/A: The adapter does not run in a separate process, and therefore doesn't
107
112
  # support retries.
108
113
  #
109
- # === Async and Inline Queue Adapters
110
- #
111
- # Active Job has two built-in queue adapters intended for development and
112
- # testing: +:async+ and +:inline+.
113
114
  module QueueAdapters
114
115
  extend ActiveSupport::Autoload
115
116
 
117
+ autoload :AbstractAdapter
116
118
  autoload :AsyncAdapter
117
119
  autoload :InlineAdapter
118
120
  autoload :BackburnerAdapter
@@ -18,7 +18,24 @@ module ActiveJob
18
18
  # end
19
19
  # end
20
20
  #
21
- # Specify either an argument or a block.
21
+ # Can be given a block that will evaluate in the context of the job
22
+ # so that a dynamic priority can be applied:
23
+ #
24
+ # class PublishToFeedJob < ApplicationJob
25
+ # queue_with_priority do
26
+ # post = self.arguments.first
27
+ #
28
+ # if post.paid?
29
+ # 10
30
+ # else
31
+ # 50
32
+ # end
33
+ # end
34
+ #
35
+ # def perform(post)
36
+ # post.to_feed!
37
+ # end
38
+ # end
22
39
  def queue_with_priority(priority = nil, &block)
23
40
  if block_given?
24
41
  self.priority = block
@@ -10,6 +10,10 @@ module ActiveJob
10
10
  config.active_job.custom_serializers = []
11
11
  config.active_job.log_query_tags_around_perform = true
12
12
 
13
+ initializer "active_job.deprecator", before: :load_environment_config do |app|
14
+ app.deprecators[:active_job] = ActiveJob.deprecator
15
+ end
16
+
13
17
  initializer "active_job.logger" do
14
18
  ActiveSupport.on_load(:active_job) { self.logger = ::Rails.logger }
15
19
  end
@@ -21,9 +25,30 @@ module ActiveJob
21
25
  end
22
26
  end
23
27
 
28
+ initializer "active_job.enqueue_after_transaction_commit" do |app|
29
+ ActiveSupport.on_load(:active_job) do
30
+ ActiveSupport.on_load(:active_record) do
31
+ ActiveJob::Base.include EnqueueAfterTransactionCommit
32
+
33
+ if app.config.active_job.key?(:enqueue_after_transaction_commit)
34
+ ActiveJob::Base.enqueue_after_transaction_commit = app.config.active_job.delete(:enqueue_after_transaction_commit)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
24
40
  initializer "active_job.set_configs" do |app|
25
41
  options = app.config.active_job
26
- options.queue_adapter ||= :async
42
+ options.queue_adapter ||= (Rails.env.test? ? :test : :async)
43
+
44
+ config.after_initialize do
45
+ options.each do |k, v|
46
+ k = "#{k}="
47
+ if ActiveJob.respond_to?(k)
48
+ ActiveJob.send(k, v)
49
+ end
50
+ end
51
+ end
27
52
 
28
53
  ActiveSupport.on_load(:active_job) do
29
54
  # Configs used in other initializers
@@ -32,19 +57,19 @@ module ActiveJob
32
57
  :custom_serializers
33
58
  )
34
59
 
35
- options.each do |k, v|
60
+ options.each do |k, v|
36
61
  k = "#{k}="
37
- send(k, v) if respond_to? k
62
+ if ActiveJob.respond_to?(k)
63
+ ActiveJob.send(k, v)
64
+ elsif respond_to? k
65
+ send(k, v)
66
+ end
38
67
  end
39
68
  end
40
69
 
41
70
  ActiveSupport.on_load(:action_dispatch_integration_test) do
42
71
  include ActiveJob::TestHelper
43
72
  end
44
-
45
- ActiveSupport.on_load(:active_record) do
46
- self.destroy_association_async_job = ActiveRecord::DestroyAssociationAsyncJob
47
- end
48
73
  end
49
74
 
50
75
  initializer "active_job.set_reloader_hook" do |app|
@@ -70,5 +95,11 @@ module ActiveJob
70
95
  end
71
96
  end
72
97
  end
98
+
99
+ initializer "active_job.backtrace_cleaner" do
100
+ ActiveSupport.on_load(:active_job) do
101
+ LogSubscriber.backtrace_cleaner = ::Rails.backtrace_cleaner
102
+ end
103
+ end
73
104
  end
74
105
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal"
4
+
5
+ module ActiveJob
6
+ module Serializers
7
+ class BigDecimalSerializer < ObjectSerializer # :nodoc:
8
+ def serialize(big_decimal)
9
+ super("value" => big_decimal.to_s)
10
+ end
11
+
12
+ def deserialize(hash)
13
+ BigDecimal(hash["value"])
14
+ end
15
+
16
+ private
17
+ def klass
18
+ BigDecimal
19
+ end
20
+ end
21
+ end
22
+ end
@@ -4,14 +4,16 @@ module ActiveJob
4
4
  module Serializers
5
5
  class DurationSerializer < ObjectSerializer # :nodoc:
6
6
  def serialize(duration)
7
+ # Ideally duration.parts would be wrapped in an array before passing to Arguments.serialize,
8
+ # but we continue passing the bare hash for backwards compatibility:
7
9
  super("value" => duration.value, "parts" => Arguments.serialize(duration.parts))
8
10
  end
9
11
 
10
12
  def deserialize(hash)
11
13
  value = hash["value"]
12
14
  parts = Arguments.deserialize(hash["parts"])
13
-
14
- klass.new(value, parts)
15
+ # `parts` is originally a hash, but will have been flattened to an array by Arguments.serialize
16
+ klass.new(value, parts.to_h)
15
17
  end
16
18
 
17
19
  private
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "singleton"
4
+
3
5
  module ActiveJob
4
6
  module Serializers
5
7
  # Base class for serializing and deserializing custom objects.
@@ -2,9 +2,18 @@
2
2
 
3
3
  module ActiveJob
4
4
  module Serializers
5
- class TimeWithZoneSerializer < TimeObjectSerializer # :nodoc:
5
+ class TimeWithZoneSerializer < ObjectSerializer # :nodoc:
6
+ NANO_PRECISION = 9
7
+
8
+ def serialize(time_with_zone)
9
+ super(
10
+ "value" => time_with_zone.iso8601(NANO_PRECISION),
11
+ "time_zone" => time_with_zone.time_zone.tzinfo.name
12
+ )
13
+ end
14
+
6
15
  def deserialize(hash)
7
- Time.iso8601(hash["value"]).in_time_zone
16
+ Time.iso8601(hash["value"]).in_time_zone(hash["time_zone"] || Time.zone)
8
17
  end
9
18
 
10
19
  private
@@ -3,7 +3,9 @@
3
3
  require "set"
4
4
 
5
5
  module ActiveJob
6
- # The <tt>ActiveJob::Serializers</tt> module is used to store a list of known serializers
6
+ # = Active Job \Serializers
7
+ #
8
+ # The +ActiveJob::Serializers+ module is used to store a list of known serializers
7
9
  # and to add new ones. It also has helpers to serialize/deserialize objects.
8
10
  module Serializers # :nodoc:
9
11
  extend ActiveSupport::Autoload
@@ -18,6 +20,7 @@ module ActiveJob
18
20
  autoload :TimeSerializer
19
21
  autoload :ModuleSerializer
20
22
  autoload :RangeSerializer
23
+ autoload :BigDecimalSerializer
21
24
 
22
25
  mattr_accessor :_additional_serializers
23
26
  self._additional_serializers = Set.new
@@ -25,7 +28,7 @@ module ActiveJob
25
28
  class << self
26
29
  # Returns serialized representative of the passed object.
27
30
  # Will look up through all known serializers.
28
- # Raises <tt>ActiveJob::SerializationError</tt> if it can't find a proper serializer.
31
+ # Raises ActiveJob::SerializationError if it can't find a proper serializer.
29
32
  def serialize(argument)
30
33
  serializer = serializers.detect { |s| s.serialize?(argument) }
31
34
  raise SerializationError.new("Unsupported argument type: #{argument.class.name}") unless serializer
@@ -63,6 +66,7 @@ module ActiveJob
63
66
  TimeWithZoneSerializer,
64
67
  TimeSerializer,
65
68
  ModuleSerializer,
66
- RangeSerializer
69
+ RangeSerializer,
70
+ BigDecimalSerializer
67
71
  end
68
72
  end
@@ -34,13 +34,18 @@ module ActiveJob
34
34
  end
35
35
  end
36
36
 
37
- ActiveJob::Base.include(TestQueueAdapter)
37
+ ActiveSupport.on_load(:active_job) do
38
+ ActiveJob::Base.include(TestQueueAdapter)
39
+ end
38
40
 
39
41
  def before_setup # :nodoc:
40
- test_adapter = queue_adapter_for_test
41
-
42
+ queue_adapter_specific_to_this_test_class = queue_adapter_for_test
42
43
  queue_adapter_changed_jobs.each do |klass|
43
- klass.enable_test_adapter(test_adapter)
44
+ if queue_adapter_specific_to_this_test_class
45
+ klass.enable_test_adapter(queue_adapter_specific_to_this_test_class)
46
+ elsif klass._queue_adapter.nil?
47
+ klass.enable_test_adapter(ActiveJob::QueueAdapters::TestAdapter.new)
48
+ end
44
49
  end
45
50
 
46
51
  clear_enqueued_jobs
@@ -54,17 +59,11 @@ module ActiveJob
54
59
  queue_adapter_changed_jobs.each { |klass| klass.disable_test_adapter }
55
60
  end
56
61
 
57
- # Specifies the queue adapter to use with all Active Job test helpers.
58
- #
59
- # Returns an instance of the queue adapter and defaults to
60
- # ActiveJob::QueueAdapters::TestAdapter.
61
- #
62
- # Note: The adapter provided by this method must provide some additional
63
- # methods from those expected of a standard ActiveJob::QueueAdapter
64
- # in order to be used with the active job test helpers. Refer to
65
- # ActiveJob::QueueAdapters::TestAdapter.
62
+ # Returns a queue adapter instance to use with all Active Job test helpers.
63
+ # By default, returns an instance of ActiveJob::QueueAdapters::TestAdapter.
64
+ # Override this method to specify a different adapter. The adapter must
65
+ # implement the same interface as ActiveJob::QueueAdapters::TestAdapter.
66
66
  def queue_adapter_for_test
67
- ActiveJob::QueueAdapters::TestAdapter.new
68
67
  end
69
68
 
70
69
  # Asserts that the number of enqueued jobs matches the given number.
@@ -121,6 +120,8 @@ module ActiveJob
121
120
  # end
122
121
  # end
123
122
  def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil, &block)
123
+ require_active_job_test_adapter!("assert_enqueued_jobs")
124
+
124
125
  if block_given?
125
126
  original_jobs = enqueued_jobs_with(only: only, except: except, queue: queue)
126
127
 
@@ -183,6 +184,8 @@ module ActiveJob
183
184
  #
184
185
  # assert_enqueued_jobs 0, &block
185
186
  def assert_no_enqueued_jobs(only: nil, except: nil, queue: nil, &block)
187
+ require_active_job_test_adapter!("assert_no_enqueued_jobs")
188
+
186
189
  assert_enqueued_jobs 0, only: only, except: except, queue: queue, &block
187
190
  end
188
191
 
@@ -273,6 +276,8 @@ module ActiveJob
273
276
  # end
274
277
  # end
275
278
  def assert_performed_jobs(number, only: nil, except: nil, queue: nil, &block)
279
+ require_active_job_test_adapter!("assert_performed_jobs")
280
+
276
281
  if block_given?
277
282
  original_count = performed_jobs.size
278
283
 
@@ -341,6 +346,8 @@ module ActiveJob
341
346
  #
342
347
  # assert_performed_jobs 0, &block
343
348
  def assert_no_performed_jobs(only: nil, except: nil, queue: nil, &block)
349
+ require_active_job_test_adapter!("assert_no_performed_jobs")
350
+
344
351
  assert_performed_jobs 0, only: only, except: except, queue: queue, &block
345
352
  end
346
353
 
@@ -354,6 +361,13 @@ module ActiveJob
354
361
  # assert_enqueued_with(at: Date.tomorrow.noon, queue: "my_queue")
355
362
  # end
356
363
  #
364
+ # For keyword arguments, specify them as a hash inside an array:
365
+ #
366
+ # def test_assert_enqueued_with_keyword_arguments
367
+ # MyJob.perform_later(arg1: 'value1', arg2: 'value2')
368
+ # assert_enqueued_with(job: MyJob, args: [{ arg1: 'value1', arg2: 'value2' }])
369
+ # end
370
+ #
357
371
  # The given arguments may also be specified as matcher procs that return a
358
372
  # boolean value indicating whether a job's attribute meets certain criteria.
359
373
  #
@@ -390,6 +404,8 @@ module ActiveJob
390
404
  # end
391
405
  # end
392
406
  def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil, priority: nil, &block)
407
+ require_active_job_test_adapter!("assert_enqueued_with")
408
+
393
409
  expected = { job: job, args: args, at: at, queue: queue, priority: priority }.compact
394
410
  expected_args = prepare_args_for_assertion(expected)
395
411
  potential_matches = []
@@ -492,6 +508,8 @@ module ActiveJob
492
508
  # end
493
509
  # end
494
510
  def assert_performed_with(job: nil, args: nil, at: nil, queue: nil, priority: nil, &block)
511
+ require_active_job_test_adapter!("assert_performed_with")
512
+
495
513
  expected = { job: job, args: args, at: at, queue: queue, priority: priority }.compact
496
514
  expected_args = prepare_args_for_assertion(expected)
497
515
  potential_matches = []
@@ -593,10 +611,19 @@ module ActiveJob
593
611
  # assert_performed_jobs 1
594
612
  # end
595
613
  #
596
- # If the +:at+ option is specified, then only run jobs enqueued to run
597
- # immediately or before the given time
614
+ # If the +:at+ option is specified, then only jobs that have been enqueued
615
+ # to run at or before the given time will be performed. This includes jobs
616
+ # that have been enqueued without a time.
617
+ #
618
+ # If queue_adapter_for_test is overridden to return a different adapter,
619
+ # +perform_enqueued_jobs+ will merely execute the block.
598
620
  def perform_enqueued_jobs(only: nil, except: nil, queue: nil, at: nil, &block)
599
- return flush_enqueued_jobs(only: only, except: except, queue: queue, at: at) unless block_given?
621
+ unless block_given?
622
+ require_active_job_test_adapter!("perform_enqueued_jobs (without a block)")
623
+ return flush_enqueued_jobs(only: only, except: except, queue: queue, at: at)
624
+ end
625
+
626
+ return _assert_nothing_raised_or_warn("perform_enqueued_jobs", &block) unless using_test_adapter?
600
627
 
601
628
  validate_option(only: only, except: except)
602
629
 
@@ -636,12 +663,22 @@ module ActiveJob
636
663
  end
637
664
 
638
665
  private
666
+ def require_active_job_test_adapter!(method)
667
+ unless using_test_adapter?
668
+ raise ArgumentError.new("#{method} requires the Active Job test adapter, you're using #{queue_adapter.class.name}.")
669
+ end
670
+ end
671
+
672
+ def using_test_adapter?
673
+ queue_adapter.is_a?(ActiveJob::QueueAdapters::TestAdapter)
674
+ end
675
+
639
676
  def clear_enqueued_jobs
640
- enqueued_jobs.clear
677
+ enqueued_jobs.clear if using_test_adapter?
641
678
  end
642
679
 
643
680
  def clear_performed_jobs
644
- performed_jobs.clear
681
+ performed_jobs.clear if using_test_adapter?
645
682
  end
646
683
 
647
684
  def jobs_with(jobs, only: nil, except: nil, queue: nil, at: nil)
@@ -694,6 +731,10 @@ module ActiveJob
694
731
 
695
732
  def prepare_args_for_assertion(args)
696
733
  args.dup.tap do |arguments|
734
+ if arguments[:queue].is_a?(Symbol)
735
+ arguments[:queue] = arguments[:queue].to_s
736
+ end
737
+
697
738
  if arguments[:at].acts_like?(:time)
698
739
  at_range = arguments[:at] - 1..arguments[:at] + 1
699
740
  arguments[:at] = ->(at) { at_range.cover?(at) }
@@ -3,7 +3,7 @@
3
3
  require_relative "gem_version"
4
4
 
5
5
  module ActiveJob
6
- # Returns the currently loaded version of Active Job as a <tt>Gem::Version</tt>.
6
+ # Returns the currently loaded version of Active Job as a +Gem::Version+.
7
7
  def self.version
8
8
  gem_version
9
9
  end
data/lib/active_job.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2014-2022 David Heinemeier Hansson
4
+ # Copyright (c) David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -26,16 +26,46 @@
26
26
  require "active_support"
27
27
  require "active_support/rails"
28
28
  require "active_job/version"
29
+ require "active_job/deprecator"
29
30
  require "global_id"
30
31
 
32
+ # :markup: markdown
33
+ # :include: ../README.md
31
34
  module ActiveJob
32
35
  extend ActiveSupport::Autoload
33
36
 
34
37
  autoload :Base
35
38
  autoload :QueueAdapters
36
- autoload :Serializers
37
- autoload :ConfiguredJob
39
+ autoload :Arguments
40
+ autoload :DeserializationError, "active_job/arguments"
41
+ autoload :SerializationError, "active_job/arguments"
42
+ autoload :EnqueueAfterTransactionCommit
43
+
44
+ eager_autoload do
45
+ autoload :Serializers
46
+ autoload :ConfiguredJob
47
+ end
48
+
38
49
  autoload :TestCase
39
50
  autoload :TestHelper
40
- autoload :QueryTags
51
+
52
+ def self.use_big_decimal_serializer
53
+ ActiveJob.deprecator.warn <<-WARNING.squish
54
+ Rails.application.config.active_job.use_big_decimal_serializer is deprecated and will be removed in Rails 8.0.
55
+ WARNING
56
+ end
57
+
58
+ def self.use_big_decimal_serializer=(value)
59
+ ActiveJob.deprecator.warn <<-WARNING.squish
60
+ Rails.application.config.active_job.use_big_decimal_serializer is deprecated and will be removed in Rails 8.0.
61
+ WARNING
62
+ end
63
+
64
+ ##
65
+ # :singleton-method: verbose_enqueue_logs
66
+ #
67
+ # Specifies if the methods calling background job enqueue should be logged below
68
+ # their relevant enqueue log lines. Defaults to false.
69
+ singleton_class.attr_accessor :verbose_enqueue_logs
70
+ self.verbose_enqueue_logs = false
41
71
  end