loading
Generated 2020-08-25T23:38:58-04:00

All Files ( 91.42% covered at 849.76 hits/line )

44 files in total.
1165 relevant lines, 1065 lines covered and 100 lines missed. ( 91.42% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
lib/active_job.rb 100.00 % 40 12 12 0 11.00
lib/active_job/arguments.rb 93.00 % 215 100 93 7 741.78
lib/active_job/base.rb 100.00 % 79 28 28 0 11.00
lib/active_job/callbacks.rb 100.00 % 197 40 40 0 66.55
lib/active_job/configured_job.rb 90.91 % 20 11 10 1 382.00
lib/active_job/core.rb 100.00 % 176 58 58 0 2346.81
lib/active_job/enqueuing.rb 100.00 % 82 29 29 0 1969.52
lib/active_job/exceptions.rb 97.83 % 166 46 45 1 221.57
lib/active_job/execution.rb 95.45 % 58 22 21 1 817.68
lib/active_job/gem_version.rb 88.89 % 17 9 8 1 9.78
lib/active_job/instrumentation.rb 100.00 % 37 20 20 0 2733.40
lib/active_job/log_subscriber.rb 100.00 % 139 77 77 0 1741.65
lib/active_job/logging.rb 100.00 % 33 19 19 0 1316.11
lib/active_job/queue_adapter.rb 93.10 % 65 29 27 2 194.14
lib/active_job/queue_adapters.rb 100.00 % 141 19 19 0 13.89
lib/active_job/queue_adapters/async_adapter.rb 90.48 % 116 42 38 4 9.19
lib/active_job/queue_adapters/backburner_adapter.rb 100.00 % 36 13 13 0 23.38
lib/active_job/queue_adapters/delayed_job_adapter.rb 95.00 % 47 20 19 1 25.55
lib/active_job/queue_adapters/inline_adapter.rb 100.00 % 23 7 7 0 11.29
lib/active_job/queue_adapters/que_adapter.rb 100.00 % 39 15 15 0 26.53
lib/active_job/queue_adapters/queue_classic_adapter.rb 95.24 % 58 21 20 1 31.43
lib/active_job/queue_adapters/resque_adapter.rb 81.82 % 53 22 18 4 15.59
lib/active_job/queue_adapters/sidekiq_adapter.rb 100.00 % 47 12 12 0 19.08
lib/active_job/queue_adapters/sneakers_adapter.rb 100.00 % 48 20 20 0 10.05
lib/active_job/queue_adapters/sucker_punch_adapter.rb 88.24 % 49 17 15 2 24.35
lib/active_job/queue_adapters/test_adapter.rb 100.00 % 85 41 41 0 1398.61
lib/active_job/queue_name.rb 100.00 % 69 21 21 0 2288.76
lib/active_job/queue_priority.rb 100.00 % 43 15 15 0 849.47
lib/active_job/railtie.rb 0.00 % 49 39 0 39 0.00
lib/active_job/serializers.rb 100.00 % 65 30 30 0 303.50
lib/active_job/serializers/date_serializer.rb 100.00 % 20 10 10 0 36.30
lib/active_job/serializers/date_time_serializer.rb 100.00 % 20 10 10 0 57.20
lib/active_job/serializers/duration_serializer.rb 100.00 % 23 12 12 0 39.42
lib/active_job/serializers/module_serializer.rb 100.00 % 20 10 10 0 24.20
lib/active_job/serializers/object_serializer.rb 86.67 % 53 15 13 2 195.53
lib/active_job/serializers/symbol_serializer.rb 100.00 % 20 10 10 0 150.00
lib/active_job/serializers/time_serializer.rb 100.00 % 20 10 10 0 40.70
lib/active_job/serializers/time_with_zone_serializer.rb 100.00 % 20 10 10 0 53.90
lib/active_job/test_case.rb 100.00 % 11 5 5 0 11.00
lib/active_job/test_helper.rb 100.00 % 716 170 170 0 1904.18
lib/active_job/timezones.rb 100.00 % 13 6 6 0 475.83
lib/active_job/translation.rb 100.00 % 13 6 6 0 475.83
lib/active_job/version.rb 75.00 % 10 4 3 1 8.25
lib/rails/generators/job/job_generator.rb 0.00 % 44 33 0 33 0.00

lib/active_job.rb

100.0% lines covered

12 relevant lines. 12 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. #--
  3. # Copyright (c) 2014-2020 David Heinemeier Hansson
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining
  6. # a copy of this software and associated documentation files (the
  7. # "Software"), to deal in the Software without restriction, including
  8. # without limitation the rights to use, copy, modify, merge, publish,
  9. # distribute, sublicense, and/or sell copies of the Software, and to
  10. # permit persons to whom the Software is furnished to do so, subject to
  11. # the following conditions:
  12. #
  13. # The above copyright notice and this permission notice shall be
  14. # included in all copies or substantial portions of the Software.
  15. #
  16. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  20. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. #++
  24. 11 require "active_support"
  25. 11 require "active_support/rails"
  26. 11 require "active_job/version"
  27. 11 require "global_id"
  28. 11 module ActiveJob
  29. 11 extend ActiveSupport::Autoload
  30. 11 autoload :Base
  31. 11 autoload :QueueAdapters
  32. 11 autoload :Serializers
  33. 11 autoload :ConfiguredJob
  34. 11 autoload :TestCase
  35. 11 autoload :TestHelper
  36. end

lib/active_job/arguments.rb

93.0% lines covered

100 relevant lines. 93 lines covered and 7 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "active_support/core_ext/hash"
  3. 11 module ActiveJob
  4. # Raised when an exception is raised during job arguments deserialization.
  5. #
  6. # Wraps the original exception raised as +cause+.
  7. 11 class DeserializationError < StandardError
  8. 11 def initialize #:nodoc:
  9. 84 super("Error while trying to deserialize arguments: #{$!.message}")
  10. 84 set_backtrace $!.backtrace
  11. end
  12. end
  13. # Raised when an unsupported argument type is set as a job argument. We
  14. # currently support String, Integer, Float, NilClass, TrueClass, FalseClass,
  15. # BigDecimal, Symbol, Date, Time, DateTime, ActiveSupport::TimeWithZone,
  16. # ActiveSupport::Duration, Hash, ActiveSupport::HashWithIndifferentAccess,
  17. # Array or GlobalID::Identification instances, although this can be extended
  18. # by adding custom serializers.
  19. # Raised if you set the key for a Hash something else than a string or
  20. # a symbol. Also raised when trying to serialize an object which can't be
  21. # identified with a GlobalID - such as an unpersisted Active Record model.
  22. 11 class SerializationError < ArgumentError; end
  23. 11 module Arguments
  24. 11 extend self
  25. # Serializes a set of arguments. Intrinsic types that can safely be
  26. # serialized without mutation are returned as-is. Arrays/Hashes are
  27. # serialized element by element. All other types are serialized using
  28. # GlobalID.
  29. 11 def serialize(arguments)
  30. 12775 arguments.map { |argument| serialize_argument(argument) }
  31. end
  32. # Deserializes a set of arguments. Intrinsic types that can safely be
  33. # deserialized without mutation are returned as-is. Arrays/Hashes are
  34. # deserialized element by element. All other types are deserialized using
  35. # GlobalID.
  36. 11 def deserialize(arguments)
  37. 9088 arguments.map { |argument| deserialize_argument(argument) }
  38. rescue
  39. 84 raise DeserializationError
  40. end
  41. 11 private
  42. # :nodoc:
  43. 11 PERMITTED_TYPES = [ NilClass, String, Integer, Float, BigDecimal, TrueClass, FalseClass ]
  44. # :nodoc:
  45. 11 GLOBALID_KEY = "_aj_globalid"
  46. # :nodoc:
  47. 11 SYMBOL_KEYS_KEY = "_aj_symbol_keys"
  48. # :nodoc:
  49. 11 RUBY2_KEYWORDS_KEY = "_aj_ruby2_keywords"
  50. # :nodoc:
  51. 11 WITH_INDIFFERENT_ACCESS_KEY = "_aj_hash_with_indifferent_access"
  52. # :nodoc:
  53. 11 OBJECT_SERIALIZER_KEY = "_aj_serialized"
  54. # :nodoc:
  55. 11 RESERVED_KEYS = [
  56. GLOBALID_KEY, GLOBALID_KEY.to_sym,
  57. SYMBOL_KEYS_KEY, SYMBOL_KEYS_KEY.to_sym,
  58. RUBY2_KEYWORDS_KEY, RUBY2_KEYWORDS_KEY.to_sym,
  59. OBJECT_SERIALIZER_KEY, OBJECT_SERIALIZER_KEY.to_sym,
  60. WITH_INDIFFERENT_ACCESS_KEY, WITH_INDIFFERENT_ACCESS_KEY.to_sym,
  61. ]
  62. 11 private_constant :PERMITTED_TYPES, :RESERVED_KEYS, :GLOBALID_KEY,
  63. :SYMBOL_KEYS_KEY, :RUBY2_KEYWORDS_KEY, :WITH_INDIFFERENT_ACCESS_KEY
  64. 11 unless Hash.respond_to?(:ruby2_keywords_hash?) && Hash.respond_to?(:ruby2_keywords_hash)
  65. 11 using Module.new {
  66. 11 refine Hash do
  67. 11 class << Hash
  68. 11 if RUBY_VERSION >= "2.7"
  69. def ruby2_keywords_hash?(hash)
  70. !new(*[hash]).default.equal?(hash)
  71. end
  72. else
  73. 11 def ruby2_keywords_hash?(hash)
  74. 506 false
  75. end
  76. end
  77. 11 def ruby2_keywords_hash(hash)
  78. _ruby2_keywords_hash(**hash)
  79. end
  80. 11 private def _ruby2_keywords_hash(*args)
  81. args.last
  82. end
  83. 11 ruby2_keywords(:_ruby2_keywords_hash) if respond_to?(:ruby2_keywords, true)
  84. end
  85. end
  86. }
  87. end
  88. 11 def serialize_argument(argument)
  89. 7624 case argument
  90. when *PERMITTED_TYPES
  91. 5909 argument
  92. when GlobalID::Identification
  93. 243 convert_to_global_id_hash(argument)
  94. when Array
  95. 843 argument.map { |arg| serialize_argument(arg) }
  96. when ActiveSupport::HashWithIndifferentAccess
  97. 22 serialize_indifferent_hash(argument)
  98. when Hash
  99. 506 symbol_keys = argument.each_key.grep(Symbol).map!(&:to_s)
  100. 506 aj_hash_key = if Hash.ruby2_keywords_hash?(argument)
  101. RUBY2_KEYWORDS_KEY
  102. else
  103. 506 SYMBOL_KEYS_KEY
  104. end
  105. 506 result = serialize_hash(argument)
  106. 385 result[aj_hash_key] = symbol_keys
  107. 385 result
  108. 691 when -> (arg) { arg.respond_to?(:permitted?) }
  109. 11 serialize_indifferent_hash(argument.to_h)
  110. else
  111. 680 Serializers.serialize(argument)
  112. end
  113. end
  114. 11 def deserialize_argument(argument)
  115. 7544 case argument
  116. when String
  117. 3566 argument
  118. when *PERMITTED_TYPES
  119. 1473 argument
  120. when Array
  121. 2125 argument.map { |arg| deserialize_argument(arg) }
  122. when Hash
  123. 1654 if serialized_global_id?(argument)
  124. 304 deserialize_global_id argument
  125. 1350 elsif custom_serialized?(argument)
  126. 734 Serializers.deserialize(argument)
  127. else
  128. 616 deserialize_hash(argument)
  129. end
  130. else
  131. 22 raise ArgumentError, "Can only deserialize primitive arguments: #{argument.inspect}"
  132. end
  133. end
  134. 11 def serialized_global_id?(hash)
  135. 1654 hash.size == 1 && hash.include?(GLOBALID_KEY)
  136. end
  137. 11 def deserialize_global_id(hash)
  138. 304 GlobalID::Locator.locate hash[GLOBALID_KEY]
  139. end
  140. 11 def custom_serialized?(hash)
  141. 1350 hash.key?(OBJECT_SERIALIZER_KEY)
  142. end
  143. 11 def serialize_hash(argument)
  144. 539 argument.each_with_object({}) do |(key, value), hash|
  145. 682 hash[serialize_hash_key(key)] = serialize_argument(value)
  146. end
  147. end
  148. 11 def deserialize_hash(serialized_hash)
  149. 2057 result = serialized_hash.transform_values { |v| deserialize_argument(v) }
  150. 616 if result.delete(WITH_INDIFFERENT_ACCESS_KEY)
  151. 22 result = result.with_indifferent_access
  152. 594 elsif symbol_keys = result.delete(SYMBOL_KEYS_KEY)
  153. 583 result = transform_symbol_keys(result, symbol_keys)
  154. 11 elsif symbol_keys = result.delete(RUBY2_KEYWORDS_KEY)
  155. result = transform_symbol_keys(result, symbol_keys)
  156. result = Hash.ruby2_keywords_hash(result)
  157. end
  158. 616 result
  159. end
  160. 11 def serialize_hash_key(key)
  161. 682 case key
  162. when *RESERVED_KEYS
  163. 88 raise SerializationError.new("Can't serialize a Hash with reserved key #{key.inspect}")
  164. when String, Symbol
  165. 572 key.to_s
  166. else
  167. 22 raise SerializationError.new("Only string and symbol hash keys may be serialized as job arguments, but #{key.inspect} is a #{key.class}")
  168. end
  169. end
  170. 11 def serialize_indifferent_hash(indifferent_hash)
  171. 33 result = serialize_hash(indifferent_hash)
  172. 33 result[WITH_INDIFFERENT_ACCESS_KEY] = serialize_argument(true)
  173. 33 result
  174. end
  175. 11 def transform_symbol_keys(hash, symbol_keys)
  176. # NOTE: HashWithIndifferentAccess#transform_keys always
  177. # returns stringified keys with indifferent access
  178. # so we call #to_h here to ensure keys are symbolized.
  179. 583 hash.to_h.transform_keys do |key|
  180. 803 if symbol_keys.include?(key)
  181. 748 key.to_sym
  182. else
  183. 55 key
  184. end
  185. end
  186. end
  187. 11 def convert_to_global_id_hash(argument)
  188. 243 { GLOBALID_KEY => argument.to_global_id.to_s }
  189. rescue URI::GID::MissingModelIdError
  190. 11 raise SerializationError, "Unable to serialize #{argument.class} " \
  191. "without an id. (Maybe you forgot to call save?)"
  192. end
  193. end
  194. end

lib/active_job/base.rb

100.0% lines covered

28 relevant lines. 28 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "active_job/core"
  3. 11 require "active_job/queue_adapter"
  4. 11 require "active_job/queue_name"
  5. 11 require "active_job/queue_priority"
  6. 11 require "active_job/enqueuing"
  7. 11 require "active_job/execution"
  8. 11 require "active_job/callbacks"
  9. 11 require "active_job/exceptions"
  10. 11 require "active_job/log_subscriber"
  11. 11 require "active_job/logging"
  12. 11 require "active_job/instrumentation"
  13. 11 require "active_job/timezones"
  14. 11 require "active_job/translation"
  15. 11 module ActiveJob #:nodoc:
  16. # = Active Job
  17. #
  18. # Active Job objects can be configured to work with different backend
  19. # queuing frameworks. To specify a queue adapter to use:
  20. #
  21. # ActiveJob::Base.queue_adapter = :inline
  22. #
  23. # A list of supported adapters can be found in QueueAdapters.
  24. #
  25. # Active Job objects can be defined by creating a class that inherits
  26. # from the ActiveJob::Base class. The only necessary method to
  27. # implement is the "perform" method.
  28. #
  29. # To define an Active Job object:
  30. #
  31. # class ProcessPhotoJob < ActiveJob::Base
  32. # def perform(photo)
  33. # photo.watermark!('Rails')
  34. # photo.rotate!(90.degrees)
  35. # photo.resize_to_fit!(300, 300)
  36. # photo.upload!
  37. # end
  38. # end
  39. #
  40. # Records that are passed in are serialized/deserialized using Global
  41. # ID. More information can be found in Arguments.
  42. #
  43. # To enqueue a job to be performed as soon as the queuing system is free:
  44. #
  45. # ProcessPhotoJob.perform_later(photo)
  46. #
  47. # To enqueue a job to be processed at some point in the future:
  48. #
  49. # ProcessPhotoJob.set(wait_until: Date.tomorrow.noon).perform_later(photo)
  50. #
  51. # More information can be found in ActiveJob::Core::ClassMethods#set
  52. #
  53. # A job can also be processed immediately without sending to the queue:
  54. #
  55. # ProcessPhotoJob.perform_now(photo)
  56. #
  57. # == Exceptions
  58. #
  59. # * DeserializationError - Error class for deserialization errors.
  60. # * SerializationError - Error class for serialization errors.
  61. 11 class Base
  62. 11 include Core
  63. 11 include QueueAdapter
  64. 11 include QueueName
  65. 11 include QueuePriority
  66. 11 include Enqueuing
  67. 11 include Execution
  68. 11 include Callbacks
  69. 11 include Exceptions
  70. 11 include Logging
  71. 11 include Instrumentation
  72. 11 include Timezones
  73. 11 include Translation
  74. 11 ActiveSupport.run_load_hooks(:active_job, self)
  75. end
  76. end

lib/active_job/callbacks.rb

100.0% lines covered

40 relevant lines. 40 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "active_support/callbacks"
  3. 11 require "active_support/core_ext/object/with_options"
  4. 11 require "active_support/core_ext/module/attribute_accessors"
  5. 11 module ActiveJob
  6. # = Active Job Callbacks
  7. #
  8. # Active Job provides hooks during the life cycle of a job. Callbacks allow you
  9. # to trigger logic during this cycle. Available callbacks are:
  10. #
  11. # * <tt>before_enqueue</tt>
  12. # * <tt>around_enqueue</tt>
  13. # * <tt>after_enqueue</tt>
  14. # * <tt>before_perform</tt>
  15. # * <tt>around_perform</tt>
  16. # * <tt>after_perform</tt>
  17. #
  18. # NOTE: Calling the same callback multiple times will overwrite previous callback definitions.
  19. #
  20. 11 module Callbacks
  21. 11 extend ActiveSupport::Concern
  22. 11 include ActiveSupport::Callbacks
  23. 11 class << self
  24. 11 include ActiveSupport::Callbacks
  25. 11 define_callbacks :execute
  26. end
  27. 11 included do
  28. 11 class_attribute :return_false_on_aborted_enqueue, instance_accessor: false, instance_predicate: false, default: false
  29. 11 cattr_accessor :skip_after_callbacks_if_terminated, instance_accessor: false, default: false
  30. 11 with_options(skip_after_callbacks_if_terminated: skip_after_callbacks_if_terminated) do
  31. 11 define_callbacks :perform
  32. 11 define_callbacks :enqueue
  33. end
  34. end
  35. # These methods will be included into any Active Job object, adding
  36. # callbacks for +perform+ and +enqueue+ methods.
  37. 11 module ClassMethods
  38. 11 def inherited(klass)
  39. 385 klass.get_callbacks(:enqueue).config[:skip_after_callbacks_if_terminated] = skip_after_callbacks_if_terminated
  40. 385 klass.get_callbacks(:perform).config[:skip_after_callbacks_if_terminated] = skip_after_callbacks_if_terminated
  41. 385 super
  42. end
  43. # Defines a callback that will get called right before the
  44. # job's perform method is executed.
  45. #
  46. # class VideoProcessJob < ActiveJob::Base
  47. # queue_as :default
  48. #
  49. # before_perform do |job|
  50. # UserMailer.notify_video_started_processing(job.arguments.first)
  51. # end
  52. #
  53. # def perform(video_id)
  54. # Video.find(video_id).process
  55. # end
  56. # end
  57. #
  58. 11 def before_perform(*filters, &blk)
  59. 99 set_callback(:perform, :before, *filters, &blk)
  60. end
  61. # Defines a callback that will get called right after the
  62. # job's perform method has finished.
  63. #
  64. # class VideoProcessJob < ActiveJob::Base
  65. # queue_as :default
  66. #
  67. # after_perform do |job|
  68. # UserMailer.notify_video_processed(job.arguments.first)
  69. # end
  70. #
  71. # def perform(video_id)
  72. # Video.find(video_id).process
  73. # end
  74. # end
  75. #
  76. 11 def after_perform(*filters, &blk)
  77. 77 set_callback(:perform, :after, *filters, &blk)
  78. end
  79. # Defines a callback that will get called around the job's perform method.
  80. #
  81. # class VideoProcessJob < ActiveJob::Base
  82. # queue_as :default
  83. #
  84. # around_perform do |job, block|
  85. # UserMailer.notify_video_started_processing(job.arguments.first)
  86. # block.call
  87. # UserMailer.notify_video_processed(job.arguments.first)
  88. # end
  89. #
  90. # def perform(video_id)
  91. # Video.find(video_id).process
  92. # end
  93. # end
  94. #
  95. # You can access the return value of the job only if the execution wasn't halted.
  96. #
  97. # class VideoProcessJob < ActiveJob::Base
  98. # around_perform do |job, block|
  99. # value = block.call
  100. # puts value # => "Hello World!"
  101. # end
  102. #
  103. # def perform
  104. # "Hello World!"
  105. # end
  106. # end
  107. #
  108. 11 def around_perform(*filters, &blk)
  109. 77 set_callback(:perform, :around, *filters, &blk)
  110. end
  111. # Defines a callback that will get called right before the
  112. # job is enqueued.
  113. #
  114. # class VideoProcessJob < ActiveJob::Base
  115. # queue_as :default
  116. #
  117. # before_enqueue do |job|
  118. # $statsd.increment "enqueue-video-job.try"
  119. # end
  120. #
  121. # def perform(video_id)
  122. # Video.find(video_id).process
  123. # end
  124. # end
  125. #
  126. 11 def before_enqueue(*filters, &blk)
  127. 99 set_callback(:enqueue, :before, *filters, &blk)
  128. end
  129. # Defines a callback that will get called right after the
  130. # job is enqueued.
  131. #
  132. # class VideoProcessJob < ActiveJob::Base
  133. # queue_as :default
  134. #
  135. # after_enqueue do |job|
  136. # $statsd.increment "enqueue-video-job.success"
  137. # end
  138. #
  139. # def perform(video_id)
  140. # Video.find(video_id).process
  141. # end
  142. # end
  143. #
  144. 11 def after_enqueue(*filters, &blk)
  145. 77 set_callback(:enqueue, :after, *filters, &blk)
  146. end
  147. # Defines a callback that will get called around the enqueuing
  148. # of the job.
  149. #
  150. # class VideoProcessJob < ActiveJob::Base
  151. # queue_as :default
  152. #
  153. # around_enqueue do |job, block|
  154. # $statsd.time "video-job.process" do
  155. # block.call
  156. # end
  157. # end
  158. #
  159. # def perform(video_id)
  160. # Video.find(video_id).process
  161. # end
  162. # end
  163. #
  164. 11 def around_enqueue(*filters, &blk)
  165. 44 set_callback(:enqueue, :around, *filters, &blk)
  166. end
  167. end
  168. 11 private
  169. 11 def halted_callback_hook(_filter, name) # :nodoc:
  170. 132 return super unless %i(enqueue perform).include?(name.to_sym)
  171. 132 callbacks = public_send("_#{name}_callbacks")
  172. 330 if !self.class.skip_after_callbacks_if_terminated && callbacks.any? { |c| c.kind == :after }
  173. 22 ActiveSupport::Deprecation.warn(<<~EOM)
  174. In Rails 6.2, `after_enqueue`/`after_perform` callbacks no longer run if `before_enqueue`/`before_perform` respectively halts with `throw :abort`.
  175. To enable this behavior, uncomment the `config.active_job.skip_after_callbacks_if_terminated` config
  176. in the new 6.1 framework defaults initializer.
  177. EOM
  178. end
  179. 132 super
  180. end
  181. end
  182. end

lib/active_job/configured_job.rb

90.91% lines covered

11 relevant lines. 10 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 class ConfiguredJob #:nodoc:
  4. 11 def initialize(job_class, options = {})
  5. 1375 @options = options
  6. 1375 @job_class = job_class
  7. end
  8. 11 def perform_now(*args)
  9. @job_class.new(*args).perform_now
  10. end
  11. 11 ruby2_keywords(:perform_now) if respond_to?(:ruby2_keywords, true)
  12. 11 def perform_later(*args)
  13. 1375 @job_class.new(*args).enqueue @options
  14. end
  15. 11 ruby2_keywords(:perform_later) if respond_to?(:ruby2_keywords, true)
  16. end
  17. end

lib/active_job/core.rb

100.0% lines covered

58 relevant lines. 58 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. # Provides general behavior that will be included into every Active Job
  4. # object that inherits from ActiveJob::Base.
  5. 11 module Core
  6. 11 extend ActiveSupport::Concern
  7. # Job arguments
  8. 11 attr_accessor :arguments
  9. 11 attr_writer :serialized_arguments
  10. # Timestamp when the job should be performed
  11. 11 attr_accessor :scheduled_at
  12. # Job Identifier
  13. 11 attr_accessor :job_id
  14. # Queue in which the job will reside.
  15. 11 attr_writer :queue_name
  16. # Priority that the job will have (lower is more priority).
  17. 11 attr_writer :priority
  18. # ID optionally provided by adapter
  19. 11 attr_accessor :provider_job_id
  20. # Number of times this job has been executed (which increments on every retry, like after an exception).
  21. 11 attr_accessor :executions
  22. # Hash that contains the number of times this job handled errors for each specific retry_on declaration.
  23. # Keys are the string representation of the exceptions listed in the retry_on declaration,
  24. # while its associated value holds the number of executions where the corresponding retry_on
  25. # declaration handled one of its listed exceptions.
  26. 11 attr_accessor :exception_executions
  27. # I18n.locale to be used during the job.
  28. 11 attr_accessor :locale
  29. # Timezone to be used during the job.
  30. 11 attr_accessor :timezone
  31. # Track when a job was enqueued
  32. 11 attr_accessor :enqueued_at
  33. # These methods will be included into any Active Job object, adding
  34. # helpers for de/serialization and creation of job instances.
  35. 11 module ClassMethods
  36. # Creates a new job instance from a hash created with +serialize+
  37. 11 def deserialize(job_data)
  38. 3170 job = job_data["job_class"].constantize.new
  39. 3170 job.deserialize(job_data)
  40. 3170 job
  41. end
  42. # Creates a job preconfigured with the given options. You can call
  43. # perform_later with the job arguments to enqueue the job with the
  44. # preconfigured options
  45. #
  46. # ==== Options
  47. # * <tt>:wait</tt> - Enqueues the job with the specified delay
  48. # * <tt>:wait_until</tt> - Enqueues the job at the time specified
  49. # * <tt>:queue</tt> - Enqueues the job on the specified queue
  50. # * <tt>:priority</tt> - Enqueues the job with the specified priority
  51. #
  52. # ==== Examples
  53. #
  54. # VideoJob.set(queue: :some_queue).perform_later(Video.last)
  55. # VideoJob.set(wait: 5.minutes).perform_later(Video.last)
  56. # VideoJob.set(wait_until: Time.now.tomorrow).perform_later(Video.last)
  57. # VideoJob.set(queue: :some_queue, wait: 5.minutes).perform_later(Video.last)
  58. # VideoJob.set(queue: :some_queue, wait_until: Time.now.tomorrow).perform_later(Video.last)
  59. # VideoJob.set(queue: :some_queue, wait: 5.minutes, priority: 10).perform_later(Video.last)
  60. 11 def set(options = {})
  61. 1375 ConfiguredJob.new(self, options)
  62. end
  63. end
  64. # Creates a new job instance. Takes the arguments that will be
  65. # passed to the perform method.
  66. 11 def initialize(*arguments)
  67. 7674 @arguments = arguments
  68. 7674 @job_id = SecureRandom.uuid
  69. 7674 @queue_name = self.class.queue_name
  70. 7674 @priority = self.class.priority
  71. 7674 @executions = 0
  72. 7674 @exception_executions = {}
  73. end
  74. 11 ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
  75. # Returns a hash with the job data that can safely be passed to the
  76. # queuing adapter.
  77. 11 def serialize
  78. 5887 {
  79. "job_class" => self.class.name,
  80. "job_id" => job_id,
  81. "provider_job_id" => provider_job_id,
  82. "queue_name" => queue_name,
  83. "priority" => priority,
  84. "arguments" => serialize_arguments_if_needed(arguments),
  85. "executions" => executions,
  86. "exception_executions" => exception_executions,
  87. "locale" => I18n.locale.to_s,
  88. "timezone" => Time.zone&.name,
  89. "enqueued_at" => Time.now.utc.iso8601
  90. }
  91. end
  92. # Attaches the stored job data to the current instance. Receives a hash
  93. # returned from +serialize+
  94. #
  95. # ==== Examples
  96. #
  97. # class DeliverWebhookJob < ActiveJob::Base
  98. # attr_writer :attempt_number
  99. #
  100. # def attempt_number
  101. # @attempt_number ||= 0
  102. # end
  103. #
  104. # def serialize
  105. # super.merge('attempt_number' => attempt_number + 1)
  106. # end
  107. #
  108. # def deserialize(job_data)
  109. # super
  110. # self.attempt_number = job_data['attempt_number']
  111. # end
  112. #
  113. # rescue_from(Timeout::Error) do |exception|
  114. # raise exception if attempt_number > 5
  115. # retry_job(wait: 10)
  116. # end
  117. # end
  118. 11 def deserialize(job_data)
  119. 3192 self.job_id = job_data["job_id"]
  120. 3192 self.provider_job_id = job_data["provider_job_id"]
  121. 3192 self.queue_name = job_data["queue_name"]
  122. 3192 self.priority = job_data["priority"]
  123. 3192 self.serialized_arguments = job_data["arguments"]
  124. 3192 self.executions = job_data["executions"]
  125. 3192 self.exception_executions = job_data["exception_executions"]
  126. 3192 self.locale = job_data["locale"] || I18n.locale.to_s
  127. 3192 self.timezone = job_data["timezone"] || Time.zone&.name
  128. 3192 self.enqueued_at = job_data["enqueued_at"]
  129. end
  130. 11 private
  131. 11 def serialize_arguments_if_needed(arguments)
  132. 5887 if arguments_serialized?
  133. 80 @serialized_arguments
  134. else
  135. 5807 serialize_arguments(arguments)
  136. end
  137. end
  138. 11 def deserialize_arguments_if_needed
  139. 4127 if arguments_serialized?
  140. 3137 @arguments = deserialize_arguments(@serialized_arguments)
  141. 3075 @serialized_arguments = nil
  142. end
  143. end
  144. 11 def serialize_arguments(arguments)
  145. 5807 Arguments.serialize(arguments)
  146. end
  147. 11 def deserialize_arguments(serialized_args)
  148. 3137 Arguments.deserialize(serialized_args)
  149. end
  150. 11 def arguments_serialized?
  151. 10014 defined?(@serialized_arguments) && @serialized_arguments
  152. end
  153. end
  154. end

lib/active_job/enqueuing.rb

100.0% lines covered

29 relevant lines. 29 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "active_job/arguments"
  3. 11 module ActiveJob
  4. # Provides behavior for enqueuing jobs.
  5. 11 module Enqueuing
  6. 11 extend ActiveSupport::Concern
  7. # Includes the +perform_later+ method for job initialization.
  8. 11 module ClassMethods
  9. # Push a job onto the queue. By default the arguments must be either String,
  10. # Integer, Float, NilClass, TrueClass, FalseClass, BigDecimal, Symbol, Date,
  11. # Time, DateTime, ActiveSupport::TimeWithZone, ActiveSupport::Duration,
  12. # Hash, ActiveSupport::HashWithIndifferentAccess, Array or
  13. # GlobalID::Identification instances, although this can be extended by adding
  14. # custom serializers.
  15. #
  16. # Returns an instance of the job class queued with arguments available in
  17. # Job#arguments.
  18. 11 def perform_later(*args)
  19. 2618 job_or_instantiate(*args).enqueue
  20. end
  21. 11 ruby2_keywords(:perform_later) if respond_to?(:ruby2_keywords, true)
  22. 11 private
  23. 11 def job_or_instantiate(*args) # :doc:
  24. 2706 args.first.is_a?(self) ? args.first : new(*args)
  25. end
  26. 11 ruby2_keywords(:job_or_instantiate) if respond_to?(:ruby2_keywords, true)
  27. end
  28. # Enqueues the job to be performed by the queue adapter.
  29. #
  30. # ==== Options
  31. # * <tt>:wait</tt> - Enqueues the job with the specified delay
  32. # * <tt>:wait_until</tt> - Enqueues the job at the time specified
  33. # * <tt>:queue</tt> - Enqueues the job on the specified queue
  34. # * <tt>:priority</tt> - Enqueues the job with the specified priority
  35. #
  36. # ==== Examples
  37. #
  38. # my_job_instance.enqueue
  39. # my_job_instance.enqueue wait: 5.minutes
  40. # my_job_instance.enqueue queue: :important
  41. # my_job_instance.enqueue wait_until: Date.tomorrow.midnight
  42. # my_job_instance.enqueue priority: 10
  43. 11 def enqueue(options = {})
  44. 4802 self.scheduled_at = options[:wait].seconds.from_now.to_f if options[:wait]
  45. 4802 self.scheduled_at = options[:wait_until].to_f if options[:wait_until]
  46. 4802 self.queue_name = self.class.queue_name_from_part(options[:queue]) if options[:queue]
  47. 4802 self.priority = options[:priority].to_i if options[:priority]
  48. 4802 successfully_enqueued = false
  49. 4802 run_callbacks :enqueue do
  50. 4692 if scheduled_at
  51. 991 queue_adapter.enqueue_at self, scheduled_at
  52. else
  53. 3701 queue_adapter.enqueue self
  54. end
  55. 4400 successfully_enqueued = true
  56. end
  57. 4488 if successfully_enqueued
  58. 4400 self
  59. else
  60. 88 if self.class.return_false_on_aborted_enqueue
  61. 77 false
  62. else
  63. 11 ActiveSupport::Deprecation.warn(
  64. "Rails 6.1 will return false when the enqueuing is aborted. Make sure your code doesn't depend on it" \
  65. " returning the instance of the job and set `config.active_job.return_false_on_aborted_enqueue = true`" \
  66. " to remove the deprecations."
  67. )
  68. 11 self
  69. end
  70. end
  71. end
  72. end
  73. end

lib/active_job/exceptions.rb

97.83% lines covered

46 relevant lines. 45 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "active_support/core_ext/numeric/time"
  3. 11 module ActiveJob
  4. # Provides behavior for retrying and discarding jobs on exceptions.
  5. 11 module Exceptions
  6. 11 extend ActiveSupport::Concern
  7. 11 included do
  8. 11 class_attribute :retry_jitter, instance_accessor: false, instance_predicate: false, default: 0.0
  9. end
  10. 11 module ClassMethods
  11. # Catch the exception and reschedule job for re-execution after so many seconds, for a specific number of attempts.
  12. # If the exception keeps getting raised beyond the specified number of attempts, the exception is allowed to
  13. # bubble up to the underlying queuing system, which may have its own retry mechanism or place it in a
  14. # holding queue for inspection.
  15. #
  16. # You can also pass a block that'll be invoked if the retry attempts fail for custom logic rather than letting
  17. # the exception bubble up. This block is yielded with the job instance as the first and the error instance as the second parameter.
  18. #
  19. # ==== Options
  20. # * <tt>:wait</tt> - Re-enqueues the job with a delay specified either in seconds (default: 3 seconds),
  21. # as a computing proc that takes the number of executions so far as an argument, or as a symbol reference of
  22. # <tt>:exponentially_longer</tt>, which applies the wait algorithm of <tt>((executions**4) + (Kernel.rand * (executions**4) * jitter)) + 2</tt>
  23. # (first wait ~3s, then ~18s, then ~83s, etc)
  24. # * <tt>:attempts</tt> - Re-enqueues the job the specified number of times (default: 5 attempts)
  25. # * <tt>:queue</tt> - Re-enqueues the job on a different queue
  26. # * <tt>:priority</tt> - Re-enqueues the job with a different priority
  27. # * <tt>:jitter</tt> - A random delay of wait time used when calculating backoff. The default is 15% (0.15) which represents the upper bound of possible wait time (expressed as a percentage)
  28. #
  29. # ==== Examples
  30. #
  31. # class RemoteServiceJob < ActiveJob::Base
  32. # retry_on CustomAppException # defaults to ~3s wait, 5 attempts
  33. # retry_on AnotherCustomAppException, wait: ->(executions) { executions * 2 }
  34. #
  35. # retry_on ActiveRecord::Deadlocked, wait: 5.seconds, attempts: 3
  36. # retry_on Net::OpenTimeout, Timeout::Error, wait: :exponentially_longer, attempts: 10 # retries at most 10 times for Net::OpenTimeout and Timeout::Error combined
  37. # # To retry at most 10 times for each individual exception:
  38. # # retry_on Net::OpenTimeout, wait: :exponentially_longer, attempts: 10
  39. # # retry_on Net::ReadTimeout, wait: 5.seconds, jitter: 0.30, attempts: 10
  40. # # retry_on Timeout::Error, wait: :exponentially_longer, attempts: 10
  41. #
  42. # retry_on(YetAnotherCustomAppException) do |job, error|
  43. # ExceptionNotifier.caught(error)
  44. # end
  45. #
  46. # def perform(*args)
  47. # # Might raise CustomAppException, AnotherCustomAppException, or YetAnotherCustomAppException for something domain specific
  48. # # Might raise ActiveRecord::Deadlocked when a local db deadlock is detected
  49. # # Might raise Net::OpenTimeout or Timeout::Error when the remote service is down
  50. # end
  51. # end
  52. 11 def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil, jitter: JITTER_DEFAULT)
  53. 121 rescue_from(*exceptions) do |error|
  54. 794 executions = executions_for(exceptions)
  55. 794 if executions < attempts
  56. 705 retry_job wait: determine_delay(seconds_or_duration_or_algorithm: wait, executions: executions, jitter: jitter), queue: queue, priority: priority, error: error
  57. else
  58. 89 if block_given?
  59. 27 instrument :retry_stopped, error: error do
  60. 27 yield self, error
  61. end
  62. else
  63. 62 instrument :retry_stopped, error: error
  64. 62 raise error
  65. end
  66. end
  67. end
  68. end
  69. # Discard the job with no attempts to retry, if the exception is raised. This is useful when the subject of the job,
  70. # like an Active Record, is no longer available, and the job is thus no longer relevant.
  71. #
  72. # You can also pass a block that'll be invoked. This block is yielded with the job instance as the first and the error instance as the second parameter.
  73. #
  74. # ==== Example
  75. #
  76. # class SearchIndexingJob < ActiveJob::Base
  77. # discard_on ActiveJob::DeserializationError
  78. # discard_on(CustomAppException) do |job, error|
  79. # ExceptionNotifier.caught(error)
  80. # end
  81. #
  82. # def perform(record)
  83. # # Will raise ActiveJob::DeserializationError if the record can't be deserialized
  84. # # Might raise CustomAppException for something domain specific
  85. # end
  86. # end
  87. 11 def discard_on(*exceptions)
  88. 33 rescue_from(*exceptions) do |error|
  89. 35 instrument :discard, error: error do
  90. 35 yield self, error if block_given?
  91. end
  92. end
  93. end
  94. end
  95. # Reschedules the job to be re-executed. This is useful in combination
  96. # with the +rescue_from+ option. When you rescue an exception from your job
  97. # you can ask Active Job to retry performing your job.
  98. #
  99. # ==== Options
  100. # * <tt>:wait</tt> - Enqueues the job with the specified delay in seconds
  101. # * <tt>:wait_until</tt> - Enqueues the job at the time specified
  102. # * <tt>:queue</tt> - Enqueues the job on the specified queue
  103. # * <tt>:priority</tt> - Enqueues the job with the specified priority
  104. #
  105. # ==== Examples
  106. #
  107. # class SiteScraperJob < ActiveJob::Base
  108. # rescue_from(ErrorLoadingSite) do
  109. # retry_job queue: :low_priority
  110. # end
  111. #
  112. # def perform(*args)
  113. # # raise ErrorLoadingSite if cannot scrape
  114. # end
  115. # end
  116. 11 def retry_job(options = {})
  117. 727 instrument :enqueue_retry, options.slice(:error, :wait) do
  118. 727 enqueue options
  119. end
  120. end
  121. 11 private
  122. 11 JITTER_DEFAULT = Object.new
  123. 11 private_constant :JITTER_DEFAULT
  124. 11 def determine_delay(seconds_or_duration_or_algorithm:, executions:, jitter: JITTER_DEFAULT)
  125. 705 jitter = jitter == JITTER_DEFAULT ? self.class.retry_jitter : (jitter || 0.0)
  126. 705 case seconds_or_duration_or_algorithm
  127. when :exponentially_longer
  128. 96 delay = executions**4
  129. 96 delay_jitter = determine_jitter_for_delay(delay, jitter)
  130. 96 delay + delay_jitter + 2
  131. when ActiveSupport::Duration, Integer
  132. 561 delay = seconds_or_duration_or_algorithm.to_i
  133. 561 delay_jitter = determine_jitter_for_delay(delay, jitter)
  134. 561 delay + delay_jitter
  135. when Proc
  136. 48 algorithm = seconds_or_duration_or_algorithm
  137. 48 algorithm.call(executions)
  138. else
  139. raise "Couldn't determine a delay based on #{seconds_or_duration_or_algorithm.inspect}"
  140. end
  141. end
  142. 11 def determine_jitter_for_delay(delay, jitter)
  143. 657 return 0.0 if jitter.zero?
  144. 56 Kernel.rand * delay * jitter
  145. end
  146. 11 def executions_for(exceptions)
  147. 794 if exception_executions
  148. 746 exception_executions[exceptions.to_s] = (exception_executions[exceptions.to_s] || 0) + 1
  149. else
  150. # Guard against jobs that were persisted before we started having individual executions counters per retry_on
  151. 48 executions
  152. end
  153. end
  154. end
  155. end

