activejob 7.0.8.1 → 7.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +112 -155
  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