activejob 5.2.2.1 → 6.0.2

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -32
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +17 -10
  5. data/lib/active_job.rb +2 -1
  6. data/lib/active_job/arguments.rb +40 -28
  7. data/lib/active_job/base.rb +3 -1
  8. data/lib/active_job/callbacks.rb +4 -1
  9. data/lib/active_job/core.rb +38 -21
  10. data/lib/active_job/enqueuing.rb +26 -5
  11. data/lib/active_job/exceptions.rb +40 -17
  12. data/lib/active_job/execution.rb +1 -1
  13. data/lib/active_job/gem_version.rb +3 -3
  14. data/lib/active_job/logging.rb +40 -9
  15. data/lib/active_job/queue_adapter.rb +2 -0
  16. data/lib/active_job/queue_adapters.rb +8 -10
  17. data/lib/active_job/queue_adapters/async_adapter.rb +1 -1
  18. data/lib/active_job/queue_adapters/backburner_adapter.rb +2 -2
  19. data/lib/active_job/queue_adapters/inline_adapter.rb +1 -1
  20. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +2 -2
  21. data/lib/active_job/queue_adapters/test_adapter.rb +22 -8
  22. data/lib/active_job/queue_name.rb +21 -1
  23. data/lib/active_job/railtie.rb +16 -1
  24. data/lib/active_job/serializers.rb +63 -0
  25. data/lib/active_job/serializers/date_serializer.rb +21 -0
  26. data/lib/active_job/serializers/date_time_serializer.rb +21 -0
  27. data/lib/active_job/serializers/duration_serializer.rb +24 -0
  28. data/lib/active_job/serializers/object_serializer.rb +54 -0
  29. data/lib/active_job/serializers/symbol_serializer.rb +21 -0
  30. data/lib/active_job/serializers/time_serializer.rb +21 -0
  31. data/lib/active_job/serializers/time_with_zone_serializer.rb +21 -0
  32. data/lib/active_job/test_helper.rb +278 -57
  33. data/lib/active_job/timezones.rb +13 -0
  34. data/lib/active_job/translation.rb +1 -1
  35. data/lib/rails/generators/job/job_generator.rb +4 -0
  36. metadata +22 -11
  37. 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 JobWrapper, [ job.serialize ], queue: job.queue_name
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 JobWrapper, [ job.serialize ], queue: job.queue_name, delay: delay
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 http://guides.rubyonrails.org/active_job_basics.html"
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.to_s,
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.to_s,
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
- enqueue_or_perform(perform_enqueued_jobs, job, job_data)
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
- enqueue_or_perform(perform_enqueued_at_jobs, job, job_data)
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 enqueue_or_perform(perform, job, job_data)
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
- !Array(filter).include?(job.class)
68
+ !filter_as_proc(filter).call(job)
59
69
  elsif reject
60
- Array(reject).include?(job.class)
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
 
@@ -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 { |k, v| send("#{k}=", v) }
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