lib/active_job/execution.rb

95.45% lines covered

22 relevant lines. 21 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "active_support/rescuable"
  3. 11 require "active_job/arguments"
  4. 11 module ActiveJob
  5. 11 module Execution
  6. 11 extend ActiveSupport::Concern
  7. 11 include ActiveSupport::Rescuable
  8. # Includes methods for executing and performing jobs instantly.
  9. 11 module ClassMethods
  10. # Performs the job immediately.
  11. #
  12. # MyJob.perform_now("mike")
  13. #
  14. 11 def perform_now(*args)
  15. 88 job_or_instantiate(*args).perform_now
  16. end
  17. 11 ruby2_keywords(:perform_now) if respond_to?(:ruby2_keywords, true)
  18. 11 def execute(job_data) #:nodoc:
  19. 1872 ActiveJob::Callbacks.run_callbacks(:execute) do
  20. 1872 job = deserialize(job_data)
  21. 1872 job.perform_now
  22. end
  23. end
  24. end
  25. # Performs the job immediately. The job is not sent to the queuing adapter
  26. # but directly executed by blocking the execution of others until it's finished.
  27. # `perform_now` returns the value of your job's `perform` method.
  28. #
  29. # class MyJob < ActiveJob::Base
  30. # def perform
  31. # "Hello World!"
  32. # end
  33. # end
  34. #
  35. # puts MyJob.new(*args).perform_now # => "Hello World!"
  36. 11 def perform_now
  37. # Guard against jobs that were persisted before we started counting executions by zeroing out nil counters
  38. 2862 self.executions = (executions || 0) + 1
  39. 2862 deserialize_arguments_if_needed
  40. 2800 run_callbacks :perform do
  41. 2734 perform(*arguments)
  42. end
  43. rescue => exception
  44. 895 rescue_with_handler(exception) || raise
  45. end
  46. 11 def perform(*)
  47. fail NotImplementedError
  48. end
  49. end
  50. end

