activejob 5.2.7.1 → 6.1.4.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +109 -58
- data/MIT-LICENSE +1 -1
- data/README.md +18 -13
- data/lib/active_job/arguments.rb +80 -30
- data/lib/active_job/base.rb +6 -1
- data/lib/active_job/callbacks.rb +46 -3
- data/lib/active_job/configured_job.rb +2 -0
- data/lib/active_job/core.rb +40 -21
- data/lib/active_job/enqueuing.rb +20 -7
- data/lib/active_job/exceptions.rb +60 -28
- data/lib/active_job/execution.rb +11 -2
- data/lib/active_job/gem_version.rb +4 -4
- data/lib/active_job/instrumentation.rb +40 -0
- data/lib/active_job/log_subscriber.rb +140 -0
- data/lib/active_job/logging.rb +3 -101
- data/lib/active_job/queue_adapter.rb +5 -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/que_adapter.rb +2 -2
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +2 -2
- data/lib/active_job/queue_adapters/sucker_punch_adapter.rb +1 -1
- data/lib/active_job/queue_adapters/test_adapter.rb +32 -14
- data/lib/active_job/queue_adapters.rb +13 -11
- data/lib/active_job/queue_name.rb +23 -3
- data/lib/active_job/railtie.rb +20 -1
- data/lib/active_job/serializers/date_serializer.rb +20 -0
- data/lib/active_job/serializers/date_time_serializer.rb +16 -0
- data/lib/active_job/serializers/duration_serializer.rb +23 -0
- data/lib/active_job/serializers/module_serializer.rb +20 -0
- data/lib/active_job/serializers/object_serializer.rb +53 -0
- data/lib/active_job/serializers/symbol_serializer.rb +20 -0
- data/lib/active_job/serializers/time_object_serializer.rb +13 -0
- data/lib/active_job/serializers/time_serializer.rb +16 -0
- data/lib/active_job/serializers/time_with_zone_serializer.rb +16 -0
- data/lib/active_job/serializers.rb +66 -0
- data/lib/active_job/test_helper.rb +317 -68
- 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 +26 -11
- data/lib/active_job/queue_adapters/qu_adapter.rb +0 -46
@@ -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
|
@@ -18,13 +18,13 @@ module ActiveJob
|
|
18
18
|
# Rails.application.config.active_job.queue_adapter = :que
|
19
19
|
class QueAdapter
|
20
20
|
def enqueue(job) #:nodoc:
|
21
|
-
que_job = JobWrapper.enqueue job.serialize, priority: job.priority
|
21
|
+
que_job = JobWrapper.enqueue job.serialize, priority: job.priority, queue: job.queue_name
|
22
22
|
job.provider_job_id = que_job.attrs["job_id"]
|
23
23
|
que_job
|
24
24
|
end
|
25
25
|
|
26
26
|
def enqueue_at(job, timestamp) #:nodoc:
|
27
|
-
que_job = JobWrapper.enqueue job.serialize, priority: job.priority, run_at: Time.at(timestamp)
|
27
|
+
que_job = JobWrapper.enqueue job.serialize, priority: job.priority, queue: job.queue_name, run_at: Time.at(timestamp)
|
28
28
|
job.provider_job_id = que_job.attrs["job_id"]
|
29
29
|
que_job
|
30
30
|
end
|
@@ -21,7 +21,7 @@ module ActiveJob
|
|
21
21
|
# Sidekiq::Client does not support symbols as keys
|
22
22
|
job.provider_job_id = Sidekiq::Client.push \
|
23
23
|
"class" => JobWrapper,
|
24
|
-
"wrapped" => job.class
|
24
|
+
"wrapped" => job.class,
|
25
25
|
"queue" => job.queue_name,
|
26
26
|
"args" => [ job.serialize ]
|
27
27
|
end
|
@@ -29,7 +29,7 @@ module ActiveJob
|
|
29
29
|
def enqueue_at(job, timestamp) #:nodoc:
|
30
30
|
job.provider_job_id = Sidekiq::Client.push \
|
31
31
|
"class" => JobWrapper,
|
32
|
-
"wrapped" => job.class
|
32
|
+
"wrapped" => job.class,
|
33
33
|
"queue" => job.queue_name,
|
34
34
|
"args" => [ job.serialize ],
|
35
35
|
"at" => timestamp
|
@@ -10,7 +10,7 @@ module ActiveJob
|
|
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 (
|
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
|
#
|
@@ -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, :at)
|
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.
|
@@ -26,42 +26,60 @@ module ActiveJob
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def enqueue(job) #:nodoc:
|
29
|
-
return if filtered?(job)
|
30
|
-
|
31
29
|
job_data = job_to_hash(job)
|
32
|
-
|
30
|
+
perform_or_enqueue(perform_enqueued_jobs && !filtered?(job), job, job_data)
|
33
31
|
end
|
34
32
|
|
35
33
|
def enqueue_at(job, timestamp) #:nodoc:
|
36
|
-
return if filtered?(job)
|
37
|
-
|
38
34
|
job_data = job_to_hash(job, at: timestamp)
|
39
|
-
|
35
|
+
perform_or_enqueue(perform_enqueued_at_jobs && !filtered?(job), job, job_data)
|
40
36
|
end
|
41
37
|
|
42
38
|
private
|
43
39
|
def job_to_hash(job, extras = {})
|
44
|
-
|
40
|
+
job.serialize.tap do |job_data|
|
41
|
+
job_data[:job] = job.class
|
42
|
+
job_data[:args] = job_data.fetch("arguments")
|
43
|
+
job_data[:queue] = job_data.fetch("queue_name")
|
44
|
+
end.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
|
-
Base.execute
|
50
|
+
Base.execute(job.serialize)
|
51
51
|
else
|
52
52
|
enqueued_jobs << job_data
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
56
|
def filtered?(job)
|
57
|
+
filtered_queue?(job) || filtered_job_class?(job) || filtered_time?(job)
|
58
|
+
end
|
59
|
+
|
60
|
+
def filtered_time?(job)
|
61
|
+
job.scheduled_at > at.to_f if at && job.scheduled_at
|
62
|
+
end
|
63
|
+
|
64
|
+
def filtered_queue?(job)
|
65
|
+
if queue
|
66
|
+
job.queue_name != queue.to_s
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def filtered_job_class?(job)
|
57
71
|
if filter
|
58
|
-
!
|
72
|
+
!filter_as_proc(filter).call(job)
|
59
73
|
elsif reject
|
60
|
-
|
61
|
-
else
|
62
|
-
false
|
74
|
+
filter_as_proc(reject).call(job)
|
63
75
|
end
|
64
76
|
end
|
77
|
+
|
78
|
+
def filter_as_proc(filter)
|
79
|
+
return filter if filter.is_a?(Proc)
|
80
|
+
|
81
|
+
->(job) { Array(filter).include?(job.class) }
|
82
|
+
end
|
65
83
|
end
|
66
84
|
end
|
67
85
|
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.
|
@@ -73,9 +72,9 @@ module ActiveJob
|
|
73
72
|
# Yes: Allows the priority to be set on the job object, at the queue level or
|
74
73
|
# as default configuration option.
|
75
74
|
#
|
76
|
-
# No:
|
75
|
+
# No: The adapter 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
|
#
|
@@ -87,6 +86,8 @@ module ActiveJob
|
|
87
86
|
#
|
88
87
|
# Global: The adapter is configured that all jobs have a maximum run time.
|
89
88
|
#
|
89
|
+
# No: The adapter does not allow the timeout of jobs to be configured.
|
90
|
+
#
|
90
91
|
# N/A: This adapter does not run in a separate process, and therefore timeout
|
91
92
|
# is unsupported.
|
92
93
|
#
|
@@ -100,6 +101,8 @@ module ActiveJob
|
|
100
101
|
#
|
101
102
|
# Global: The adapter has a global number of retries.
|
102
103
|
#
|
104
|
+
# No: The adapter does not allow the number of retries to be configured.
|
105
|
+
#
|
103
106
|
# N/A: The adapter does not run in a separate process, and therefore doesn't
|
104
107
|
# support retries.
|
105
108
|
#
|
@@ -114,7 +117,6 @@ module ActiveJob
|
|
114
117
|
autoload :InlineAdapter
|
115
118
|
autoload :BackburnerAdapter
|
116
119
|
autoload :DelayedJobAdapter
|
117
|
-
autoload :QuAdapter
|
118
120
|
autoload :QueAdapter
|
119
121
|
autoload :QueueClassicAdapter
|
120
122
|
autoload :ResqueAdapter
|
@@ -123,7 +125,7 @@ module ActiveJob
|
|
123
125
|
autoload :SuckerPunchAdapter
|
124
126
|
autoload :TestAdapter
|
125
127
|
|
126
|
-
ADAPTER = "Adapter"
|
128
|
+
ADAPTER = "Adapter"
|
127
129
|
private_constant :ADAPTER
|
128
130
|
|
129
131
|
class << self
|
@@ -6,7 +6,6 @@ module ActiveJob
|
|
6
6
|
|
7
7
|
# Includes the ability to override the default queue name and prefix.
|
8
8
|
module ClassMethods
|
9
|
-
mattr_accessor :queue_name_prefix
|
10
9
|
mattr_accessor :default_queue_name, default: "default"
|
11
10
|
|
12
11
|
# Specifies the name of the queue to process the job on.
|
@@ -18,6 +17,26 @@ module ActiveJob
|
|
18
17
|
# post.to_feed!
|
19
18
|
# end
|
20
19
|
# end
|
20
|
+
#
|
21
|
+
# Can be given a block that will evaluate in the context of the job
|
22
|
+
# allowing +self.arguments+ to be accessed so that a dynamic queue name
|
23
|
+
# can be applied:
|
24
|
+
#
|
25
|
+
# class PublishToFeedJob < ApplicationJob
|
26
|
+
# queue_as do
|
27
|
+
# post = self.arguments.first
|
28
|
+
#
|
29
|
+
# if post.paid?
|
30
|
+
# :paid_feeds
|
31
|
+
# else
|
32
|
+
# :feeds
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# def perform(post)
|
37
|
+
# post.to_feed!
|
38
|
+
# end
|
39
|
+
# end
|
21
40
|
def queue_as(part_name = nil, &block)
|
22
41
|
if block_given?
|
23
42
|
self.queue_name = block
|
@@ -29,13 +48,14 @@ module ActiveJob
|
|
29
48
|
def queue_name_from_part(part_name) #:nodoc:
|
30
49
|
queue_name = part_name || default_queue_name
|
31
50
|
name_parts = [queue_name_prefix.presence, queue_name]
|
32
|
-
name_parts.compact.join(queue_name_delimiter)
|
51
|
+
-name_parts.compact.join(queue_name_delimiter)
|
33
52
|
end
|
34
53
|
end
|
35
54
|
|
36
55
|
included do
|
37
|
-
class_attribute :queue_name, instance_accessor: false, default: default_queue_name
|
56
|
+
class_attribute :queue_name, instance_accessor: false, default: -> { self.class.default_queue_name }
|
38
57
|
class_attribute :queue_name_delimiter, instance_accessor: false, default: "_"
|
58
|
+
class_attribute :queue_name_prefix
|
39
59
|
end
|
40
60
|
|
41
61
|
# Returns the name of the queue the job will be run on.
|
data/lib/active_job/railtie.rb
CHANGED
@@ -7,17 +7,36 @@ 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
|
36
|
+
end
|
37
|
+
|
38
|
+
ActiveSupport.on_load(:active_record) do
|
39
|
+
self.destroy_association_async_job = ActiveRecord::DestroyAssociationAsyncJob
|
21
40
|
end
|
22
41
|
end
|
23
42
|
|
@@ -0,0 +1,20 @@
|
|
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
|
+
def klass
|
16
|
+
Date
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Serializers
|
5
|
+
class DateTimeSerializer < TimeObjectSerializer # :nodoc:
|
6
|
+
def deserialize(hash)
|
7
|
+
DateTime.iso8601(hash["value"])
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def klass
|
12
|
+
DateTime
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
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
|
+
def klass
|
19
|
+
ActiveSupport::Duration
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Serializers
|
5
|
+
class ModuleSerializer < ObjectSerializer # :nodoc:
|
6
|
+
def serialize(constant)
|
7
|
+
super("value" => constant.name)
|
8
|
+
end
|
9
|
+
|
10
|
+
def deserialize(hash)
|
11
|
+
hash["value"].constantize
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def klass
|
16
|
+
Module
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,53 @@
|
|
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(json)
|
43
|
+
raise NotImplementedError
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
# The class of the object that will be serialized.
|
48
|
+
def klass # :doc:
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,20 @@
|
|
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
|
+
def klass
|
16
|
+
Symbol
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Serializers
|
5
|
+
class TimeSerializer < TimeObjectSerializer # :nodoc:
|
6
|
+
def deserialize(hash)
|
7
|
+
Time.iso8601(hash["value"])
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def klass
|
12
|
+
Time
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module Serializers
|
5
|
+
class TimeWithZoneSerializer < TimeObjectSerializer # :nodoc:
|
6
|
+
def deserialize(hash)
|
7
|
+
Time.iso8601(hash["value"]).in_time_zone
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def klass
|
12
|
+
ActiveSupport::TimeWithZone
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
module ActiveJob
|
6
|
+
# The <tt>ActiveJob::Serializers</tt> module is used to store a list of known serializers
|
7
|
+
# and to add new ones. It also has helpers to serialize/deserialize objects.
|
8
|
+
module Serializers # :nodoc:
|
9
|
+
extend ActiveSupport::Autoload
|
10
|
+
|
11
|
+
autoload :ObjectSerializer
|
12
|
+
autoload :TimeObjectSerializer
|
13
|
+
autoload :SymbolSerializer
|
14
|
+
autoload :DurationSerializer
|
15
|
+
autoload :DateTimeSerializer
|
16
|
+
autoload :DateSerializer
|
17
|
+
autoload :TimeWithZoneSerializer
|
18
|
+
autoload :TimeSerializer
|
19
|
+
autoload :ModuleSerializer
|
20
|
+
|
21
|
+
mattr_accessor :_additional_serializers
|
22
|
+
self._additional_serializers = Set.new
|
23
|
+
|
24
|
+
class << self
|
25
|
+
# Returns serialized representative of the passed object.
|
26
|
+
# Will look up through all known serializers.
|
27
|
+
# Raises <tt>ActiveJob::SerializationError</tt> if it can't find a proper serializer.
|
28
|
+
def serialize(argument)
|
29
|
+
serializer = serializers.detect { |s| s.serialize?(argument) }
|
30
|
+
raise SerializationError.new("Unsupported argument type: #{argument.class.name}") unless serializer
|
31
|
+
serializer.serialize(argument)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns deserialized object.
|
35
|
+
# Will look up through all known serializers.
|
36
|
+
# If no serializer found will raise <tt>ArgumentError</tt>.
|
37
|
+
def deserialize(argument)
|
38
|
+
serializer_name = argument[Arguments::OBJECT_SERIALIZER_KEY]
|
39
|
+
raise ArgumentError, "Serializer name is not present in the argument: #{argument.inspect}" unless serializer_name
|
40
|
+
|
41
|
+
serializer = serializer_name.safe_constantize
|
42
|
+
raise ArgumentError, "Serializer #{serializer_name} is not known" unless serializer
|
43
|
+
|
44
|
+
serializer.deserialize(argument)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns list of known serializers.
|
48
|
+
def serializers
|
49
|
+
self._additional_serializers
|
50
|
+
end
|
51
|
+
|
52
|
+
# Adds new serializers to a list of known serializers.
|
53
|
+
def add_serializers(*new_serializers)
|
54
|
+
self._additional_serializers += new_serializers.flatten
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
add_serializers SymbolSerializer,
|
59
|
+
DurationSerializer,
|
60
|
+
DateTimeSerializer,
|
61
|
+
DateSerializer,
|
62
|
+
TimeWithZoneSerializer,
|
63
|
+
TimeSerializer,
|
64
|
+
ModuleSerializer
|
65
|
+
end
|
66
|
+
end
|