activejob 7.0.8.4 → 7.1.0.beta1

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +109 -167
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +2 -2
  5. data/lib/active_job/arguments.rb +14 -25
  6. data/lib/active_job/base.rb +1 -1
  7. data/lib/active_job/callbacks.rb +1 -4
  8. data/lib/active_job/configured_job.rb +4 -0
  9. data/lib/active_job/core.rb +3 -1
  10. data/lib/active_job/deprecator.rb +7 -0
  11. data/lib/active_job/enqueuing.rb +30 -0
  12. data/lib/active_job/exceptions.rb +30 -0
  13. data/lib/active_job/execution.rb +5 -1
  14. data/lib/active_job/gem_version.rb +4 -4
  15. data/lib/active_job/instrumentation.rb +18 -10
  16. data/lib/active_job/log_subscriber.rb +77 -7
  17. data/lib/active_job/queue_adapter.rb +13 -2
  18. data/lib/active_job/queue_adapters/async_adapter.rb +2 -2
  19. data/lib/active_job/queue_adapters/backburner_adapter.rb +7 -3
  20. data/lib/active_job/queue_adapters/delayed_job_adapter.rb +1 -1
  21. data/lib/active_job/queue_adapters/inline_adapter.rb +1 -1
  22. data/lib/active_job/queue_adapters/queue_classic_adapter.rb +4 -4
  23. data/lib/active_job/queue_adapters/resque_adapter.rb +1 -1
  24. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +42 -14
  25. data/lib/active_job/queue_adapters/sneakers_adapter.rb +1 -1
  26. data/lib/active_job/queue_adapters/sucker_punch_adapter.rb +2 -2
  27. data/lib/active_job/queue_adapters/test_adapter.rb +2 -2
  28. data/lib/active_job/queue_adapters.rb +8 -7
  29. data/lib/active_job/queue_priority.rb +18 -1
  30. data/lib/active_job/railtie.rb +25 -6
  31. data/lib/active_job/serializers/big_decimal_serializer.rb +22 -0
  32. data/lib/active_job/serializers/duration_serializer.rb +4 -2
  33. data/lib/active_job/serializers.rb +7 -3
  34. data/lib/active_job/test_helper.rb +23 -3
  35. data/lib/active_job/version.rb +1 -1
  36. data/lib/active_job.rb +26 -4
  37. data/lib/rails/generators/job/USAGE +19 -0
  38. data/lib/rails/generators/job/job_generator.rb +6 -2
  39. data/lib/rails/generators/job/templates/job.rb.tt +1 -1
  40. metadata +13 -11
  41. data/lib/active_job/queue_adapters/que_adapter.rb +0 -61
@@ -1,6 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveJob
4
+ class << self
5
+ private
6
+ def instrument_enqueue_all(queue_adapter, jobs)
7
+ payload = { adapter: queue_adapter, jobs: jobs }
8
+ ActiveSupport::Notifications.instrument("enqueue_all.active_job", payload) do
9
+ result = yield payload
10
+ payload[:enqueued_count] = result
11
+ result
12
+ end
13
+ end
14
+ end
15
+
4
16
  module Instrumentation # :nodoc:
5
17
  extend ActiveSupport::Concern
6
18
 
@@ -21,19 +33,15 @@ module ActiveJob
21
33
  end
22
34
 
23
35
  def instrument(operation, payload = {}, &block)
24
- enhanced_block = ->(event_payload) do
25
- value = block.call if block
26
-
27
- if defined?(@_halted_callback_hook_called) && @_halted_callback_hook_called
28
- event_payload[:aborted] = true
29
- @_halted_callback_hook_called = nil
30
- end
36
+ payload[:job] = self
37
+ payload[:adapter] = queue_adapter
31
38
 
39
+ ActiveSupport::Notifications.instrument("#{operation}.active_job", payload) do
40
+ value = block.call if block
41
+ payload[:aborted] = @_halted_callback_hook_called if defined?(@_halted_callback_hook_called)
42
+ @_halted_callback_hook_called = nil
32
43
  value
33
44
  end
34
-
35
- ActiveSupport::Notifications.instrument \
36
- "#{operation}.active_job", payload.merge(adapter: queue_adapter, job: self), &enhanced_block
37
45
  end
38
46
 
39
47
  def halted_callback_hook(*)
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/string/filters"
4
3
  require "active_support/log_subscriber"
5
4
 
6
5
  module ActiveJob
7
6
  class LogSubscriber < ActiveSupport::LogSubscriber # :nodoc:
7
+ class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
8
+
8
9
  def enqueue(event)
9
10
  job = event.payload[:job]
10
11
  ex = event.payload[:exception_object] || job.enqueue_error
@@ -23,6 +24,7 @@ module ActiveJob
23
24
  end