lib/active_job/gem_version.rb

88.89% lines covered

9 relevant lines. 8 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. # Returns the version of the currently loaded Active Job as a <tt>Gem::Version</tt>
  4. 11 def self.gem_version
  5. Gem::Version.new VERSION::STRING
  6. end
  7. 11 module VERSION
  8. 11 MAJOR = 6
  9. 11 MINOR = 1
  10. 11 TINY = 0
  11. 11 PRE = "alpha"
  12. 11 STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
  13. end
  14. end

lib/active_job/instrumentation.rb

100.0% lines covered

20 relevant lines. 20 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module Instrumentation #:nodoc:
  4. 11 extend ActiveSupport::Concern
  5. 11 included do
  6. 11 around_enqueue do |_, block|
  7. 4802 scheduled_at ? instrument(:enqueue_at, &block) : instrument(:enqueue, &block)
  8. end
  9. 11 around_perform do |_, block|
  10. 2800 instrument :perform_start
  11. 2800 instrument :perform, &block
  12. end
  13. end
  14. 11 private
  15. 11 def instrument(operation, payload = {}, &block)
  16. 11253 enhanced_block = ->(event_payload) do
  17. 11253 block.call if block
  18. 9880 if defined?(@_halted_callback_hook_called) && @_halted_callback_hook_called
  19. 132 event_payload[:aborted] = true
  20. 132 @_halted_callback_hook_called = nil
  21. end
  22. end
  23. 11253 ActiveSupport::Notifications.instrument \
  24. "#{operation}.active_job", payload.merge(adapter: queue_adapter, job: self), &enhanced_block
  25. end
  26. 11 def halted_callback_hook(*)
  27. 132 super
  28. 132 @_halted_callback_hook_called = true
  29. end
  30. end
  31. end

lib/active_job/log_subscriber.rb

100.0% lines covered

