activejob 5.2.4 → 6.0.2.1
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 +125 -34
- data/MIT-LICENSE +1 -1
- data/README.md +17 -10
- data/lib/active_job.rb +2 -1
- data/lib/active_job/arguments.rb +40 -28
- data/lib/active_job/base.rb +3 -1
- data/lib/active_job/callbacks.rb +4 -1
- data/lib/active_job/core.rb +38 -21
- data/lib/active_job/enqueuing.rb +26 -5
- data/lib/active_job/exceptions.rb +40 -17
- data/lib/active_job/execution.rb +1 -1
- data/lib/active_job/gem_version.rb +4 -4
- data/lib/active_job/logging.rb +40 -9
- data/lib/active_job/queue_adapter.rb +2 -0
- data/lib/active_job/queue_adapters.rb +8 -10
- 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/sidekiq_adapter.rb +2 -2
- data/lib/active_job/queue_adapters/test_adapter.rb +22 -8
- data/lib/active_job/queue_name.rb +21 -1
- data/lib/active_job/railtie.rb +16 -1
- data/lib/active_job/serializers.rb +63 -0
- 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/test_helper.rb +278 -57
- data/lib/active_job/timezones.rb +13 -0
- data/lib/active_job/translation.rb +1 -1
- data/lib/rails/generators/job/job_generator.rb +4 -0
- metadata +21 -10
- 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
|
@@ -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
|
@@ -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
|
@@ -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,63 @@
|
|
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 :SymbolSerializer
|
13
|
+
autoload :DurationSerializer
|
14
|
+
autoload :DateTimeSerializer
|
15
|
+
autoload :DateSerializer
|
16
|
+
autoload :TimeWithZoneSerializer
|
17
|
+
autoload :TimeSerializer
|
18
|
+
|
19
|
+
mattr_accessor :_additional_serializers
|
20
|
+
self._additional_serializers = Set.new
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# Returns serialized representative of the passed object.
|
24
|
+
# Will look up through all known serializers.
|
25
|
+
# Raises <tt>ActiveJob::SerializationError</tt> if it can't find a proper serializer.
|
26
|
+
def serialize(argument)
|
27
|
+
serializer = serializers.detect { |s| s.serialize?(argument) }
|
28
|
+
raise SerializationError.new("Unsupported argument type: #{argument.class.name}") unless serializer
|
29
|
+
serializer.serialize(argument)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns deserialized object.
|
33
|
+
# Will look up through all known serializers.
|
34
|
+
# If no serializer found will raise <tt>ArgumentError</tt>.
|
35
|
+
def deserialize(argument)
|
36
|
+
serializer_name = argument[Arguments::OBJECT_SERIALIZER_KEY]
|
37
|
+
raise ArgumentError, "Serializer name is not present in the argument: #{argument.inspect}" unless serializer_name
|
38
|
+
|
39
|
+
serializer = serializer_name.safe_constantize
|
40
|
+
raise ArgumentError, "Serializer #{serializer_name} is not known" unless serializer
|
41
|
+
|
42
|
+
serializer.deserialize(argument)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns list of known serializers.
|
46
|
+
def serializers
|
47
|
+
self._additional_serializers
|
48
|
+
end
|
49
|
+
|
50
|
+
# Adds new serializers to a list of known serializers.
|
51
|
+
def add_serializers(*new_serializers)
|
52
|
+
self._additional_serializers += new_serializers.flatten
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
add_serializers SymbolSerializer,
|
57
|
+
DurationSerializer,
|
58
|
+
DateTimeSerializer,
|
59
|
+
DateSerializer,
|
60
|
+
TimeWithZoneSerializer,
|
61
|
+
TimeSerializer
|
62
|
+
end
|
63
|
+
end
|
@@ -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
|