24
25
  end
25
26
  end
27
+ subscribe_log_level :enqueue, :info
26
28
 
27
29
  def enqueue_at(event)
28
30
  job = event.payload[:job]
@@ -38,10 +40,38 @@ module ActiveJob
38
40
  end
39
41
  else
40
42
  info do
41
- "Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)} at #{scheduled_at(event).to_default_s}" + args_info(job)
43
+ "Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)} at #{scheduled_at(event)}" + args_info(job)
44
+ end
45
+ end
46
+ end
47
+ subscribe_log_level :enqueue_at, :info
48
+
49
+ def enqueue_all(event)
50
+ info do
51
+ jobs = event.payload[:jobs]
52
+ adapter = event.payload[:adapter]
53
+ enqueued_count = event.payload[:enqueued_count]
54
+
55
+ if enqueued_count == jobs.size
56
+ enqueued_jobs_message(adapter, jobs)
57
+ elsif jobs.any?(&:successfully_enqueued?)
58
+ enqueued_jobs = jobs.select(&:successfully_enqueued?)
59
+
60
+ failed_enqueue_count = jobs.size - enqueued_count
61
+ if failed_enqueue_count == 0
62
+ enqueued_jobs_message(adapter, enqueued_jobs)
63
+ else
64
+ "#{enqueued_jobs_message(adapter, enqueued_jobs)}. "\
65
+ "Failed enqueuing #{failed_enqueue_count} #{'job'.pluralize(failed_enqueue_count)}"
66
+ end
67
+ else
68
+ failed_enqueue_count = jobs.size - enqueued_count
69
+ "Failed enqueuing #{failed_enqueue_count} #{'job'.pluralize(failed_enqueue_count)} "\
70
+ "to #{ActiveJob.adapter_name(adapter)}"
42
71
  end
43
72
  end
44
73
  end
74
+ subscribe_log_level :enqueue_all, :info
45
75
 
46
76
  def perform_start(event)
47
77
  info do
@@ -49,6 +79,7 @@ module ActiveJob
49
79
  "Performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} enqueued at #{job.enqueued_at}" + args_info(job)
50
80
  end
51
81
  end
82
+ subscribe_log_level :perform_start, :info
52
83
 
53
84
  def perform(event)
54
85
  job = event.payload[:job]
@@ -67,6 +98,7 @@ module ActiveJob
67
98
  end
68
99
  end
69
100
  end
101
+ subscribe_log_level :perform, :info
70
102
 
71
103
  def enqueue_retry(event)
72
104
  job = event.payload[:job]
@@ -75,34 +107,37 @@ module ActiveJob
75
107
 
76
108
  info do
77
109
  if ex
78
- "Retrying #{job.class} in #{wait.to_i} seconds, due to a #{ex.class}."
110
+ "Retrying #{job.class} (Job ID: #{job.job_id}) after #{job.executions} attempts in #{wait.to_i} seconds, due to a #{ex.class} (#{ex.message})."
79
111
  else
80
- "Retrying #{job.class} in #{wait.to_i} seconds."
112
+ "Retrying #{job.class} (Job ID: #{job.job_id}) after #{job.executions} attempts in #{wait.to_i} seconds."
81
113
  end
82
114
  end
83
115
  end
116
+ subscribe_log_level :enqueue_retry, :info
84
117
 
85
118
  def retry_stopped(event)
86
119
  job = event.payload[:job]
87
120
  ex = event.payload[:error]
88
121
 
89
122
  error do
90
- "Stopped retrying #{job.class} due to a #{ex.class}, which reoccurred on #{job.executions} attempts."
123
+ "Stopped retrying #{job.class} (Job ID: #{job.job_id}) due to a #{ex.class} (#{ex.message}), which reoccurred on #{job.executions} attempts."
91
124
  end
92
125
  end
126
+ subscribe_log_level :enqueue_retry, :error
93
127
 
94
128
  def discard(event)
95
129
  job = event.payload[:job]
96
130
  ex = event.payload[:error]
97
131
 
98
132
  error do
99
- "Discarded #{job.class} due to a #{ex.class}."
133
+ "Discarded #{job.class} (Job ID: #{job.job_id}) due to a #{ex.class} (#{ex.message})."
100
134
  end
101
135
  end
136
+ subscribe_log_level :discard, :error
102
137
 
103
138
  private
104
139
  def queue_name(event)
105
- event.payload[:adapter].class.name.demodulize.remove("Adapter") + "(#{event.payload[:job].queue_name})"
140
+ ActiveJob.adapter_name(event.payload[:adapter]) + "(#{event.payload[:job].queue_name})"
106
141
  end
107
142
 
108
143
  def args_info(job)
@@ -134,6 +169,41 @@ module ActiveJob
134
169
  def logger