77 relevant lines. 77 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "active_support/log_subscriber"
  3. 11 module ActiveJob
  4. 11 class LogSubscriber < ActiveSupport::LogSubscriber #:nodoc:
  5. 11 def enqueue(event)
  6. 3789 job = event.payload[:job]
  7. 3789 ex = event.payload[:exception_object]
  8. 3789 if ex
  9. 84 error do
  10. 69 "Failed enqueuing #{job.class.name} to #{queue_name(event)}: #{ex.class} (#{ex.message})"
  11. end
  12. 3705 elsif event.payload[:aborted]
  13. 66 info do
  14. 41 "Failed enqueuing #{job.class.name} to #{queue_name(event)}, a before_enqueue callback halted the enqueuing execution."
  15. end
  16. else
  17. 3639 info do
  18. 2402 "Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)}" + args_info(job)
  19. end
  20. end
  21. end
  22. 11 def enqueue_at(event)
  23. 1013 job = event.payload[:job]
  24. 1013 ex = event.payload[:exception_object]
  25. 1013 if ex
  26. 230 error do
  27. 185 "Failed enqueuing #{job.class.name} to #{queue_name(event)}: #{ex.class} (#{ex.message})"
  28. end
  29. 783 elsif event.payload[:aborted]
  30. 11 info do
  31. 11 "Failed enqueuing #{job.class.name} to #{queue_name(event)}, a before_enqueue callback halted the enqueuing execution."
  32. end
  33. else
  34. 772 info do
  35. 578 "Enqueued #{job.class.name} (Job ID: #{job.job_id}) to #{queue_name(event)} at #{scheduled_at(event)}" + args_info(job)
  36. end
  37. end
  38. end
  39. 11 def perform_start(event)
  40. 2800 info do
  41. 1849 job = event.payload[:job]
  42. 1849 "Performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} enqueued at #{job.enqueued_at}" + args_info(job)
  43. end
  44. end
  45. 11 def perform(event)
  46. 2800 job = event.payload[:job]
  47. 2800 ex = event.payload[:exception_object]
  48. 2800 if ex
  49. 844 error do
  50. 660 "Error performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} in #{event.duration.round(2)}ms: #{ex.class} (#{ex.message}):\n" + Array(ex.backtrace).join("\n")
  51. end
  52. 1956 elsif event.payload[:aborted]
  53. 55 error do
  54. 40 "Error performing #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} in #{event.duration.round(2)}ms: a before_perform callback halted the job execution"
  55. end
  56. else
  57. 1901 info do
  58. 1149 "Performed #{job.class.name} (Job ID: #{job.job_id}) from #{queue_name(event)} in #{event.duration.round(2)}ms"
  59. end
  60. end
  61. end
  62. 11 def enqueue_retry(event)
  63. 727 job = event.payload[:job]
  64. 727 ex = event.payload[:error]
  65. 727 wait = event.payload[:wait]
  66. 727 info do
  67. 567 if ex
  68. 549 "Retrying #{job.class} in #{wait.to_i} seconds, due to a #{ex.class}."
  69. else
  70. 18 "Retrying #{job.class} in #{wait.to_i} seconds."
  71. end
  72. end
  73. end
  74. 11 def retry_stopped(event)
  75. 89 job = event.payload[:job]
  76. 89 ex = event.payload[:error]
  77. 89 error do
  78. 70 "Stopped retrying #{job.class} due to a #{ex.class}, which reoccurred on #{job.executions} attempts."
  79. end
  80. end
  81. 11 def discard(event)
  82. 35 job = event.payload[:job]
  83. 35 ex = event.payload[:error]
  84. 35 error do
  85. 29 "Discarded #{job.class} due to a #{ex.class}."
  86. end
  87. end
  88. 11 private
  89. 11 def queue_name(event)
  90. 6984 event.payload[:adapter].class.name.demodulize.remove("Adapter") + "(#{event.payload[:job].queue_name})"
  91. end
  92. 11 def args_info(job)
  93. 4829 if job.class.log_arguments? && job.arguments.any?
  94. 3681 " with arguments: " +
  95. 5334 job.arguments.map { |arg| format(arg).inspect }.join(", ")
  96. else
  97. 1148 ""
  98. end
  99. end
  100. 11 def format(arg)
  101. 6230 case arg
  102. when Hash
  103. 500 arg.transform_values { |value| format(value) }
  104. when Array
  105. 843 arg.map { |value| format(value) }
  106. when GlobalID::Identification
  107. 162 arg.to_global_id rescue arg
  108. else
  109. 5621 arg
  110. end
  111. end
  112. 11 def scheduled_at(event)
  113. 578 Time.at(event.payload[:job].scheduled_at).utc
  114. end
  115. 11 def logger
  116. 45012 ActiveJob::Base.logger
  117. end
  118. end
  119. end
  120. 11 ActiveJob::LogSubscriber.attach_to :active_job

lib/active_job/logging.rb

100.0% lines covered

19 relevant lines. 19 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "active_support/core_ext/string/filters"
  3. 11 require "active_support/tagged_logging"
  4. 11 require "active_support/logger"
  5. 11 module ActiveJob
  6. 11 module Logging #:nodoc:
  7. 11 extend ActiveSupport::Concern
  8. 11 included do
  9. 11 cattr_accessor :logger, default: ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
  10. 11 class_attribute :log_arguments, instance_accessor: false, default: true
  11. 4813 around_enqueue { |_, block| tag_logger(&block) }
  12. 2811 around_perform { |job, block| tag_logger(job.class.name, job.job_id, &block) }
  13. end
  14. 11 private
  15. 11 def tag_logger(*tags)
  16. 7602 if logger.respond_to?(:tagged)
  17. 682 tags.unshift "ActiveJob" unless logger_tagged_by_active_job?
  18. 1364 logger.tagged(*tags) { yield }
  19. else
  20. 6920 yield
  21. end
  22. end
  23. 11 def logger_tagged_by_active_job?
  24. 682 logger.formatter.current_tags.include?("ActiveJob")
  25. end
  26. end
  27. end

lib/active_job/queue_adapter.rb

93.1% lines covered

29 relevant lines. 27 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "active_support/core_ext/string/inflections"
  3. 11 module ActiveJob
  4. # The <tt>ActiveJob::QueueAdapter</tt> module is used to load the
  5. # correct adapter. The default queue adapter is the +:async+ queue.
  6. 11 module QueueAdapter #:nodoc:
  7. 11 extend ActiveSupport::Concern
  8. 11 included do
  9. 11 class_attribute :_queue_adapter_name, instance_accessor: false, instance_predicate: false
  10. 11 class_attribute :_queue_adapter, instance_accessor: false, instance_predicate: false
  11. 11 delegate :queue_adapter, to: :class
  12. 11 self.queue_adapter = :async
  13. end
  14. # Includes the setter method for changing the active queue adapter.
  15. 11 module ClassMethods
  16. # Returns the backend queue provider. The default queue adapter
  17. # is the +:async+ queue. See QueueAdapters for more information.
  18. 11 def queue_adapter
  19. 4959 _queue_adapter
  20. end
  21. # Returns string denoting the name of the configured queue adapter.
  22. # By default returns +"async"+.
  23. 11 def queue_adapter_name
  24. 44 _queue_adapter_name
  25. end
  26. # Specify the backend queue provider. The default queue adapter
  27. # is the +:async+ queue. See QueueAdapters for more
  28. # information.
  29. 11 def queue_adapter=(name_or_adapter)
  30. 88 case name_or_adapter
  31. when Symbol, String
  32. 66 queue_adapter = ActiveJob::QueueAdapters.lookup(name_or_adapter).new
  33. 66 assign_adapter(name_or_adapter.to_s, queue_adapter)
  34. else
  35. 22 if queue_adapter?(name_or_adapter)
  36. adapter_name = "#{name_or_adapter.class.name.demodulize.remove('Adapter').underscore}"
  37. assign_adapter(adapter_name, name_or_adapter)
  38. else
  39. 22 raise ArgumentError
  40. end
  41. end
  42. end
  43. 11 private
  44. 11 def assign_adapter(adapter_name, queue_adapter)
  45. 66 self._queue_adapter_name = adapter_name
  46. 66 self._queue_adapter = queue_adapter
  47. end
  48. 11 QUEUE_ADAPTER_METHODS = [:enqueue, :enqueue_at].freeze
  49. 11 def queue_adapter?(object)
  50. 44 QUEUE_ADAPTER_METHODS.all? { |meth| object.respond_to?(meth) }
  51. end
  52. end
  53. end
  54. end

lib/active_job/queue_adapters.rb

100.0% lines covered

19 relevant lines. 19 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. # == Active Job adapters
  4. #
  5. # Active Job has adapters for the following queuing backends:
  6. #
  7. # * {Backburner}[https://github.com/nesquena/backburner]
  8. # * {Delayed Job}[https://github.com/collectiveidea/delayed_job]
  9. # * {Que}[https://github.com/chanks/que]
  10. # * {queue_classic}[https://github.com/QueueClassic/queue_classic]
  11. # * {Resque}[https://github.com/resque/resque]
  12. # * {Sidekiq}[https://sidekiq.org]
  13. # * {Sneakers}[https://github.com/jondot/sneakers]
  14. # * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch]
  15. # * {Active Job Async Job}[https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html]
  16. # * {Active Job Inline}[https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html]
  17. # * Please Note: We are not accepting pull requests for new adapters. See the {README}[link:files/activejob/README_md.html] for more details.
  18. #
  19. # === Backends Features
  20. #
  21. # | | Async | Queues | Delayed | Priorities | Timeout | Retries |
  22. # |-------------------|-------|--------|------------|------------|---------|---------|
  23. # | Backburner | Yes | Yes | Yes | Yes | Job | Global |
  24. # | Delayed Job | Yes | Yes | Yes | Job | Global | Global |
  25. # | Que | Yes | Yes | Yes | Job | No | Job |
  26. # | queue_classic | Yes | Yes | Yes* | No | No | No |
  27. # | Resque | Yes | Yes | Yes (Gem) | Queue | Global | Yes |
  28. # | Sidekiq | Yes | Yes | Yes | Queue | No | Job |
  29. # | Sneakers | Yes | Yes | No | Queue | Queue | No |
  30. # | Sucker Punch | Yes | Yes | Yes | No | No | No |
  31. # | Active Job Async | Yes | Yes | Yes | No | No | No |
  32. # | Active Job Inline | No | Yes | N/A | N/A | N/A | N/A |
  33. #
  34. # ==== Async
  35. #
  36. # Yes: The Queue Adapter has the ability to run the job in a non-blocking manner.
  37. # It either runs on a separate or forked process, or on a different thread.
  38. #
  39. # No: The job is run in the same process.
  40. #
  41. # ==== Queues
  42. #
  43. # Yes: Jobs may set which queue they are run in with queue_as or by using the set
  44. # method.
  45. #
  46. # ==== Delayed
  47. #
  48. # Yes: The adapter will run the job in the future through perform_later.
  49. #
  50. # (Gem): An additional gem is required to use perform_later with this adapter.
  51. #
  52. # No: The adapter will run jobs at the next opportunity and cannot use perform_later.
  53. #
  54. # N/A: The adapter does not support queuing.
  55. #
  56. # NOTE:
  57. # queue_classic supports job scheduling since version 3.1.
  58. # For older versions you can use the queue_classic-later gem.
  59. #
  60. # ==== Priorities
  61. #
  62. # The order in which jobs are processed can be configured differently depending
  63. # on the adapter.
  64. #
  65. # Job: Any class inheriting from the adapter may set the priority on the job
  66. # object relative to other jobs.
  67. #
  68. # Queue: The adapter can set the priority for job queues, when setting a queue
  69. # with Active Job this will be respected.
  70. #
  71. # Yes: Allows the priority to be set on the job object, at the queue level or
  72. # as default configuration option.
  73. #
  74. # No: The adapter does not allow the priority of jobs to be configured.
  75. #
  76. # N/A: The adapter does not support queuing, and therefore sorting them.
  77. #
  78. # ==== Timeout
  79. #
  80. # When a job will stop after the allotted time.
  81. #
  82. # Job: The timeout can be set for each instance of the job class.
  83. #
  84. # Queue: The timeout is set for all jobs on the queue.
  85. #
  86. # Global: The adapter is configured that all jobs have a maximum run time.
  87. #
  88. # No: The adapter does not allow the timeout of jobs to be configured.
  89. #
  90. # N/A: This adapter does not run in a separate process, and therefore timeout
  91. # is unsupported.
  92. #
  93. # ==== Retries
  94. #
  95. # Job: The number of retries can be set per instance of the job class.
  96. #
  97. # Yes: The Number of retries can be configured globally, for each instance or
  98. # on the queue. This adapter may also present failed instances of the job class
  99. # that can be restarted.
  100. #
  101. # Global: The adapter has a global number of retries.
  102. #
  103. # No: The adapter does not allow the number of retries to be configured.
  104. #
  105. # N/A: The adapter does not run in a separate process, and therefore doesn't
  106. # support retries.
  107. #
  108. # === Async and Inline Queue Adapters
  109. #
  110. # Active Job has two built-in queue adapters intended for development and
  111. # testing: +:async+ and +:inline+.
  112. 11 module QueueAdapters
  113. 11 extend ActiveSupport::Autoload
  114. 11 autoload :AsyncAdapter
  115. 11 autoload :InlineAdapter
  116. 11 autoload :BackburnerAdapter
  117. 11 autoload :DelayedJobAdapter
  118. 11 autoload :QueAdapter
  119. 11 autoload :QueueClassicAdapter
  120. 11 autoload :ResqueAdapter
  121. 11 autoload :SidekiqAdapter
  122. 11 autoload :SneakersAdapter
  123. 11 autoload :SuckerPunchAdapter
  124. 11 autoload :TestAdapter
  125. 11 ADAPTER = "Adapter"
  126. 11 private_constant :ADAPTER
  127. 11 class << self
  128. # Returns adapter for specified name.
  129. #
  130. # ActiveJob::QueueAdapters.lookup(:sidekiq)
  131. # # => ActiveJob::QueueAdapters::SidekiqAdapter
  132. 11 def lookup(name)
  133. 66 const_get(name.to_s.camelize << ADAPTER)
  134. end
  135. end
  136. end
  137. end

lib/active_job/queue_adapters/async_adapter.rb

90.48% lines covered

42 relevant lines. 38 lines covered and 4 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "securerandom"
  3. 11 require "concurrent/scheduled_task"
  4. 11 require "concurrent/executor/thread_pool_executor"
  5. 11 require "concurrent/utility/processor_counter"
  6. 11 module ActiveJob
  7. 11 module QueueAdapters
  8. # == Active Job Async adapter
  9. #
  10. # The Async adapter runs jobs with an in-process thread pool.
  11. #
  12. # This is the default queue adapter. It's well-suited for dev/test since
  13. # it doesn't need an external infrastructure, but it's a poor fit for
  14. # production since it drops pending jobs on restart.
  15. #
  16. # To use this adapter, set queue adapter to +:async+:
  17. #
  18. # config.active_job.queue_adapter = :async
  19. #
  20. # To configure the adapter's thread pool, instantiate the adapter and
  21. # pass your own config:
  22. #
  23. # config.active_job.queue_adapter = ActiveJob::QueueAdapters::AsyncAdapter.new \
  24. # min_threads: 1,
  25. # max_threads: 2 * Concurrent.processor_count,
  26. # idletime: 600.seconds
  27. #
  28. # The adapter uses a {Concurrent Ruby}[https://github.com/ruby-concurrency/concurrent-ruby] thread pool to schedule and execute
  29. # jobs. Since jobs share a single thread pool, long-running jobs will block
  30. # short-lived jobs. Fine for dev/test; bad for production.
  31. 11 class AsyncAdapter
  32. # See {Concurrent::ThreadPoolExecutor}[https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ThreadPoolExecutor.html] for executor options.
  33. 11 def initialize(**executor_options)
  34. 12 @scheduler = Scheduler.new(**executor_options)
  35. end
  36. 11 def enqueue(job) #:nodoc:
  37. 11 @scheduler.enqueue JobWrapper.new(job), queue_name: job.queue_name
  38. end
  39. 11 def enqueue_at(job, timestamp) #:nodoc:
  40. 2 @scheduler.enqueue_at JobWrapper.new(job), timestamp, queue_name: job.queue_name
  41. end
  42. # Gracefully stop processing jobs. Finishes in-progress work and handles
  43. # any new jobs following the executor's fallback policy (`caller_runs`).
  44. # Waits for termination by default. Pass `wait: false` to continue.
  45. 11 def shutdown(wait: true) #:nodoc:
  46. @scheduler.shutdown wait: wait
  47. end
  48. # Used for our test suite.
  49. 11 def immediate=(immediate) #:nodoc:
  50. 1 @scheduler.immediate = immediate
  51. end
  52. # Note that we don't actually need to serialize the jobs since we're
  53. # performing them in-process, but we do so anyway for parity with other
  54. # adapters and deployment environments. Otherwise, serialization bugs
  55. # may creep in undetected.
  56. 11 class JobWrapper #:nodoc:
  57. 11 def initialize(job)
  58. 13 job.provider_job_id = SecureRandom.uuid
  59. 13 @job_data = job.serialize
  60. end
  61. 11 def perform
  62. 13 Base.execute @job_data
  63. end
  64. end
  65. 11 class Scheduler #:nodoc:
  66. 11 DEFAULT_EXECUTOR_OPTIONS = {
  67. min_threads: 0,
  68. max_threads: Concurrent.processor_count,
  69. auto_terminate: true,
  70. idletime: 60, # 1 minute
  71. max_queue: 0, # unlimited
  72. fallback_policy: :caller_runs # shouldn't matter -- 0 max queue
  73. }.freeze
  74. 11 attr_accessor :immediate
  75. 11 def initialize(**options)
  76. 12 self.immediate = false
  77. 12 @immediate_executor = Concurrent::ImmediateExecutor.new
  78. 12 @async_executor = Concurrent::ThreadPoolExecutor.new(DEFAULT_EXECUTOR_OPTIONS.merge(options))
  79. end
  80. 11 def enqueue(job, queue_name:)
  81. 13 executor.post(job, &:perform)
  82. end
  83. 11 def enqueue_at(job, timestamp, queue_name:)
  84. 2 delay = timestamp - Time.current.to_f
  85. 2 if delay > 0
  86. Concurrent::ScheduledTask.execute(delay, args: [job], executor: executor, &:perform)
  87. else
  88. 2 enqueue(job, queue_name: queue_name)
  89. end
  90. end
  91. 11 def shutdown(wait: true)
  92. @async_executor.shutdown
  93. @async_executor.wait_for_termination if wait
  94. end
  95. 11 def executor
  96. 13 immediate ? @immediate_executor : @async_executor
  97. end
  98. end
  99. end
  100. end
  101. end

