activejob 5.2.0 → 6.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +145 -14
- data/MIT-LICENSE +1 -1
- data/README.md +17 -10
- data/lib/active_job/arguments.rb +54 -33
- data/lib/active_job/base.rb +3 -1
- data/lib/active_job/callbacks.rb +4 -1
- data/lib/active_job/core.rb +54 -25
- data/lib/active_job/enqueuing.rb +26 -5
- data/lib/active_job/exceptions.rb +44 -21
- data/lib/active_job/execution.rb +4 -4
- data/lib/active_job/gem_version.rb +2 -2
- data/lib/active_job/logging.rb +40 -9
- data/lib/active_job/queue_adapter.rb +2 -0
- data/lib/active_job/queue_adapters/async_adapter.rb +1 -1
- data/lib/active_job/queue_adapters/backburner_adapter.rb +2 -2
- data/lib/active_job/queue_adapters/inline_adapter.rb +1 -1
- data/lib/active_job/queue_adapters/test_adapter.rb +22 -8
- data/lib/active_job/queue_adapters.rb +8 -10
- data/lib/active_job/queue_name.rb +21 -1
- data/lib/active_job/railtie.rb +16 -1
- data/lib/active_job/serializers/date_serializer.rb +21 -0
- data/lib/active_job/serializers/date_time_serializer.rb +21 -0
- data/lib/active_job/serializers/duration_serializer.rb +24 -0
- data/lib/active_job/serializers/object_serializer.rb +54 -0
- data/lib/active_job/serializers/symbol_serializer.rb +21 -0
- data/lib/active_job/serializers/time_serializer.rb +21 -0
- data/lib/active_job/serializers/time_with_zone_serializer.rb +21 -0
- data/lib/active_job/serializers.rb +63 -0
- data/lib/active_job/test_helper.rb +290 -61
- data/lib/active_job/timezones.rb +13 -0
- data/lib/active_job/translation.rb +1 -1
- data/lib/active_job.rb +2 -1
- data/lib/rails/generators/job/job_generator.rb +4 -0
- metadata +19 -12
- data/lib/active_job/queue_adapters/qu_adapter.rb +0 -46
@@ -30,28 +30,36 @@ module ActiveJob
|
|
30
30
|
# class RemoteServiceJob < ActiveJob::Base
|
31
31
|
# retry_on CustomAppException # defaults to 3s wait, 5 attempts
|
32
32
|
# retry_on AnotherCustomAppException, wait: ->(executions) { executions * 2 }
|
33
|
-
#
|
34
|
-
# ExceptionNotifier.caught(exception)
|
35
|
-
# end
|
33
|
+
#
|
36
34
|
# retry_on ActiveRecord::Deadlocked, wait: 5.seconds, attempts: 3
|
37
|
-
# retry_on Net::OpenTimeout, wait: :exponentially_longer, attempts: 10
|
35
|
+
# retry_on Net::OpenTimeout, Timeout::Error, wait: :exponentially_longer, attempts: 10 # retries at most 10 times for Net::OpenTimeout and Timeout::Error combined
|
36
|
+
# # To retry at most 10 times for each individual exception:
|
37
|
+
# # retry_on Net::OpenTimeout, wait: :exponentially_longer, attempts: 10
|
38
|
+
# # retry_on Timeout::Error, wait: :exponentially_longer, attempts: 10
|
39
|
+
#
|
40
|
+
# retry_on(YetAnotherCustomAppException) do |job, error|
|
41
|
+
# ExceptionNotifier.caught(error)
|
42
|
+
# end
|
38
43
|
#
|
39
44
|
# def perform(*args)
|
40
45
|
# # Might raise CustomAppException, AnotherCustomAppException, or YetAnotherCustomAppException for something domain specific
|
41
46
|
# # Might raise ActiveRecord::Deadlocked when a local db deadlock is detected
|
42
|
-
# # Might raise Net::OpenTimeout when the remote service is down
|
47
|
+
# # Might raise Net::OpenTimeout or Timeout::Error when the remote service is down
|
43
48
|
# end
|
44
49
|
# end
|
45
|
-
def retry_on(
|
46
|
-
rescue_from
|
50
|
+
def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil)
|
51
|
+
rescue_from(*exceptions) do |error|
|
52
|
+
executions = executions_for(exceptions)
|
53
|
+
|
47
54
|
if executions < attempts
|
48
|
-
|
49
|
-
retry_job wait: determine_delay(wait), queue: queue, priority: priority
|
55
|
+
retry_job wait: determine_delay(seconds_or_duration_or_algorithm: wait, executions: executions), queue: queue, priority: priority, error: error
|
50
56
|
else
|
51
57
|
if block_given?
|
52
|
-
|
58
|
+
instrument :retry_stopped, error: error do
|
59
|
+
yield self, error
|
60
|
+
end
|
53
61
|
else
|
54
|
-
|
62
|
+
instrument :retry_stopped, error: error
|
55
63
|
raise error
|
56
64
|
end
|
57
65
|
end
|
@@ -67,8 +75,8 @@ module ActiveJob
|
|
67
75
|
#
|
68
76
|
# class SearchIndexingJob < ActiveJob::Base
|
69
77
|
# discard_on ActiveJob::DeserializationError
|
70
|
-
# discard_on(CustomAppException) do |job,
|
71
|
-
# ExceptionNotifier.caught(
|
78
|
+
# discard_on(CustomAppException) do |job, error|
|
79
|
+
# ExceptionNotifier.caught(error)
|
72
80
|
# end
|
73
81
|
#
|
74
82
|
# def perform(record)
|
@@ -76,12 +84,10 @@ module ActiveJob
|
|
76
84
|
# # Might raise CustomAppException for something domain specific
|
77
85
|
# end
|
78
86
|
# end
|
79
|
-
def discard_on(
|
80
|
-
rescue_from
|
81
|
-
|
82
|
-
yield self,
|
83
|
-
else
|
84
|
-
logger.error "Discarded #{self.class} due to a #{exception}. The original exception was #{error.cause.inspect}."
|
87
|
+
def discard_on(*exceptions)
|
88
|
+
rescue_from(*exceptions) do |error|
|
89
|
+
instrument :discard, error: error do
|
90
|
+
yield self, error if block_given?
|
85
91
|
end
|
86
92
|
end
|
87
93
|
end
|
@@ -109,11 +115,13 @@ module ActiveJob
|
|
109
115
|
# end
|
110
116
|
# end
|
111
117
|
def retry_job(options = {})
|
112
|
-
|
118
|
+
instrument :enqueue_retry, options.slice(:error, :wait) do
|
119
|
+
enqueue options
|
120
|
+
end
|
113
121
|
end
|
114
122
|
|
115
123
|
private
|
116
|
-
def determine_delay(seconds_or_duration_or_algorithm)
|
124
|
+
def determine_delay(seconds_or_duration_or_algorithm:, executions:)
|
117
125
|
case seconds_or_duration_or_algorithm
|
118
126
|
when :exponentially_longer
|
119
127
|
(executions**4) + 2
|
@@ -130,5 +138,20 @@ module ActiveJob
|
|
130
138
|
raise "Couldn't determine a delay based on #{seconds_or_duration_or_algorithm.inspect}"
|
131
139
|
end
|
132
140
|
end
|
141
|
+
|
142
|
+
def instrument(name, error: nil, wait: nil, &block)
|
143
|
+
payload = { job: self, adapter: self.class.queue_adapter, error: error, wait: wait }
|
144
|
+
|
145
|
+
ActiveSupport::Notifications.instrument("#{name}.active_job", payload, &block)
|
146
|
+
end
|
147
|
+
|
148
|
+
def executions_for(exceptions)
|
149
|
+
if exception_executions
|
150
|
+
exception_executions[exceptions.to_s] = (exception_executions[exceptions.to_s] || 0) + 1
|
151
|
+
else
|
152
|
+
# Guard against jobs that were persisted before we started having individual executions counters per retry_on
|
153
|
+
executions
|
154
|
+
end
|
155
|
+
end
|
133
156
|
end
|
134
157
|
end
|
data/lib/active_job/execution.rb
CHANGED
@@ -26,16 +26,16 @@ module ActiveJob
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
# Performs the job immediately. The job is not sent to the
|
29
|
+
# Performs the job immediately. The job is not sent to the queuing adapter
|
30
30
|
# but directly executed by blocking the execution of others until it's finished.
|
31
31
|
#
|
32
32
|
# MyJob.new(*args).perform_now
|
33
33
|
def perform_now
|
34
|
+
# Guard against jobs that were persisted before we started counting executions by zeroing out nil counters
|
35
|
+
self.executions = (executions || 0) + 1
|
36
|
+
|
34
37
|
deserialize_arguments_if_needed
|
35
38
|
run_callbacks :perform do
|
36
|
-
# Guard against jobs that were persisted before we started counting executions by zeroing out nil counters
|
37
|
-
self.executions = (executions || 0) + 1
|
38
|
-
|
39
39
|
perform(*arguments)
|
40
40
|
end
|
41
41
|
rescue => exception
|
data/lib/active_job/logging.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/hash/transform_values"
|
4
3
|
require "active_support/core_ext/string/filters"
|
5
4
|
require "active_support/tagged_logging"
|
6
5
|
require "active_support/logger"
|
@@ -12,13 +11,13 @@ module ActiveJob
|
|
12
11
|
included do
|
13
12
|
cattr_accessor :logger, default: ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
|
14
13
|
|
15
|
-
around_enqueue do |_, block
|
14
|
+
around_enqueue do |_, block|
|
16
15
|
tag_logger do
|
17
16
|
block.call
|
18
17
|
end
|
19
18
|
end
|
20
19
|
|
21
|
-
around_perform do |job, block
|
20
|
+
around_perform do |job, block|
|
22
21
|
tag_logger(job.class.name, job.job_id) do
|
23
22
|
payload = { adapter: job.class.queue_adapter, job: job }
|
24
23
|
ActiveSupport::Notifications.instrument("perform_start.active_job", payload.dup)
|
@@ -28,13 +27,13 @@ module ActiveJob
|
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
31
|
-
|
30
|
+
around_enqueue do |job, block|
|
32
31
|
if job.scheduled_at
|
33
|
-
ActiveSupport::Notifications.instrument
|
34
|
-
adapter: job.class.queue_adapter, job: job
|
32
|
+
ActiveSupport::Notifications.instrument("enqueue_at.active_job",
|
33
|
+
adapter: job.class.queue_adapter, job: job, &block)
|
35
34
|
else
|
36
|
-
ActiveSupport::Notifications.instrument
|
37
|
-
adapter: job.class.queue_adapter, job: job
|
35
|
+
ActiveSupport::Notifications.instrument("enqueue.active_job",
|
36
|
+
adapter: job.class.queue_adapter, job: job, &block)
|
38
37
|
end
|
39
38
|
end
|
40
39
|
end
|
@@ -71,7 +70,7 @@ module ActiveJob
|
|
71
70
|
def perform_start(event)
|
72
71
|
info do
|
73
72
|
job = event.payload[:job]
|
74
|
-
"Performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)}" + args_info(job)
|
73
|
+
"Performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} enqueued at #{job.enqueued_at}" + args_info(job)
|
75
74
|
end
|
76
75
|
end
|
77
76
|
|
@@ -89,6 +88,38 @@ module ActiveJob
|
|
89
88
|
end
|
90
89
|
end
|
91
90
|
|
91
|
+
def enqueue_retry(event)
|
92
|
+
job = event.payload[:job]
|
93
|
+
ex = event.payload[:error]
|
94
|
+
wait = event.payload[:wait]
|
95
|
+
|
96
|
+
info do
|
97
|
+
if ex
|
98
|
+
"Retrying #{job.class} in #{wait.to_i} seconds, due to a #{ex.class}."
|
99
|
+
else
|
100
|
+
"Retrying #{job.class} in #{wait.to_i} seconds."
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def retry_stopped(event)
|
106
|
+
job = event.payload[:job]
|
107
|
+
ex = event.payload[:error]
|
108
|
+
|
109
|
+
error do
|
110
|
+
"Stopped retrying #{job.class} due to a #{ex.class}, which reoccurred on #{job.executions} attempts."
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def discard(event)
|
115
|
+
job = event.payload[:job]
|
116
|
+
ex = event.payload[:error]
|
117
|
+
|
118
|
+
error do
|
119
|
+
"Discarded #{job.class} due to a #{ex.class}."
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
92
123
|
private
|
93
124
|
def queue_name(event)
|
94
125
|
event.payload[:adapter].class.name.demodulize.remove("Adapter") + "(#{event.payload[:job].queue_name})"
|
@@ -31,7 +31,7 @@ module ActiveJob
|
|
31
31
|
# jobs. Since jobs share a single thread pool, long-running jobs will block
|
32
32
|
# short-lived jobs. Fine for dev/test; bad for production.
|
33
33
|
class AsyncAdapter
|
34
|
-
# See {Concurrent::ThreadPoolExecutor}[https://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ThreadPoolExecutor.html] for executor options.
|
34
|
+
# See {Concurrent::ThreadPoolExecutor}[https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ThreadPoolExecutor.html] for executor options.
|
35
35
|
def initialize(**executor_options)
|
36
36
|
@scheduler = Scheduler.new(**executor_options)
|
37
37
|
end
|
@@ -16,12 +16,12 @@ 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
|
19
|
+
Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority)
|
20
20
|
end
|
21
21
|
|
22
22
|
def enqueue_at(job, timestamp) #:nodoc:
|
23
23
|
delay = timestamp - Time.current.to_f
|
24
|
-
Backburner::Worker.enqueue
|
24
|
+
Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority, delay: delay)
|
25
25
|
end
|
26
26
|
|
27
27
|
class JobWrapper #:nodoc:
|
@@ -16,7 +16,7 @@ module ActiveJob
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def enqueue_at(*) #:nodoc:
|
19
|
-
raise NotImplementedError, "Use a queueing backend to enqueue jobs in the future. Read more at
|
19
|
+
raise NotImplementedError, "Use a queueing backend to enqueue jobs in the future. Read more at https://guides.rubyonrails.org/active_job_basics.html"
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -12,7 +12,7 @@ module ActiveJob
|
|
12
12
|
#
|
13
13
|
# Rails.application.config.active_job.queue_adapter = :test
|
14
14
|
class TestAdapter
|
15
|
-
attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject)
|
15
|
+
attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue)
|
16
16
|
attr_writer(:enqueued_jobs, :performed_jobs)
|
17
17
|
|
18
18
|
# Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
|
@@ -29,14 +29,14 @@ module ActiveJob
|
|
29
29
|
return if filtered?(job)
|
30
30
|
|
31
31
|
job_data = job_to_hash(job)
|
32
|
-
|
32
|
+
perform_or_enqueue(perform_enqueued_jobs, job, job_data)
|
33
33
|
end
|
34
34
|
|
35
35
|
def enqueue_at(job, timestamp) #:nodoc:
|
36
36
|
return if filtered?(job)
|
37
37
|
|
38
38
|
job_data = job_to_hash(job, at: timestamp)
|
39
|
-
|
39
|
+
perform_or_enqueue(perform_enqueued_at_jobs, job, job_data)
|
40
40
|
end
|
41
41
|
|
42
42
|
private
|
@@ -44,7 +44,7 @@ module ActiveJob
|
|
44
44
|
{ job: job.class, args: job.serialize.fetch("arguments"), queue: job.queue_name }.merge!(extras)
|
45
45
|
end
|
46
46
|
|
47
|
-
def
|
47
|
+
def perform_or_enqueue(perform, job, job_data)
|
48
48
|
if perform
|
49
49
|
performed_jobs << job_data
|
50
50
|
Base.execute job.serialize
|
@@ -54,14 +54,28 @@ module ActiveJob
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def filtered?(job)
|
57
|
+
filtered_queue?(job) || filtered_job_class?(job)
|
58
|
+
end
|
59
|
+
|
60
|
+
def filtered_queue?(job)
|
61
|
+
if queue
|
62
|
+
job.queue_name != queue.to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def filtered_job_class?(job)
|
57
67
|
if filter
|
58
|
-
!
|
68
|
+
!filter_as_proc(filter).call(job)
|
59
69
|
elsif reject
|
60
|
-
|
61
|
-
else
|
62
|
-
false
|
70
|
+
filter_as_proc(reject).call(job)
|
63
71
|
end
|
64
72
|
end
|
73
|
+
|
74
|
+
def filter_as_proc(filter)
|
75
|
+
return filter if filter.is_a?(Proc)
|
76
|
+
|
77
|
+
->(job) { Array(filter).include?(job.class) }
|
78
|
+
end
|
65
79
|
end
|
66
80
|
end
|
67
81
|
end
|
@@ -3,19 +3,19 @@
|
|
3
3
|
module ActiveJob
|
4
4
|
# == Active Job adapters
|
5
5
|
#
|
6
|
-
# Active Job has adapters for the following
|
6
|
+
# Active Job has adapters for the following queuing backends:
|
7
7
|
#
|
8
8
|
# * {Backburner}[https://github.com/nesquena/backburner]
|
9
9
|
# * {Delayed Job}[https://github.com/collectiveidea/delayed_job]
|
10
|
-
# * {Qu}[https://github.com/bkeepers/qu]
|
11
10
|
# * {Que}[https://github.com/chanks/que]
|
12
11
|
# * {queue_classic}[https://github.com/QueueClassic/queue_classic]
|
13
12
|
# * {Resque}[https://github.com/resque/resque]
|
14
|
-
# * {Sidekiq}[
|
13
|
+
# * {Sidekiq}[https://sidekiq.org]
|
15
14
|
# * {Sneakers}[https://github.com/jondot/sneakers]
|
16
15
|
# * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch]
|
17
|
-
# * {Active Job Async Job}[
|
18
|
-
# * {Active Job Inline}[
|
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
|
+
# * Please Note: We are not accepting pull requests for new adapters. See the {README}[link:files/activejob/README_md.html] for more details.
|
19
19
|
#
|
20
20
|
# === Backends Features
|
21
21
|
#
|
@@ -23,7 +23,6 @@ module ActiveJob
|
|
23
23
|
# |-------------------|-------|--------|------------|------------|---------|---------|
|
24
24
|
# | Backburner | Yes | Yes | Yes | Yes | Job | Global |
|
25
25
|
# | Delayed Job | Yes | Yes | Yes | Job | Global | Global |
|
26
|
-
# | Qu | Yes | Yes | No | No | No | Global |
|
27
26
|
# | Que | Yes | Yes | Yes | Job | No | Job |
|
28
27
|
# | queue_classic | Yes | Yes | Yes* | No | No | No |
|
29
28
|
# | Resque | Yes | Yes | Yes (Gem) | Queue | Global | Yes |
|
@@ -53,7 +52,7 @@ module ActiveJob
|
|
53
52
|
#
|
54
53
|
# No: The adapter will run jobs at the next opportunity and cannot use perform_later.
|
55
54
|
#
|
56
|
-
# N/A: The adapter does not support
|
55
|
+
# N/A: The adapter does not support queuing.
|
57
56
|
#
|
58
57
|
# NOTE:
|
59
58
|
# queue_classic supports job scheduling since version 3.1.
|
@@ -75,7 +74,7 @@ module ActiveJob
|
|
75
74
|
#
|
76
75
|
# No: Does not allow the priority of jobs to be configured.
|
77
76
|
#
|
78
|
-
# N/A: The adapter does not support
|
77
|
+
# N/A: The adapter does not support queuing, and therefore sorting them.
|
79
78
|
#
|
80
79
|
# ==== Timeout
|
81
80
|
#
|
@@ -114,7 +113,6 @@ module ActiveJob
|
|
114
113
|
autoload :InlineAdapter
|
115
114
|
autoload :BackburnerAdapter
|
116
115
|
autoload :DelayedJobAdapter
|
117
|
-
autoload :QuAdapter
|
118
116
|
autoload :QueAdapter
|
119
117
|
autoload :QueueClassicAdapter
|
120
118
|
autoload :ResqueAdapter
|
@@ -123,7 +121,7 @@ module ActiveJob
|
|
123
121
|
autoload :SuckerPunchAdapter
|
124
122
|
autoload :TestAdapter
|
125
123
|
|
126
|
-
ADAPTER = "Adapter"
|
124
|
+
ADAPTER = "Adapter"
|
127
125
|
private_constant :ADAPTER
|
128
126
|
|
129
127
|
class << self
|
@@ -18,6 +18,26 @@ module ActiveJob
|
|
18
18
|
# post.to_feed!
|
19
19
|
# end
|
20
20
|
# end
|
21
|
+
#
|
22
|
+
# Can be given a block that will evaluate in the context of the job
|
23
|
+
# allowing +self.arguments+ to be accessed so that a dynamic queue name
|
24
|
+
# can be applied:
|
25
|
+
#
|
26
|
+
# class PublishToFeedJob < ApplicationJob
|
27
|
+
# queue_as do
|
28
|
+
# post = self.arguments.first
|
29
|
+
#
|
30
|
+
# if post.paid?
|
31
|
+
# :paid_feeds
|
32
|
+
# else
|
33
|
+
# :feeds
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# def perform(post)
|
38
|
+
# post.to_feed!
|
39
|
+
# end
|
40
|
+
# end
|
21
41
|
def queue_as(part_name = nil, &block)
|
22
42
|
if block_given?
|
23
43
|
self.queue_name = block
|
@@ -34,7 +54,7 @@ module ActiveJob
|
|
34
54
|
end
|
35
55
|
|
36
56
|
included do
|
37
|
-
class_attribute :queue_name, instance_accessor: false, default: default_queue_name
|
57
|
+
class_attribute :queue_name, instance_accessor: false, default: -> { self.class.default_queue_name }
|
38
58
|
class_attribute :queue_name_delimiter, instance_accessor: false, default: "_"
|
39
59
|
end
|
40
60
|
|
data/lib/active_job/railtie.rb
CHANGED
@@ -7,17 +7,32 @@ module ActiveJob
|
|
7
7
|
# = Active Job Railtie
|
8
8
|
class Railtie < Rails::Railtie # :nodoc:
|
9
9
|
config.active_job = ActiveSupport::OrderedOptions.new
|
10
|
+
config.active_job.custom_serializers = []
|
10
11
|
|
11
12
|
initializer "active_job.logger" do
|
12
13
|
ActiveSupport.on_load(:active_job) { self.logger = ::Rails.logger }
|
13
14
|
end
|
14
15
|
|
16
|
+
initializer "active_job.custom_serializers" do |app|
|
17
|
+
config.after_initialize do
|
18
|
+
custom_serializers = app.config.active_job.delete(:custom_serializers)
|
19
|
+
ActiveJob::Serializers.add_serializers custom_serializers
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
15
23
|
initializer "active_job.set_configs" do |app|
|
16
24
|
options = app.config.active_job
|
17
25
|
options.queue_adapter ||= :async
|
18
26
|
|
19
27
|
ActiveSupport.on_load(:active_job) do
|
20
|
-
options.each
|
28
|
+
options.each do |k, v|
|
29
|
+
k = "#{k}="
|
30
|
+
send(k, v) if respond_to? k
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
ActiveSupport.on_load(:action_dispatch_integration_test) do
|
35
|
+
include ActiveJob::TestHelper
|
21
36
|
end
|
22
37
|
end
|
23
38
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Serializers
|
5
|
+
class DateSerializer < ObjectSerializer # :nodoc:
|
6
|
+
def serialize(date)
|
7
|
+
super("value" => date.iso8601)
|
8
|
+
end
|
9
|
+
|
10
|
+
def deserialize(hash)
|
11
|
+
Date.iso8601(hash["value"])
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def klass
|
17
|
+
Date
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Serializers
|
5
|
+
class DateTimeSerializer < ObjectSerializer # :nodoc:
|
6
|
+
def serialize(time)
|
7
|
+
super("value" => time.iso8601)
|
8
|
+
end
|
9
|
+
|
10
|
+
def deserialize(hash)
|
11
|
+
DateTime.iso8601(hash["value"])
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def klass
|
17
|
+
DateTime
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Serializers
|
5
|
+
class DurationSerializer < ObjectSerializer # :nodoc:
|
6
|
+
def serialize(duration)
|
7
|
+
super("value" => duration.value, "parts" => Arguments.serialize(duration.parts))
|
8
|
+
end
|
9
|
+
|
10
|
+
def deserialize(hash)
|
11
|
+
value = hash["value"]
|
12
|
+
parts = Arguments.deserialize(hash["parts"])
|
13
|
+
|
14
|
+
klass.new(value, parts)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def klass
|
20
|
+
ActiveSupport::Duration
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Serializers
|
5
|
+
# Base class for serializing and deserializing custom objects.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# class MoneySerializer < ActiveJob::Serializers::ObjectSerializer
|
10
|
+
# def serialize(money)
|
11
|
+
# super("amount" => money.amount, "currency" => money.currency)
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# def deserialize(hash)
|
15
|
+
# Money.new(hash["amount"], hash["currency"])
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# private
|
19
|
+
#
|
20
|
+
# def klass
|
21
|
+
# Money
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
class ObjectSerializer
|
25
|
+
include Singleton
|
26
|
+
|
27
|
+
class << self
|
28
|
+
delegate :serialize?, :serialize, :deserialize, to: :instance
|
29
|
+
end
|
30
|
+
|
31
|
+
# Determines if an argument should be serialized by a serializer.
|
32
|
+
def serialize?(argument)
|
33
|
+
argument.is_a?(klass)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Serializes an argument to a JSON primitive type.
|
37
|
+
def serialize(hash)
|
38
|
+
{ Arguments::OBJECT_SERIALIZER_KEY => self.class.name }.merge!(hash)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Deserializes an argument from a JSON primitive type.
|
42
|
+
def deserialize(_argument)
|
43
|
+
raise NotImplementedError
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# The class of the object that will be serialized.
|
49
|
+
def klass # :doc:
|
50
|
+
raise NotImplementedError
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Serializers
|
5
|
+
class SymbolSerializer < ObjectSerializer # :nodoc:
|
6
|
+
def serialize(argument)
|
7
|
+
super("value" => argument.to_s)
|
8
|
+
end
|
9
|
+
|
10
|
+
def deserialize(argument)
|
11
|
+
argument["value"].to_sym
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def klass
|
17
|
+
Symbol
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Serializers
|
5
|
+
class TimeSerializer < ObjectSerializer # :nodoc:
|
6
|
+
def serialize(time)
|
7
|
+
super("value" => time.iso8601)
|
8
|
+
end
|
9
|
+
|
10
|
+
def deserialize(hash)
|
11
|
+
Time.iso8601(hash["value"])
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def klass
|
17
|
+
Time
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Serializers
|
5
|
+
class TimeWithZoneSerializer < ObjectSerializer # :nodoc:
|
6
|
+
def serialize(time)
|
7
|
+
super("value" => time.iso8601)
|
8
|
+
end
|
9
|
+
|
10
|
+
def deserialize(hash)
|
11
|
+
Time.iso8601(hash["value"]).in_time_zone
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def klass
|
17
|
+
ActiveSupport::TimeWithZone
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|