135
170
  ActiveJob::Base.logger
136
171
  end
172
+
173
+ def info(progname = nil, &block)
174
+ return unless super
175
+
176
+ if ActiveJob.verbose_enqueue_logs
177
+ log_enqueue_source
178
+ end
179
+ end
180
+
181
+ def error(progname = nil, &block)
182
+ return unless super
183
+
184
+ if ActiveJob.verbose_enqueue_logs
185
+ log_enqueue_source
186
+ end
187
+ end
188
+
189
+ def log_enqueue_source
190
+ source = extract_enqueue_source_location(caller)
191
+
192
+ if source
193
+ logger.info("↳ #{source}")
194
+ end
195
+ end
196
+
197
+ def extract_enqueue_source_location(locations)
198
+ backtrace_cleaner.clean(locations.lazy).first
199
+ end
200
+
201
+ def enqueued_jobs_message(adapter, enqueued_jobs)
202
+ enqueued_count = enqueued_jobs.size
203
+ job_classes_counts = enqueued_jobs.map(&:class).tally.sort_by { |_k, v| -v }
204
+ "Enqueued #{enqueued_count} #{'job'.pluralize(enqueued_count)} to #{ActiveJob.adapter_name(adapter)}"\
205
+ " (#{job_classes_counts.map { |klass, count| "#{count} #{klass}" }.join(', ')})"
206
+ end
137
207
  end
138
208
  end
139
209
 
@@ -3,7 +3,18 @@
3
3
  require "active_support/core_ext/string/inflections"
4
4
 
5
5
  module ActiveJob
6
- # The <tt>ActiveJob::QueueAdapter</tt> module is used to load the
6
+ class << self
7
+ def adapter_name(adapter) # :nodoc:
8
+ return adapter.queue_adapter_name if adapter.respond_to?(:queue_adapter_name)
9
+
10
+ adapter_class = adapter.is_a?(Module) ? adapter : adapter.class
11
+ "#{adapter_class.name.demodulize.delete_suffix('Adapter')}"
12
+ end
13
+ end
14
+
15
+ # = Active Job Queue adapter
16
+ #
17
+ # The +ActiveJob::QueueAdapter+ module is used to load the
7
18
  # correct adapter. The default queue adapter is the +:async+ queue.
8
19
  module QueueAdapter # :nodoc:
9
20
  extend ActiveSupport::Concern
@@ -41,7 +52,7 @@ module ActiveJob
41
52
  assign_adapter(name_or_adapter.to_s, queue_adapter)
42
53
  else
43
54
  if queue_adapter?(name_or_adapter)
44
- adapter_name = "#{name_or_adapter.class.name.demodulize.remove('Adapter').underscore}"
55
+ adapter_name = ActiveJob.adapter_name(name_or_adapter).underscore
45
56
  assign_adapter(adapter_name, name_or_adapter)
46
57
  else
47
58
  raise ArgumentError
@@ -7,7 +7,7 @@ require "concurrent/utility/processor_counter"
7
7
 
8
8
  module ActiveJob
9
9
  module QueueAdapters
10
- # == Active Job Async adapter
10
+ # = Active Job Async adapter
11
11
  #
12
12
  # The Async adapter runs jobs with an in-process thread pool.
13
13
  #
@@ -95,7 +95,7 @@ module ActiveJob
95
95
 
96
96
  def enqueue_at(job, timestamp, queue_name:)
97
97
  delay = timestamp - Time.current.to_f
98
- if delay > 0
98
+ if !immediate && delay > 0
99
99
  Concurrent::ScheduledTask.execute(delay, args: [job], executor: executor, &:perform)
100
100
  else
101
101
  enqueue(job, queue_name: queue_name)
@@ -4,7 +4,7 @@ require "backburner"
4
4
 
5
5
  module ActiveJob
6
6
  module QueueAdapters
7
- # == Backburner adapter for Active Job
7
+ # = Backburner adapter for Active Job
8
8
  #
9
9
  # Backburner is a beanstalkd-powered job queue that can handle a very
10
10
  # high volume of jobs. You create background jobs and place them on
@@ -16,12 +16,16 @@ module ActiveJob
16
16
  # Rails.application.config.active_job.queue_adapter = :backburner
17
17
  class BackburnerAdapter
18
18
  def enqueue(job) # :nodoc:
19
- Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority)
19
+ response = Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority)
20
+ job.provider_job_id = response[:id] if response.is_a?(Hash)
21
+ response
20
22
  end
21
23
 
22
24
  def enqueue_at(job, timestamp) # :nodoc:
23
25
  delay = timestamp - Time.current.to_f