lib/active_job/queue_adapters/backburner_adapter.rb

100.0% lines covered

13 relevant lines. 13 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "backburner"
  3. 1 module ActiveJob
  4. 1 module QueueAdapters
  5. # == Backburner adapter for Active Job
  6. #
  7. # Backburner is a beanstalkd-powered job queue that can handle a very
  8. # high volume of jobs. You create background jobs and place them on
  9. # multiple work queues to be processed later. Read more about
  10. # Backburner {here}[https://github.com/nesquena/backburner].
  11. #
  12. # To use Backburner set the queue_adapter config to +:backburner+.
  13. #
  14. # Rails.application.config.active_job.queue_adapter = :backburner
  15. 1 class BackburnerAdapter
  16. 1 def enqueue(job) #:nodoc:
  17. 35 Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority)
  18. end
  19. 1 def enqueue_at(job, timestamp) #:nodoc:
  20. 75 delay = timestamp - Time.current.to_f
  21. 75 Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority, delay: delay)
  22. end
  23. 1 class JobWrapper #:nodoc:
  24. 1 class << self
  25. 1 def perform(job_data)
  26. 110 Base.execute job_data
  27. end
  28. end
  29. end
  30. end
  31. end
  32. end

lib/active_job/queue_adapters/delayed_job_adapter.rb

95.0% lines covered

20 relevant lines. 19 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "delayed_job"
  3. 1 module ActiveJob
  4. 1 module QueueAdapters
  5. # == Delayed Job adapter for Active Job
  6. #
  7. # Delayed::Job (or DJ) encapsulates the common pattern of asynchronously
  8. # executing longer tasks in the background. Although DJ can have many
  9. # storage backends, one of the most used is based on Active Record.
  10. # Read more about Delayed Job {here}[https://github.com/collectiveidea/delayed_job].
  11. #
  12. # To use Delayed Job, set the queue_adapter config to +:delayed_job+.
  13. #
  14. # Rails.application.config.active_job.queue_adapter = :delayed_job
  15. 1 class DelayedJobAdapter
  16. 1 def enqueue(job) #:nodoc:
  17. 35 delayed_job = Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name, priority: job.priority)
  18. 30 job.provider_job_id = delayed_job.id
  19. 30 delayed_job
  20. end
  21. 1 def enqueue_at(job, timestamp) #:nodoc:
  22. 75 delayed_job = Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name, priority: job.priority, run_at: Time.at(timestamp))
  23. 55 job.provider_job_id = delayed_job.id
  24. 55 delayed_job
  25. end
  26. 1 class JobWrapper #:nodoc:
  27. 1 attr_accessor :job_data
  28. 1 def initialize(job_data)
  29. 110 @job_data = job_data
  30. end
  31. 1 def display_name
  32. "#{job_data['job_class']} [#{job_data['job_id']}] from DelayedJob(#{job_data['queue_name']}) with arguments: #{job_data['arguments']}"
  33. end
  34. 1 def perform
  35. 110 Base.execute(job_data)
  36. end
  37. end
  38. end
  39. end
  40. end

lib/active_job/queue_adapters/inline_adapter.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module QueueAdapters
  4. # == Active Job Inline adapter
  5. #
  6. # When enqueuing jobs with the Inline adapter the job will be executed
  7. # immediately.
  8. #
  9. # To use the Inline set the queue_adapter config to +:inline+.
  10. #
  11. # Rails.application.config.active_job.queue_adapter = :inline
  12. 11 class InlineAdapter
  13. 11 def enqueue(job) #:nodoc:
  14. 22 Thread.new { Base.execute(job.serialize) }.join
  15. end
  16. 11 def enqueue_at(*) #:nodoc:
  17. 2 raise NotImplementedError, "Use a queueing backend to enqueue jobs in the future. Read more at https://guides.rubyonrails.org/active_job_basics.html"
  18. end
  19. end
  20. end
  21. end

lib/active_job/queue_adapters/que_adapter.rb

100.0% lines covered

15 relevant lines. 15 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "que"
  3. 1 module ActiveJob
  4. 1 module QueueAdapters
  5. # == Que adapter for Active Job
  6. #
  7. # Que is a high-performance alternative to DelayedJob or QueueClassic that
  8. # improves the reliability of your application by protecting your jobs with
  9. # the same ACID guarantees as the rest of your data. Que is a queue for
  10. # Ruby and PostgreSQL that manages jobs using advisory locks.
  11. #
  12. # Read more about Que {here}[https://github.com/chanks/que].
  13. #
  14. # To use Que set the queue_adapter config to +:que+.
  15. #
  16. # Rails.application.config.active_job.queue_adapter = :que
  17. 1 class QueAdapter
  18. 1 def enqueue(job) #:nodoc:
  19. 35 que_job = JobWrapper.enqueue job.serialize, priority: job.priority, queue: job.queue_name
  20. 30 job.provider_job_id = que_job.attrs["job_id"]
  21. 30 que_job
  22. end
  23. 1 def enqueue_at(job, timestamp) #:nodoc:
  24. 75 que_job = JobWrapper.enqueue job.serialize, priority: job.priority, queue: job.queue_name, run_at: Time.at(timestamp)
  25. 55 job.provider_job_id = que_job.attrs["job_id"]
  26. 55 que_job
  27. end
  28. 1 class JobWrapper < Que::Job #:nodoc:
  29. 1 def run(job_data)
  30. 110 Base.execute job_data
  31. end
  32. end
  33. end
  34. end
  35. end

lib/active_job/queue_adapters/queue_classic_adapter.rb

95.24% lines covered

21 relevant lines. 20 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "queue_classic"
  3. 1 module ActiveJob
  4. 1 module QueueAdapters
  5. # == queue_classic adapter for Active Job
  6. #
  7. # queue_classic provides a simple interface to a PostgreSQL-backed message
  8. # queue. queue_classic specializes in concurrent locking and minimizing
  9. # database load while providing a simple, intuitive developer experience.
  10. # queue_classic assumes that you are already using PostgreSQL in your
  11. # production environment and that adding another dependency (e.g. redis,
  12. # beanstalkd, 0mq) is undesirable.
  13. #
  14. # Read more about queue_classic {here}[https://github.com/QueueClassic/queue_classic].
  15. #
  16. # To use queue_classic set the queue_adapter config to +:queue_classic+.
  17. #
  18. # Rails.application.config.active_job.queue_adapter = :queue_classic
  19. 1 class QueueClassicAdapter
  20. 1 def enqueue(job) #:nodoc:
  21. 35 qc_job = build_queue(job.queue_name).enqueue("#{JobWrapper.name}.perform", job.serialize)
  22. 30 job.provider_job_id = qc_job["id"] if qc_job.is_a?(Hash)
  23. 30 qc_job
  24. end
  25. 1 def enqueue_at(job, timestamp) #:nodoc:
  26. 75 queue = build_queue(job.queue_name)
  27. 75 unless queue.respond_to?(:enqueue_at)
  28. raise NotImplementedError, "To be able to schedule jobs with queue_classic " \
  29. "the QC::Queue needs to respond to `enqueue_at(timestamp, method, *args)`. " \
  30. "You can implement this yourself or you can use the queue_classic-later gem."
  31. end
  32. 75 qc_job = queue.enqueue_at(timestamp, "#{JobWrapper.name}.perform", job.serialize)
  33. 55 job.provider_job_id = qc_job["id"] if qc_job.is_a?(Hash)
  34. 55 qc_job
  35. end
  36. # Builds a <tt>QC::Queue</tt> object to schedule jobs on.
  37. #
  38. # If you have a custom <tt>QC::Queue</tt> subclass you'll need to subclass
  39. # <tt>ActiveJob::QueueAdapters::QueueClassicAdapter</tt> and override the
  40. # <tt>build_queue</tt> method.
  41. 1 def build_queue(queue_name)
  42. 110 QC::Queue.new(queue_name)
  43. end
  44. 1 class JobWrapper #:nodoc:
  45. 1 class << self
  46. 1 def perform(job_data)
  47. 110 Base.execute job_data
  48. end
  49. end
  50. end
  51. end
  52. end
  53. end

lib/active_job/queue_adapters/resque_adapter.rb

81.82% lines covered

22 relevant lines. 18 lines covered and 4 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "resque"
  3. 1 require "active_support/core_ext/enumerable"
  4. 1 require "active_support/core_ext/array/access"
  5. 1 begin
  6. 1 require "resque-scheduler"
  7. rescue LoadError
  8. begin
  9. require "resque_scheduler"
  10. rescue LoadError
  11. false
  12. end
  13. end
  14. 1 module ActiveJob
  15. 1 module QueueAdapters
  16. # == Resque adapter for Active Job
  17. #
  18. # Resque (pronounced like "rescue") is a Redis-backed library for creating
  19. # background jobs, placing those jobs on multiple queues, and processing
  20. # them later.
  21. #
  22. # Read more about Resque {here}[https://github.com/resque/resque].
  23. #
  24. # To use Resque set the queue_adapter config to +:resque+.
  25. #
  26. # Rails.application.config.active_job.queue_adapter = :resque
  27. 1 class ResqueAdapter
  28. 1 def enqueue(job) #:nodoc:
  29. 35 JobWrapper.instance_variable_set(:@queue, job.queue_name)
  30. 35 Resque.enqueue_to job.queue_name, JobWrapper, job.serialize
  31. end
  32. 1 def enqueue_at(job, timestamp) #:nodoc:
  33. 75 unless Resque.respond_to?(:enqueue_at_with_queue)
  34. raise NotImplementedError, "To be able to schedule jobs with Resque you need the " \
  35. "resque-scheduler gem. Please add it to your Gemfile and run bundle install"
  36. end
  37. 75 Resque.enqueue_at_with_queue job.queue_name, timestamp, JobWrapper, job.serialize
  38. end
  39. 1 class JobWrapper #:nodoc:
  40. 1 class << self
  41. 1 def perform(job_data)
  42. 110 Base.execute job_data
  43. end
  44. end
  45. end
  46. end
  47. end
  48. end

lib/active_job/queue_adapters/sidekiq_adapter.rb

100.0% lines covered

12 relevant lines. 12 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "sidekiq"
  3. 1 module ActiveJob
  4. 1 module QueueAdapters
  5. # == Sidekiq adapter for Active Job
  6. #
  7. # Simple, efficient background processing for Ruby. Sidekiq uses threads to
  8. # handle many jobs at the same time in the same process. It does not
  9. # require Rails but will integrate tightly with it to make background
  10. # processing dead simple.
  11. #
  12. # Read more about Sidekiq {here}[http://sidekiq.org].
  13. #
  14. # To use Sidekiq set the queue_adapter config to +:sidekiq+.
  15. #
  16. # Rails.application.config.active_job.queue_adapter = :sidekiq
  17. 1 class SidekiqAdapter
  18. 1 def enqueue(job) #:nodoc:
  19. # Sidekiq::Client does not support symbols as keys
  20. 35 job.provider_job_id = Sidekiq::Client.push \
  21. "class" => JobWrapper,
  22. "wrapped" => job.class,
  23. "queue" => job.queue_name,
  24. "args" => [ job.serialize ]
  25. end
  26. 1 def enqueue_at(job, timestamp) #:nodoc:
  27. 75 job.provider_job_id = Sidekiq::Client.push \
  28. "class" => JobWrapper,
  29. "wrapped" => job.class,
  30. "queue" => job.queue_name,
  31. "args" => [ job.serialize ],
  32. "at" => timestamp
  33. end
  34. 1 class JobWrapper #:nodoc:
  35. 1 include Sidekiq::Worker
  36. 1 def perform(job_data)
  37. 110 Base.execute job_data.merge("provider_job_id" => jid)
  38. end
  39. end
  40. end
  41. end
  42. end

lib/active_job/queue_adapters/sneakers_adapter.rb

100.0% lines covered

20 relevant lines. 20 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "sneakers"
  3. 11 require "monitor"
  4. 11 module ActiveJob
  5. 11 module QueueAdapters
  6. # == Sneakers adapter for Active Job
  7. #
  8. # A high-performance RabbitMQ background processing framework for Ruby.
  9. # Sneakers is being used in production for both I/O and CPU intensive
  10. # workloads, and have achieved the goals of high-performance and
  11. # 0-maintenance, as designed.
  12. #
  13. # Read more about Sneakers {here}[https://github.com/jondot/sneakers].
  14. #
  15. # To use Sneakers set the queue_adapter config to +:sneakers+.
  16. #
  17. # Rails.application.config.active_job.queue_adapter = :sneakers
  18. 11 class SneakersAdapter
  19. 11 def initialize
  20. 1 @monitor = Monitor.new
  21. end
  22. 11 def enqueue(job) #:nodoc:
  23. 11 @monitor.synchronize do
  24. 11 JobWrapper.from_queue job.queue_name
  25. 11 JobWrapper.enqueue ActiveSupport::JSON.encode(job.serialize)
  26. end
  27. end
  28. 11 def enqueue_at(job, timestamp) #:nodoc:
  29. 2 raise NotImplementedError, "This queueing backend does not support scheduling jobs. To see what features are supported go to http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html"
  30. end
  31. 11 class JobWrapper #:nodoc:
  32. 11 include Sneakers::Worker
  33. 11 from_queue "default"
  34. 11 def work(msg)
  35. 11 job_data = ActiveSupport::JSON.decode(msg)
  36. 11 Base.execute job_data
  37. 11 ack!
  38. end
  39. end
  40. end
  41. end
  42. end

lib/active_job/queue_adapters/sucker_punch_adapter.rb

88.24% lines covered

17 relevant lines. 15 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 1 require "sucker_punch"
  3. 1 module ActiveJob
  4. 1 module QueueAdapters
  5. # == Sucker Punch adapter for Active Job
  6. #
  7. # Sucker Punch is a single-process Ruby asynchronous processing library.
  8. # This reduces the cost of hosting on a service like Heroku along
  9. # with the memory footprint of having to maintain additional jobs if
  10. # hosting on a dedicated server. All queues can run within a
  11. # single application (e.g. Rails, Sinatra, etc.) process.
  12. #
  13. # Read more about Sucker Punch {here}[https://github.com/brandonhilkert/sucker_punch].
  14. #
  15. # To use Sucker Punch set the queue_adapter config to +:sucker_punch+.
  16. #
  17. # Rails.application.config.active_job.queue_adapter = :sucker_punch
  18. 1 class SuckerPunchAdapter
  19. 1 def enqueue(job) #:nodoc:
  20. 35 if JobWrapper.respond_to?(:perform_async)
  21. # sucker_punch 2.0 API
  22. 35 JobWrapper.perform_async job.serialize
  23. else
  24. # sucker_punch 1.0 API
  25. JobWrapper.new.async.perform job.serialize
  26. end
  27. end
  28. 1 def enqueue_at(job, timestamp) #:nodoc:
  29. 75 if JobWrapper.respond_to?(:perform_in)
  30. 75 delay = timestamp - Time.current.to_f
  31. 75 JobWrapper.perform_in delay, job.serialize
  32. else
  33. raise NotImplementedError, "sucker_punch 1.0 does not support `enqueued_at`. Please upgrade to version ~> 2.0.0 to enable this behavior."
  34. end
  35. end
  36. 1 class JobWrapper #:nodoc:
  37. 1 include SuckerPunch::Job
  38. 1 def perform(job_data)
  39. 110 Base.execute job_data
  40. end
  41. end
  42. end
  43. end
  44. end

lib/active_job/queue_adapters/test_adapter.rb

100.0% lines covered

41 relevant lines. 41 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module QueueAdapters
  4. # == Test adapter for Active Job
  5. #
  6. # The test adapter should be used only in testing. Along with
  7. # <tt>ActiveJob::TestCase</tt> and <tt>ActiveJob::TestHelper</tt>
  8. # it makes a great tool to test your Rails application.
  9. #
  10. # To use the test adapter set queue_adapter config to +:test+.
  11. #
  12. # Rails.application.config.active_job.queue_adapter = :test
  13. 11 class TestAdapter
  14. 11 attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue, :at)
  15. 11 attr_writer(:enqueued_jobs, :performed_jobs)
  16. # Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
  17. 11 def enqueued_jobs
  18. 8250 @enqueued_jobs ||= []
  19. end
  20. # Provides a store of all the performed jobs with the TestAdapter so you can check them.
  21. 11 def performed_jobs
  22. 6523 @performed_jobs ||= []
  23. end
  24. 11 def enqueue(job) #:nodoc:
  25. 3423 job_data = job_to_hash(job)
  26. 3423 perform_or_enqueue(perform_enqueued_jobs && !filtered?(job), job, job_data)
  27. end
  28. 11 def enqueue_at(job, timestamp) #:nodoc:
  29. 460 job_data = job_to_hash(job, at: timestamp)
  30. 460 perform_or_enqueue(perform_enqueued_at_jobs && !filtered?(job), job, job_data)
  31. end
  32. 11 private
  33. 11 def job_to_hash(job, extras = {})
  34. job.serialize.tap do |job_data|
  35. 3883 job_data[:job] = job.class
  36. 3883 job_data[:args] = job_data.fetch("arguments")
  37. 3883 job_data[:queue] = job_data.fetch("queue_name")
  38. 3883 end.merge(extras)
  39. end
  40. 11 def perform_or_enqueue(perform, job, job_data)
  41. 3883 if perform
  42. 1067 performed_jobs << job_data
  43. 1067 Base.execute(job.serialize)
  44. else
  45. 2816 enqueued_jobs << job_data
  46. end
  47. end
  48. 11 def filtered?(job)
  49. 1694 filtered_queue?(job) || filtered_job_class?(job) || filtered_time?(job)
  50. end
  51. 11 def filtered_time?(job)
  52. 1078 job.scheduled_at > at.to_f if at && job.scheduled_at
  53. end
  54. 11 def filtered_queue?(job)
  55. 1694 if queue
  56. 407 job.queue_name != queue.to_s
  57. end
  58. end
  59. 11 def filtered_job_class?(job)
  60. 1496 if filter
  61. 385 !filter_as_proc(filter).call(job)
  62. 1111 elsif reject
  63. 330 filter_as_proc(reject).call(job)
  64. end
  65. end
  66. 11 def filter_as_proc(filter)
  67. 715 return filter if filter.is_a?(Proc)
  68. 1342 ->(job) { Array(filter).include?(job.class) }
  69. end
  70. end
  71. end
  72. end

lib/active_job/queue_name.rb

100.0% lines covered

21 relevant lines. 21 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module QueueName
  4. 11 extend ActiveSupport::Concern
  5. # Includes the ability to override the default queue name and prefix.
  6. 11 module ClassMethods
  7. 11 mattr_accessor :default_queue_name, default: "default"
  8. # Specifies the name of the queue to process the job on.
  9. #
  10. # class PublishToFeedJob < ActiveJob::Base
  11. # queue_as :feeds
  12. #
  13. # def perform(post)
  14. # post.to_feed!
  15. # end
  16. # end
  17. #
  18. # Can be given a block that will evaluate in the context of the job
  19. # allowing +self.arguments+ to be accessed so that a dynamic queue name
  20. # can be applied:
  21. #
  22. # class PublishToFeedJob < ApplicationJob
  23. # queue_as do
  24. # post = self.arguments.first
  25. #
  26. # if post.paid?
  27. # :paid_feeds
  28. # else
  29. # :feeds
  30. # end
  31. # end
  32. #
  33. # def perform(post)
  34. # post.to_feed!
  35. # end
  36. # end
  37. 11 def queue_as(part_name = nil, &block)
  38. 88 if block_given?
  39. 22 self.queue_name = block
  40. else
  41. 66 self.queue_name = queue_name_from_part(part_name)
  42. end
  43. end
  44. 11 def queue_name_from_part(part_name) #:nodoc:
  45. 4355 queue_name = part_name || default_queue_name
  46. 4355 name_parts = [queue_name_prefix.presence, queue_name]
  47. 4355 -name_parts.compact.join(queue_name_delimiter)
  48. end
  49. end
  50. 11 included do
  51. 3189 class_attribute :queue_name, instance_accessor: false, default: -> { self.class.default_queue_name }
  52. 11 class_attribute :queue_name_delimiter, instance_accessor: false, default: "_"
  53. 11 class_attribute :queue_name_prefix
  54. end
  55. # Returns the name of the queue the job will be run on.
  56. 11 def queue_name
  57. 14151 if @queue_name.is_a?(Proc)
  58. 3211 @queue_name = self.class.queue_name_from_part(instance_exec(&@queue_name))
  59. end
  60. 14151 @queue_name
  61. end
  62. end
  63. end

lib/active_job/queue_priority.rb

100.0% lines covered

15 relevant lines. 15 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module QueuePriority
  4. 11 extend ActiveSupport::Concern
  5. # Includes the ability to override the default queue priority.
  6. 11 module ClassMethods
  7. 11 mattr_accessor :default_priority
  8. # Specifies the priority of the queue to create the job with.
  9. #
  10. # class PublishToFeedJob < ActiveJob::Base
  11. # queue_with_priority 50
  12. #
  13. # def perform(post)
  14. # post.to_feed!
  15. # end
  16. # end
  17. #
  18. # Specify either an argument or a block.
  19. 11 def queue_with_priority(priority = nil, &block)
  20. 33 if block_given?
  21. 22 self.priority = block
  22. else
  23. 11 self.priority = priority
  24. end
  25. end
  26. end
  27. 11 included do
  28. 11 class_attribute :priority, instance_accessor: false, default: default_priority
  29. end
  30. # Returns the priority that the job will be created with
  31. 11 def priority
  32. 6272 if @priority.is_a?(Proc)
  33. 33 @priority = instance_exec(&@priority)
  34. end
  35. 6272 @priority
  36. end
  37. end
  38. end

lib/active_job/railtie.rb

0.0% lines covered

39 relevant lines. 0 lines covered and 39 lines missed.
    
  1. # frozen_string_literal: true
  2. require "global_id/railtie"
  3. require "active_job"
  4. module ActiveJob
  5. # = Active Job Railtie
  6. class Railtie < Rails::Railtie # :nodoc:
  7. config.active_job = ActiveSupport::OrderedOptions.new
  8. config.active_job.custom_serializers = []
  9. initializer "active_job.logger" do
  10. ActiveSupport.on_load(:active_job) { self.logger = ::Rails.logger }
  11. end
  12. initializer "active_job.custom_serializers" do |app|
  13. config.after_initialize do
  14. custom_serializers = app.config.active_job.delete(:custom_serializers)
  15. ActiveJob::Serializers.add_serializers custom_serializers
  16. end
  17. end
  18. initializer "active_job.set_configs" do |app|
  19. options = app.config.active_job
  20. options.queue_adapter ||= :async
  21. ActiveSupport.on_load(:active_job) do
  22. options.each do |k, v|
  23. k = "#{k}="
  24. send(k, v) if respond_to? k
  25. end
  26. end
  27. ActiveSupport.on_load(:action_dispatch_integration_test) do
  28. include ActiveJob::TestHelper
  29. end
  30. end
  31. initializer "active_job.set_reloader_hook" do |app|
  32. ActiveSupport.on_load(:active_job) do
  33. ActiveJob::Callbacks.singleton_class.set_callback(:execute, :around, prepend: true) do |_, inner|
  34. app.reloader.wrap do
  35. inner.call
  36. end
  37. end
  38. end
  39. end
  40. end
  41. end

lib/active_job/serializers.rb

100.0% lines covered

30 relevant lines. 30 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "set"
  3. 11 module ActiveJob
  4. # The <tt>ActiveJob::Serializers</tt> module is used to store a list of known serializers
  5. # and to add new ones. It also has helpers to serialize/deserialize objects.
  6. 11 module Serializers # :nodoc:
  7. 11 extend ActiveSupport::Autoload
  8. 11 autoload :ObjectSerializer
  9. 11 autoload :SymbolSerializer
  10. 11 autoload :DurationSerializer
  11. 11 autoload :DateTimeSerializer
  12. 11 autoload :DateSerializer
  13. 11 autoload :TimeWithZoneSerializer
  14. 11 autoload :TimeSerializer
  15. 11 autoload :ModuleSerializer
  16. 11 mattr_accessor :_additional_serializers
  17. 11 self._additional_serializers = Set.new
  18. 11 class << self
  19. # Returns serialized representative of the passed object.
  20. # Will look up through all known serializers.
  21. # Raises <tt>ActiveJob::SerializationError</tt> if it can't find a proper serializer.
  22. 11 def serialize(argument)
  23. 2867 serializer = serializers.detect { |s| s.serialize?(argument) }
  24. 702 raise SerializationError.new("Unsupported argument type: #{argument.class.name}") unless serializer
  25. 647 serializer.serialize(argument)
  26. end
  27. # Returns deserialized object.
  28. # Will look up through all known serializers.
  29. # If no serializer found will raise <tt>ArgumentError</tt>.
  30. 11 def deserialize(argument)
  31. 767 serializer_name = argument[Arguments::OBJECT_SERIALIZER_KEY]
  32. 767 raise ArgumentError, "Serializer name is not present in the argument: #{argument.inspect}" unless serializer_name
  33. 756 serializer = serializer_name.safe_constantize
  34. 756 raise ArgumentError, "Serializer #{serializer_name} is not known" unless serializer
  35. 745 serializer.deserialize(argument)
  36. end
  37. # Returns list of known serializers.
  38. 11 def serializers
  39. 812 self._additional_serializers
  40. end
  41. # Adds new serializers to a list of known serializers.
  42. 11 def add_serializers(*new_serializers)
  43. 66 self._additional_serializers += new_serializers.flatten
  44. end
  45. end
  46. 11 add_serializers SymbolSerializer,
  47. DurationSerializer,
  48. DateTimeSerializer,
  49. DateSerializer,
  50. TimeWithZoneSerializer,
  51. TimeSerializer,
  52. ModuleSerializer
  53. end
  54. end

lib/active_job/serializers/date_serializer.rb

100.0% lines covered

10 relevant lines. 10 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module Serializers
  4. 11 class DateSerializer < ObjectSerializer # :nodoc:
  5. 11 def serialize(date)
  6. 11 super("value" => date.iso8601)
  7. end
  8. 11 def deserialize(hash)
  9. 11 Date.iso8601(hash["value"])
  10. end
  11. 11 private
  12. 11 def klass
  13. 264 Date
  14. end
  15. end
  16. end
  17. end

lib/active_job/serializers/date_time_serializer.rb

100.0% lines covered

10 relevant lines. 10 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module Serializers
  4. 11 class DateTimeSerializer < ObjectSerializer # :nodoc:
  5. 11 def serialize(time)
  6. 66 super("value" => time.iso8601)
  7. end
  8. 11 def deserialize(hash)
  9. 99 DateTime.iso8601(hash["value"])
  10. end
  11. 11 private
  12. 11 def klass
  13. 330 DateTime
  14. end
  15. end
  16. end
  17. end

lib/active_job/serializers/duration_serializer.rb

100.0% lines covered

12 relevant lines. 12 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module Serializers
  4. 11 class DurationSerializer < ObjectSerializer # :nodoc:
  5. 11 def serialize(duration)
  6. 11 super("value" => duration.value, "parts" => Arguments.serialize(duration.parts))
  7. end
  8. 11 def deserialize(hash)
  9. 11 value = hash["value"]
  10. 11 parts = Arguments.deserialize(hash["parts"])
  11. 11 klass.new(value, parts)
  12. end
  13. 11 private
  14. 11 def klass
  15. 352 ActiveSupport::Duration
  16. end
  17. end
  18. end
  19. end

lib/active_job/serializers/module_serializer.rb

100.0% lines covered

10 relevant lines. 10 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module Serializers
  4. 11 class ModuleSerializer < ObjectSerializer # :nodoc:
  5. 11 def serialize(constant)
  6. 33 super("value" => constant.name)
  7. end
  8. 11 def deserialize(hash)
  9. 33 hash["value"].constantize
  10. end
  11. 11 private
  12. 11 def klass
  13. 99 Module
  14. end
  15. end
  16. end
  17. end

lib/active_job/serializers/object_serializer.rb

86.67% lines covered

15 relevant lines. 13 lines covered and 2 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module Serializers
  4. # Base class for serializing and deserializing custom objects.
  5. #
  6. # Example:
  7. #
  8. # class MoneySerializer < ActiveJob::Serializers::ObjectSerializer
  9. # def serialize(money)
  10. # super("amount" => money.amount, "currency" => money.currency)
  11. # end
  12. #
  13. # def deserialize(hash)
  14. # Money.new(hash["amount"], hash["currency"])
  15. # end
  16. #
  17. # private
  18. #
  19. # def klass
  20. # Money
  21. # end
  22. # end
  23. 11 class ObjectSerializer
  24. 11 include Singleton
  25. 11 class << self
  26. 11 delegate :serialize?, :serialize, :deserialize, to: :instance
  27. end
  28. # Determines if an argument should be serialized by a serializer.
  29. 11 def serialize?(argument)
  30. 2165 argument.is_a?(klass)
  31. end
  32. # Serializes an argument to a JSON primitive type.
  33. 11 def serialize(hash)
  34. 647 { Arguments::OBJECT_SERIALIZER_KEY => self.class.name }.merge!(hash)
  35. end
  36. # Deserializes an argument from a JSON primitive type.
  37. 11 def deserialize(json)
  38. raise NotImplementedError
  39. end
  40. 11 private
  41. # The class of the object that will be serialized.
  42. 11 def klass # :doc:
  43. raise NotImplementedError
  44. end
  45. end
  46. end
  47. end