24
- Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority, delay: delay)
26
+ response = Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority, delay: delay)
27
+ job.provider_job_id = response[:id] if response.is_a?(Hash)
28
+ response
25
29
  end
26
30
 
27
31
  class JobWrapper # :nodoc:
@@ -5,7 +5,7 @@ require "active_support/core_ext/string/inflections"
5
5
 
6
6
  module ActiveJob
7
7
  module QueueAdapters
8
- # == Delayed Job adapter for Active Job
8
+ # = Delayed Job adapter for Active Job
9
9
  #
10
10
  # Delayed::Job (or DJ) encapsulates the common pattern of asynchronously
11
11
  # executing longer tasks in the background. Although DJ can have many
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveJob
4
4
  module QueueAdapters
5
- # == Active Job Inline adapter
5
+ # = Active Job Inline adapter
6
6
  #
7
7
  # When enqueuing jobs with the Inline adapter the job will be executed
8
8
  # immediately.
@@ -4,7 +4,7 @@ require "queue_classic"
4
4
 
5
5
  module ActiveJob
6
6
  module QueueAdapters
7
- # == queue_classic adapter for Active Job
7
+ # = queue_classic adapter for Active Job
8
8
  #
9
9
  # queue_classic provides a simple interface to a PostgreSQL-backed message
10
10
  # queue. queue_classic specializes in concurrent locking and minimizing
@@ -37,10 +37,10 @@ module ActiveJob
37
37
  qc_job
38
38
  end
39
39
 
40
- # Builds a <tt>QC::Queue</tt> object to schedule jobs on.
40
+ # Builds a +QC::Queue+ object to schedule jobs on.
41
41
  #
42
- # If you have a custom <tt>QC::Queue</tt> subclass you'll need to subclass
43
- # <tt>ActiveJob::QueueAdapters::QueueClassicAdapter</tt> and override the
42
+ # If you have a custom +QC::Queue+ subclass you'll need to subclass
43
+ # +ActiveJob::QueueAdapters::QueueClassicAdapter+ and override the
44
44
  # <tt>build_queue</tt> method.
45
45
  def build_queue(queue_name)
46
46
  QC::Queue.new(queue_name)
@@ -16,7 +16,7 @@ end
16
16
 
17
17
  module ActiveJob
18
18
  module QueueAdapters
19
- # == Resque adapter for Active Job
19
+ # = Resque adapter for Active Job
20
20
  #
21
21
  # Resque (pronounced like "rescue") is a Redis-backed library for creating
22
22
  # background jobs, placing those jobs on multiple queues, and processing
@@ -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].
@@ -18,21 +19,48 @@ module ActiveJob
18
19
  # Rails.application.config.active_job.queue_adapter = :sidekiq
19
20
  class SidekiqAdapter
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 }
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
@@ -4,13 +4,13 @@ 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
  #
@@ -2,11 +2,11 @@
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
  #
@@ -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,10 +111,6 @@ 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
 
@@ -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
@@ -25,6 +29,15 @@ module ActiveJob
25
29
  options = app.config.active_job
26
30
  options.queue_adapter ||= :async
27
31
 
32
+ config.after_initialize do
33
+ options.each do |k, v|
34
+ k = "#{k}="
35
+ if ActiveJob.respond_to?(k)
36
+ ActiveJob.send(k, v)
37
+ end
38
+ end
39
+ end
40
+
28
41
  ActiveSupport.on_load(:active_job) do
29
42
  # Configs used in other initializers
30
43
  options = options.except(
@@ -32,19 +45,19 @@ module ActiveJob
32
45
  :custom_serializers
33
46
  )
34
47
 
35
- options.each do |k, v|
48
+ options.each do |k, v|
36
49
  k = "#{k}="
37
- send(k, v) if respond_to? k
50
+ if ActiveJob.respond_to?(k)
51
+ ActiveJob.send(k, v)
52
+ elsif respond_to? k
53
+ send(k, v)
54
+ end
38
55
  end
39
56
  end
40
57
 
41
58
  ActiveSupport.on_load(:action_dispatch_integration_test) do
42
59
  include ActiveJob::TestHelper
43
60
  end
44
-
45
- ActiveSupport.on_load(:active_record) do
46
- self.destroy_association_async_job = ActiveRecord::DestroyAssociationAsyncJob
47
- end
48
61
  end
49
62
 
50
63
  initializer "active_job.set_reloader_hook" do |app|
@@ -70,5 +83,11 @@ module ActiveJob
70
83
  end
71
84
  end
72
85
  end
86
+
87
+ initializer "active_job.backtrace_cleaner" do
88
+ ActiveSupport.on_load(:active_job) do
89
+ LogSubscriber.backtrace_cleaner = ::Rails.backtrace_cleaner
90
+ end
91
+ end
73
92
  end
74
93
  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
@@ -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