lib/active_job/serializers/symbol_serializer.rb

100.0% lines covered

10 relevant lines. 10 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module Serializers
  4. 11 class SymbolSerializer < ObjectSerializer # :nodoc:
  5. 11 def serialize(argument)
  6. 361 super("value" => argument.to_s)
  7. end
  8. 11 def deserialize(argument)
  9. 360 argument["value"].to_sym
  10. end
  11. 11 private
  12. 11 def klass
  13. 702 Symbol
  14. end
  15. end
  16. end
  17. end

lib/active_job/serializers/time_serializer.rb

100.0% lines covered

10 relevant lines. 10 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module Serializers
  4. 11 class TimeSerializer < ObjectSerializer # :nodoc:
  5. 11 def serialize(time)
  6. 66 super("value" => time.iso8601)
  7. end
  8. 11 def deserialize(hash)
  9. 99 Time.iso8601(hash["value"])
  10. end
  11. 11 private
  12. 11 def klass
  13. 165 Time
  14. end
  15. end
  16. end
  17. end

lib/active_job/serializers/time_with_zone_serializer.rb

100.0% lines covered

10 relevant lines. 10 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module Serializers
  4. 11 class TimeWithZoneSerializer < ObjectSerializer # :nodoc:
  5. 11 def serialize(time)
  6. 88 super("value" => time.iso8601)
  7. end
  8. 11 def deserialize(hash)
  9. 121 Time.iso8601(hash["value"]).in_time_zone
  10. end
  11. 11 private
  12. 11 def klass
  13. 253 ActiveSupport::TimeWithZone
  14. end
  15. end
  16. end
  17. end

lib/active_job/test_case.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "active_support/test_case"
  3. 11 module ActiveJob
  4. 11 class TestCase < ActiveSupport::TestCase
  5. 11 include ActiveJob::TestHelper
  6. 11 ActiveSupport.run_load_hooks(:active_job_test_case, self)
  7. end
  8. end

lib/active_job/test_helper.rb

100.0% lines covered

170 relevant lines. 170 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require "active_support/core_ext/class/subclasses"
  3. 11 module ActiveJob
  4. # Provides helper methods for testing Active Job
  5. 11 module TestHelper
  6. 11 delegate :enqueued_jobs, :enqueued_jobs=,
  7. :performed_jobs, :performed_jobs=,
  8. to: :queue_adapter
  9. 11 module TestQueueAdapter
  10. 11 extend ActiveSupport::Concern
  11. 11 included do
  12. 11 class_attribute :_test_adapter, instance_accessor: false, instance_predicate: false
  13. end
  14. 11 module ClassMethods
  15. 11 def queue_adapter
  16. 44028 self._test_adapter.nil? ? super : self._test_adapter
  17. end
  18. 11 def disable_test_adapter
  19. 6375 self._test_adapter = nil
  20. end
  21. 11 def enable_test_adapter(test_adapter)
  22. 6335 self._test_adapter = test_adapter
  23. end
  24. end
  25. end
  26. 11 ActiveJob::Base.include(TestQueueAdapter)
  27. 11 def before_setup # :nodoc:
  28. 2464 test_adapter = queue_adapter_for_test
  29. 2464 queue_adapter_changed_jobs.each do |klass|
  30. 6335 klass.enable_test_adapter(test_adapter)
  31. end
  32. 2464 clear_enqueued_jobs
  33. 2464 clear_performed_jobs
  34. 2464 super
  35. end
  36. 11 def after_teardown # :nodoc:
  37. 2464 super
  38. 8828 queue_adapter_changed_jobs.each { |klass| klass.disable_test_adapter }
  39. end
  40. # Specifies the queue adapter to use with all Active Job test helpers.
  41. #
  42. # Returns an instance of the queue adapter and defaults to
  43. # <tt>ActiveJob::QueueAdapters::TestAdapter</tt>.
  44. #
  45. # Note: The adapter provided by this method must provide some additional
  46. # methods from those expected of a standard <tt>ActiveJob::QueueAdapter</tt>
  47. # in order to be used with the active job test helpers. Refer to
  48. # <tt>ActiveJob::QueueAdapters::TestAdapter</tt>.
  49. 11 def queue_adapter_for_test
  50. 2453 ActiveJob::QueueAdapters::TestAdapter.new
  51. end
  52. # Asserts that the number of enqueued jobs matches the given number.
  53. #
  54. # def test_jobs
  55. # assert_enqueued_jobs 0
  56. # HelloJob.perform_later('david')
  57. # assert_enqueued_jobs 1
  58. # HelloJob.perform_later('abdelkader')
  59. # assert_enqueued_jobs 2
  60. # end
  61. #
  62. # If a block is passed, asserts that the block will cause the specified number of
  63. # jobs to be enqueued.
  64. #
  65. # def test_jobs_again
  66. # assert_enqueued_jobs 1 do
  67. # HelloJob.perform_later('cristian')
  68. # end
  69. #
  70. # assert_enqueued_jobs 2 do
  71. # HelloJob.perform_later('aaron')
  72. # HelloJob.perform_later('rafael')
  73. # end
  74. # end
  75. #
  76. # Asserts the number of times a specific job was enqueued by passing +:only+ option.
  77. #
  78. # def test_logging_job
  79. # assert_enqueued_jobs 1, only: LoggingJob do
  80. # LoggingJob.perform_later
  81. # HelloJob.perform_later('jeremy')
  82. # end
  83. # end
  84. #
  85. # Asserts the number of times a job except specific class was enqueued by passing +:except+ option.
  86. #
  87. # def test_logging_job
  88. # assert_enqueued_jobs 1, except: HelloJob do
  89. # LoggingJob.perform_later
  90. # HelloJob.perform_later('jeremy')
  91. # end
  92. # end
  93. #
  94. # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
  95. # a hash containing the job's class and it's argument are passed as argument.
  96. #
  97. # Asserts the number of times a job is enqueued to a specific queue by passing +:queue+ option.
  98. #
  99. # def test_logging_job
  100. # assert_enqueued_jobs 2, queue: 'default' do
  101. # LoggingJob.perform_later
  102. # HelloJob.perform_later('elfassy')
  103. # end
  104. # end
  105. 11 def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil, &block)
  106. 561 if block_given?
  107. 506 original_jobs = enqueued_jobs_with(only: only, except: except, queue: queue)
  108. 396 assert_nothing_raised(&block)
  109. 396 new_jobs = enqueued_jobs_with(only: only, except: except, queue: queue)
  110. 396 actual_count = (new_jobs - original_jobs).count
  111. else
  112. 55 actual_count = enqueued_jobs_with(only: only, except: except, queue: queue).count
  113. end
  114. 451 assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
  115. end
  116. # Asserts that no jobs have been enqueued.
  117. #
  118. # def test_jobs
  119. # assert_no_enqueued_jobs
  120. # HelloJob.perform_later('jeremy')
  121. # assert_enqueued_jobs 1
  122. # end
  123. #
  124. # If a block is passed, asserts that the block will not cause any job to be enqueued.
  125. #
  126. # def test_jobs_again
  127. # assert_no_enqueued_jobs do
  128. # # No job should be enqueued from this block
  129. # end
  130. # end
  131. #
  132. # Asserts that no jobs of a specific kind are enqueued by passing +:only+ option.
  133. #
  134. # def test_no_logging
  135. # assert_no_enqueued_jobs only: LoggingJob do
  136. # HelloJob.perform_later('jeremy')
  137. # end
  138. # end
  139. #
  140. # Asserts that no jobs except specific class are enqueued by passing +:except+ option.
  141. #
  142. # def test_no_logging
  143. # assert_no_enqueued_jobs except: HelloJob do
  144. # HelloJob.perform_later('jeremy')
  145. # end
  146. # end
  147. #
  148. # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
  149. # a hash containing the job's class and it's argument are passed as argument.
  150. #
  151. # Asserts that no jobs are enqueued to a specific queue by passing +:queue+ option
  152. #
  153. # def test_no_logging
  154. # assert_no_enqueued_jobs queue: 'default' do
  155. # LoggingJob.set(queue: :some_queue).perform_later
  156. # end
  157. # end
  158. #
  159. # Note: This assertion is simply a shortcut for:
  160. #
  161. # assert_enqueued_jobs 0, &block
  162. 11 def assert_no_enqueued_jobs(only: nil, except: nil, queue: nil, &block)
  163. 209 assert_enqueued_jobs 0, only: only, except: except, queue: queue, &block
  164. end
  165. # Asserts that the number of performed jobs matches the given number.
  166. # If no block is passed, <tt>perform_enqueued_jobs</tt>
  167. # must be called around or after the job call.
  168. #
  169. # def test_jobs
  170. # assert_performed_jobs 0
  171. #
  172. # perform_enqueued_jobs do
  173. # HelloJob.perform_later('xavier')
  174. # end
  175. # assert_performed_jobs 1
  176. #
  177. # HelloJob.perform_later('yves')
  178. #
  179. # perform_enqueued_jobs
  180. #
  181. # assert_performed_jobs 2
  182. # end
  183. #
  184. # If a block is passed, asserts that the block will cause the specified number of
  185. # jobs to be performed.
  186. #
  187. # def test_jobs_again
  188. # assert_performed_jobs 1 do
  189. # HelloJob.perform_later('robin')
  190. # end
  191. #
  192. # assert_performed_jobs 2 do
  193. # HelloJob.perform_later('carlos')
  194. # HelloJob.perform_later('sean')
  195. # end
  196. # end
  197. #
  198. # This method also supports filtering. If the +:only+ option is specified,
  199. # then only the listed job(s) will be performed.
  200. #
  201. # def test_hello_job
  202. # assert_performed_jobs 1, only: HelloJob do
  203. # HelloJob.perform_later('jeremy')
  204. # LoggingJob.perform_later
  205. # end
  206. # end
  207. #
  208. # Also if the +:except+ option is specified,
  209. # then the job(s) except specific class will be performed.
  210. #
  211. # def test_hello_job
  212. # assert_performed_jobs 1, except: LoggingJob do
  213. # HelloJob.perform_later('jeremy')
  214. # LoggingJob.perform_later
  215. # end
  216. # end
  217. #
  218. # An array may also be specified, to support testing multiple jobs.
  219. #
  220. # def test_hello_and_logging_jobs
  221. # assert_nothing_raised do
  222. # assert_performed_jobs 2, only: [HelloJob, LoggingJob] do
  223. # HelloJob.perform_later('jeremy')
  224. # LoggingJob.perform_later('stewie')
  225. # RescueJob.perform_later('david')
  226. # end
  227. # end
  228. # end
  229. #
  230. # A proc may also be specified. When passed a Proc, the job's instance will be passed as argument.
  231. #
  232. # def test_hello_and_logging_jobs
  233. # assert_nothing_raised do
  234. # assert_performed_jobs(1, only: ->(job) { job.is_a?(HelloJob) }) do
  235. # HelloJob.perform_later('jeremy')
  236. # LoggingJob.perform_later('stewie')
  237. # RescueJob.perform_later('david')
  238. # end
  239. # end
  240. # end
  241. #
  242. # If the +:queue+ option is specified,
  243. # then only the job(s) enqueued to a specific queue will be performed.
  244. #
  245. # def test_assert_performed_jobs_with_queue_option
  246. # assert_performed_jobs 1, queue: :some_queue do
  247. # HelloJob.set(queue: :some_queue).perform_later("jeremy")
  248. # HelloJob.set(queue: :other_queue).perform_later("bogdan")
  249. # end
  250. # end
  251. 11 def assert_performed_jobs(number, only: nil, except: nil, queue: nil, &block)
  252. 1133 if block_given?
  253. 506 original_count = performed_jobs.size
  254. 506 perform_enqueued_jobs(only: only, except: except, queue: queue, &block)
  255. 418 new_count = performed_jobs.size
  256. 418 performed_jobs_size = new_count - original_count
  257. else
  258. 627 performed_jobs_size = performed_jobs_with(only: only, except: except, queue: queue).count
  259. end
  260. 1023 assert_equal number, performed_jobs_size, "#{number} jobs expected, but #{performed_jobs_size} were performed"
  261. end
  262. # Asserts that no jobs have been performed.
  263. #
  264. # def test_jobs
  265. # assert_no_performed_jobs
  266. #
  267. # perform_enqueued_jobs do
  268. # HelloJob.perform_later('matthew')
  269. # assert_performed_jobs 1
  270. # end
  271. # end
  272. #
  273. # If a block is passed, asserts that the block will not cause any job to be performed.
  274. #
  275. # def test_jobs_again
  276. # assert_no_performed_jobs do
  277. # # No job should be performed from this block
  278. # end
  279. # end
  280. #
  281. # The block form supports filtering. If the +:only+ option is specified,
  282. # then only the listed job(s) will not be performed.
  283. #
  284. # def test_no_logging
  285. # assert_no_performed_jobs only: LoggingJob do
  286. # HelloJob.perform_later('jeremy')
  287. # end
  288. # end
  289. #
  290. # Also if the +:except+ option is specified,
  291. # then the job(s) except specific class will not be performed.
  292. #
  293. # def test_no_logging
  294. # assert_no_performed_jobs except: HelloJob do
  295. # HelloJob.perform_later('jeremy')
  296. # end
  297. # end
  298. #
  299. # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
  300. # an instance of the job will be passed as argument.
  301. #
  302. # If the +:queue+ option is specified,
  303. # then only the job(s) enqueued to a specific queue will not be performed.
  304. #
  305. # def test_assert_no_performed_jobs_with_queue_option
  306. # assert_no_performed_jobs queue: :some_queue do
  307. # HelloJob.set(queue: :other_queue).perform_later("jeremy")
  308. # end
  309. # end
  310. #
  311. # Note: This assertion is simply a shortcut for:
  312. #
  313. # assert_performed_jobs 0, &block
  314. 11 def assert_no_performed_jobs(only: nil, except: nil, queue: nil, &block)
  315. 319 assert_performed_jobs 0, only: only, except: except, queue: queue, &block
  316. end
  317. # Asserts that the job has been enqueued with the given arguments.
  318. #
  319. # def test_assert_enqueued_with
  320. # MyJob.perform_later(1,2,3)
  321. # assert_enqueued_with(job: MyJob, args: [1,2,3])
  322. #
  323. # MyJob.set(wait_until: Date.tomorrow.noon, queue: "my_queue").perform_later
  324. # assert_enqueued_with(at: Date.tomorrow.noon, queue: "my_queue")
  325. # end
  326. #
  327. # The given arguments may also be specified as matcher procs that return a
  328. # boolean value indicating whether a job's attribute meets certain criteria.
  329. #
  330. # For example, a proc can be used to match a range of times:
  331. #
  332. # def test_assert_enqueued_with
  333. # at_matcher = ->(job_at) { (Date.yesterday..Date.tomorrow).cover?(job_at) }
  334. #
  335. # MyJob.set(wait_until: Date.today.noon).perform_later
  336. #
  337. # assert_enqueued_with(job: MyJob, at: at_matcher)
  338. # end
  339. #
  340. # A proc can also be used to match a subset of a job's args:
  341. #
  342. # def test_assert_enqueued_with
  343. # args_matcher = ->(job_args) { job_args[0].key?(:foo) }
  344. #
  345. # MyJob.perform_later(foo: "bar", other_arg: "No need to check in the test")
  346. #
  347. # assert_enqueued_with(job: MyJob, args: args_matcher)
  348. # end
  349. #
  350. # If a block is passed, asserts that the block will cause the job to be
  351. # enqueued with the given arguments.
  352. #
  353. # def test_assert_enqueued_with
  354. # assert_enqueued_with(job: MyJob, args: [1,2,3]) do
  355. # MyJob.perform_later(1,2,3)
  356. # end
  357. #
  358. # assert_enqueued_with(job: MyJob, at: Date.tomorrow.noon) do
  359. # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
  360. # end
  361. # end
  362. 11 def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil, &block)
  363. 363 expected = { job: job, args: args, at: at, queue: queue }.compact
  364. 363 expected_args = prepare_args_for_assertion(expected)
  365. 363 potential_matches = []
  366. 363 if block_given?
  367. 187 original_enqueued_jobs = enqueued_jobs.dup
  368. 187 assert_nothing_raised(&block)
  369. 187 jobs = enqueued_jobs - original_enqueued_jobs
  370. else
  371. 176 jobs = enqueued_jobs
  372. end
  373. 363 matching_job = jobs.find do |enqueued_job|
  374. 352 deserialized_job = deserialize_args_for_assertion(enqueued_job)
  375. 352 potential_matches << deserialized_job
  376. 352 expected_args.all? do |key, value|
  377. 517 if value.respond_to?(:call)
  378. 121 value.call(deserialized_job[key])
  379. else
  380. 396 value == deserialized_job[key]
  381. end
  382. end
  383. end
  384. 363 message = +"No enqueued job found with #{expected}"
  385. 363 message << "\n\nPotential matches: #{potential_matches.join("\n")}" if potential_matches.present?
  386. 363 assert matching_job, message
  387. 242 instantiate_job(matching_job)
  388. end
  389. # Asserts that the job has been performed with the given arguments.
  390. #
  391. # def test_assert_performed_with
  392. # MyJob.perform_later(1,2,3)
  393. #
  394. # perform_enqueued_jobs
  395. #
  396. # assert_performed_with(job: MyJob, args: [1,2,3])
  397. #
  398. # MyJob.set(wait_until: Date.tomorrow.noon, queue: "my_queue").perform_later
  399. #
  400. # perform_enqueued_jobs
  401. #
  402. # assert_performed_with(at: Date.tomorrow.noon, queue: "my_queue")
  403. # end
  404. #
  405. # The given arguments may also be specified as matcher procs that return a
  406. # boolean value indicating whether a job's attribute meets certain criteria.
  407. #
  408. # For example, a proc can be used to match a range of times:
  409. #
  410. # def test_assert_performed_with
  411. # at_matcher = ->(job_at) { (Date.yesterday..Date.tomorrow).cover?(job_at) }
  412. #
  413. # MyJob.set(wait_until: Date.today.noon).perform_later
  414. #
  415. # perform_enqueued_jobs
  416. #
  417. # assert_performed_with(job: MyJob, at: at_matcher)
  418. # end
  419. #
  420. # A proc can also be used to match a subset of a job's args:
  421. #
  422. # def test_assert_performed_with
  423. # args_matcher = ->(job_args) { job_args[0].key?(:foo) }
  424. #
  425. # MyJob.perform_later(foo: "bar", other_arg: "No need to check in the test")
  426. #
  427. # perform_enqueued_jobs
  428. #
  429. # assert_performed_with(job: MyJob, args: args_matcher)
  430. # end
  431. #
  432. # If a block is passed, that block performs all of the jobs that were
  433. # enqueued throughout the duration of the block and asserts that
  434. # the job has been performed with the given arguments in the block.
  435. #
  436. # def test_assert_performed_with
  437. # assert_performed_with(job: MyJob, args: [1,2,3]) do
  438. # MyJob.perform_later(1,2,3)
  439. # end
  440. #
  441. # assert_performed_with(job: MyJob, at: Date.tomorrow.noon) do
  442. # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
  443. # end
  444. # end
  445. 11 def assert_performed_with(job: nil, args: nil, at: nil, queue: nil, &block)
  446. 374 expected = { job: job, args: args, at: at, queue: queue }.compact
  447. 374 expected_args = prepare_args_for_assertion(expected)
  448. 374 potential_matches = []
  449. 374 if block_given?
  450. 176 original_performed_jobs_count = performed_jobs.count
  451. 176 perform_enqueued_jobs(&block)
  452. 176 jobs = performed_jobs.drop(original_performed_jobs_count)
  453. else
  454. 198 jobs = performed_jobs
  455. end
  456. 374 matching_job = jobs.find do |enqueued_job|
  457. 385 deserialized_job = deserialize_args_for_assertion(enqueued_job)
  458. 385 potential_matches << deserialized_job
  459. 385 expected_args.all? do |key, value|
  460. 616 if value.respond_to?(:call)
  461. 165 value.call(deserialized_job[key])
  462. else
  463. 451 value == deserialized_job[key]
  464. end
  465. end
  466. end
  467. 374 message = +"No performed job found with #{expected}"
  468. 374 message << "\n\nPotential matches: #{potential_matches.join("\n")}" if potential_matches.present?
  469. 374 assert matching_job, message
  470. 231 instantiate_job(matching_job)
  471. end
  472. # Performs all enqueued jobs. If a block is given, performs all of the jobs
  473. # that were enqueued throughout the duration of the block. If a block is
  474. # not given, performs all of the enqueued jobs up to this point in the test.
  475. #
  476. # def test_perform_enqueued_jobs
  477. # perform_enqueued_jobs do
  478. # MyJob.perform_later(1, 2, 3)
  479. # end
  480. # assert_performed_jobs 1
  481. # end
  482. #
  483. # def test_perform_enqueued_jobs_without_block
  484. # MyJob.perform_later(1, 2, 3)
  485. #
  486. # perform_enqueued_jobs
  487. #
  488. # assert_performed_jobs 1
  489. # end
  490. #
  491. # This method also supports filtering. If the +:only+ option is specified,
  492. # then only the listed job(s) will be performed.
  493. #
  494. # def test_perform_enqueued_jobs_with_only
  495. # perform_enqueued_jobs(only: MyJob) do
  496. # MyJob.perform_later(1, 2, 3) # will be performed
  497. # HelloJob.perform_later(1, 2, 3) # will not be performed
  498. # end
  499. # assert_performed_jobs 1
  500. # end
  501. #
  502. # Also if the +:except+ option is specified,
  503. # then the job(s) except specific class will be performed.
  504. #
  505. # def test_perform_enqueued_jobs_with_except
  506. # perform_enqueued_jobs(except: HelloJob) do
  507. # MyJob.perform_later(1, 2, 3) # will be performed
  508. # HelloJob.perform_later(1, 2, 3) # will not be performed
  509. # end
  510. # assert_performed_jobs 1
  511. # end
  512. #
  513. # +:only+ and +:except+ options accepts Class, Array of Class or Proc. When passed a Proc,
  514. # an instance of the job will be passed as argument.
  515. #
  516. # If the +:queue+ option is specified,
  517. # then only the job(s) enqueued to a specific queue will be performed.
  518. #
  519. # def test_perform_enqueued_jobs_with_queue
  520. # perform_enqueued_jobs queue: :some_queue do
  521. # MyJob.set(queue: :some_queue).perform_later(1, 2, 3) # will be performed
  522. # HelloJob.set(queue: :other_queue).perform_later(1, 2, 3) # will not be performed
  523. # end
  524. # assert_performed_jobs 1
  525. # end
  526. #
  527. # If the +:at+ option is specified, then only run jobs enqueued to run
  528. # immediately or before the given time
  529. 11 def perform_enqueued_jobs(only: nil, except: nil, queue: nil, at: nil, &block)
  530. 1584 return flush_enqueued_jobs(only: only, except: except, queue: queue, at: at) unless block_given?
  531. 1012 validate_option(only: only, except: except)
  532. 924 old_perform_enqueued_jobs = queue_adapter.perform_enqueued_jobs
  533. 924 old_perform_enqueued_at_jobs = queue_adapter.perform_enqueued_at_jobs
  534. 924 old_filter = queue_adapter.filter
  535. 924 old_reject = queue_adapter.reject
  536. 924 old_queue = queue_adapter.queue
  537. 924 old_at = queue_adapter.at
  538. 924 begin
  539. 924 queue_adapter.perform_enqueued_jobs = true
  540. 924 queue_adapter.perform_enqueued_at_jobs = true
  541. 924 queue_adapter.filter = only
  542. 924 queue_adapter.reject = except
  543. 924 queue_adapter.queue = queue
  544. 924 queue_adapter.at = at
  545. 924 assert_nothing_raised(&block)
  546. ensure
  547. 924 queue_adapter.perform_enqueued_jobs = old_perform_enqueued_jobs
  548. 924 queue_adapter.perform_enqueued_at_jobs = old_perform_enqueued_at_jobs
  549. 924 queue_adapter.filter = old_filter
  550. 924 queue_adapter.reject = old_reject
  551. 924 queue_adapter.queue = old_queue
  552. 924 queue_adapter.at = old_at
  553. end
  554. end
  555. # Accesses the queue_adapter set by ActiveJob::Base.
  556. #
  557. # def test_assert_job_has_custom_queue_adapter_set
  558. # assert_instance_of CustomQueueAdapter, HelloJob.queue_adapter
  559. # end
  560. 11 def queue_adapter
  561. 27665 ActiveJob::Base.queue_adapter
  562. end
  563. 11 private
  564. 11 def clear_enqueued_jobs
  565. 2464 enqueued_jobs.clear
  566. end
  567. 11 def clear_performed_jobs
  568. 2464 performed_jobs.clear
  569. end
  570. 11 def jobs_with(jobs, only: nil, except: nil, queue: nil, at: nil)
  571. 2156 validate_option(only: only, except: except)
  572. 2024 jobs.dup.select do |job|
  573. 2728 job_class = job.fetch(:job)
  574. 2728 if only
  575. 715 next false unless filter_as_proc(only).call(job)
  576. 2013 elsif except
  577. 616 next false if filter_as_proc(except).call(job)
  578. end
  579. 2090 if queue
  580. 550 next false unless queue.to_s == job.fetch(:queue, job_class.queue_name)
  581. end
  582. 1782 if at && job[:at]
  583. 22 next false if job[:at] > at.to_f
  584. end
  585. 1771 yield job if block_given?
  586. 1760 true
  587. end
  588. end
  589. 11 def filter_as_proc(filter)
  590. 1331 return filter if filter.is_a?(Proc)
  591. 2442 ->(job) { Array(filter).include?(job.fetch(:job)) }
  592. end
  593. 11 def enqueued_jobs_with(only: nil, except: nil, queue: nil, at: nil, &block)
  594. 1529 jobs_with(enqueued_jobs, only: only, except: except, queue: queue, at: at, &block)
  595. end
  596. 11 def performed_jobs_with(only: nil, except: nil, queue: nil, &block)
  597. 627 jobs_with(performed_jobs, only: only, except: except, queue: queue, &block)
  598. end
  599. 11 def flush_enqueued_jobs(only: nil, except: nil, queue: nil, at: nil)
  600. enqueued_jobs_with(only: only, except: except, queue: queue, at: at) do |payload|
  601. 792 queue_adapter.enqueued_jobs.delete(payload)
  602. 792 queue_adapter.performed_jobs << payload
  603. 792 instantiate_job(payload).perform_now
  604. 572 end.count
  605. end
  606. 11 def prepare_args_for_assertion(args)
  607. 737 args.dup.tap do |arguments|
  608. 737 if arguments[:at].acts_like?(:time)
  609. 99 at_range = arguments[:at] - 1..arguments[:at] + 1
  610. 209 arguments[:at] = ->(at) { at_range.cover?(at) }
  611. end
  612. 737 arguments[:args] = round_time_arguments(arguments[:args]) if arguments[:args]
  613. end
  614. end
  615. 11 def round_time_arguments(argument)
  616. 649 case argument
  617. when Time, ActiveSupport::TimeWithZone, DateTime
  618. 99 argument.change(usec: 0)
  619. when Hash
  620. 363 argument.transform_values { |value| round_time_arguments(value) }
  621. when Array
  622. 418 argument.map { |element| round_time_arguments(element) }
  623. else
  624. 198 argument
  625. end
  626. end
  627. 11 def deserialize_args_for_assertion(job)
  628. 737 job.dup.tap do |new_job|
  629. 737 new_job[:at] = Time.at(new_job[:at]) if new_job[:at]
  630. 737 new_job[:args] = ActiveJob::Arguments.deserialize(new_job[:args]) if new_job[:args]
  631. end
  632. end
  633. 11 def instantiate_job(payload)
  634. 1265 job = payload[:job].deserialize(payload)
  635. 1265 job.scheduled_at = Time.at(payload[:at]) if payload.key?(:at)
  636. 1265 job.send(:deserialize_arguments_if_needed)
  637. 1265 job
  638. end
  639. 11 def queue_adapter_changed_jobs
  640. 4928 (ActiveJob::Base.descendants << ActiveJob::Base).select do |klass|
  641. # only override explicitly set adapters, a quirk of `class_attribute`
  642. 110811 klass.singleton_class.public_instance_methods(false).include?(:_queue_adapter)
  643. end
  644. end
  645. 11 def validate_option(only: nil, except: nil)
  646. 3168 raise ArgumentError, "Cannot specify both `:only` and `:except` options." if only && except
  647. end
  648. end
  649. end

lib/active_job/timezones.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module Timezones #:nodoc:
  4. 11 extend ActiveSupport::Concern
  5. 11 included do
  6. 11 around_perform do |job, block|
  7. 2800 Time.use_zone(job.timezone, &block)
  8. end
  9. end
  10. end
  11. end

lib/active_job/translation.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 module ActiveJob
  3. 11 module Translation #:nodoc:
  4. 11 extend ActiveSupport::Concern
  5. 11 included do
  6. 11 around_perform do |job, block|
  7. 2800 I18n.with_locale(job.locale, &block)
  8. end
  9. end
  10. end
  11. end

lib/active_job/version.rb

75.0% lines covered

4 relevant lines. 3 lines covered and 1 lines missed.
    
  1. # frozen_string_literal: true
  2. 11 require_relative "gem_version"
  3. 11 module ActiveJob
  4. # Returns the version of the currently loaded Active Job as a <tt>Gem::Version</tt>
  5. 11 def self.version
  6. gem_version
  7. end
  8. end

lib/rails/generators/job/job_generator.rb

0.0% lines covered

33 relevant lines. 0 lines covered and 33 lines missed.
    
  1. # frozen_string_literal: true
  2. require "rails/generators/named_base"
  3. module Rails # :nodoc:
  4. module Generators # :nodoc:
  5. class JobGenerator < Rails::Generators::NamedBase # :nodoc:
  6. desc "This generator creates an active job file at app/jobs"
  7. class_option :queue, type: :string, default: "default", desc: "The queue name for the generated job"
  8. check_class_collision suffix: "Job"
  9. hook_for :test_framework
  10. def self.default_generator_root
  11. __dir__
  12. end
  13. def create_job_file
  14. template "job.rb", File.join("app/jobs", class_path, "#{file_name}_job.rb")
  15. in_root do
  16. if behavior == :invoke && !File.exist?(application_job_file_name)
  17. template "application_job.rb", application_job_file_name
  18. end
  19. end
  20. end
  21. private
  22. def file_name
  23. @_file_name ||= super.sub(/_job\z/i, "")
  24. end
  25. def application_job_file_name
  26. @application_job_file_name ||= if mountable_engine?
  27. "app/jobs/#{namespaced_path}/application_job.rb"
  28. else
  29. "app/jobs/application_job.rb"
  30. end
  31. end
  32. end
  33. end
  34. end