diff options
Diffstat (limited to 'activejob')
49 files changed, 657 insertions, 189 deletions
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md index 77dfdefc05..a3d13ad162 100644 --- a/activejob/CHANGELOG.md +++ b/activejob/CHANGELOG.md @@ -1,8 +1,26 @@ -* Change logging instrumentation to log errors when a job raises an exception. +* Remove support for Qu gem. - Fixes #26848. + Reasons are that the Qu gem wasn't compatible since Rails 5.1, + gem development was stopped in 2014 and maintainers have + confirmed its demise. See issue #32273 - *Steven Bull* + *Alberto Almagro* +* Add support for timezones to Active Job. -Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/activejob/CHANGELOG.md) for previous changes. + Record what was the current timezone in effect when the job was + enqueued and then restore when the job is executed in same way + that the current locale is recorded and restored. + + *Andrew White* + +* Rails 6 requires Ruby 2.4.1 or newer. + + *Jeremy Daer* + +* Add support to define custom argument serializers. + + *Evgenii Pecherkin*, *Rafael Mendonça França* + + +Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/activejob/CHANGELOG.md) for previous changes. diff --git a/activejob/MIT-LICENSE b/activejob/MIT-LICENSE index daa726b9f0..274211f710 100644 --- a/activejob/MIT-LICENSE +++ b/activejob/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2017 David Heinemeier Hansson +Copyright (c) 2014-2018 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/activejob/README.md b/activejob/README.md index 8a9a23929b..d49fcfe3c2 100644 --- a/activejob/README.md +++ b/activejob/README.md @@ -88,6 +88,12 @@ Active Job has built-in adapters for multiple queueing backends (Sidekiq, Resque, Delayed Job and others). To get an up-to-date list of the adapters see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html). +**Please note:** We are not accepting pull requests for new adapters. We +encourage library authors to provide an ActiveJob adapter as part of +their gem, or as a stand-alone gem. For discussion about this see the +following PRs: [23311](https://github.com/rails/rails/issues/23311#issuecomment-176275718), +[21406](https://github.com/rails/rails/pull/21406#issuecomment-138813484), and [#32285](https://github.com/rails/rails/pull/32285). + ## Auxiliary gems * [activejob-stats](https://github.com/seuros/activejob-stats) @@ -100,7 +106,7 @@ The latest version of Active Job can be installed with RubyGems: $ gem install activejob ``` -Source code can be downloaded as part of the Rails project on GitHub +Source code can be downloaded as part of the Rails project on GitHub: * https://github.com/rails/rails/tree/master/activejob @@ -117,7 +123,7 @@ API documentation is at: * http://api.rubyonrails.org -Bug reports can be filed for the Ruby on Rails project here: +Bug reports for the Ruby on Rails project can be filed here: * https://github.com/rails/rails/issues diff --git a/activejob/Rakefile b/activejob/Rakefile index 6f13ef449d..b4da75adab 100644 --- a/activejob/Rakefile +++ b/activejob/Rakefile @@ -2,7 +2,6 @@ require "rake/testtask" -#TODO: add qu back to the list after it support Rails 5.1 ACTIVEJOB_ADAPTERS = %w(async inline delayed_job que queue_classic resque sidekiq sneakers sucker_punch backburner test) ACTIVEJOB_ADAPTERS.delete("queue_classic") if defined?(JRUBY_VERSION) diff --git a/activejob/activejob.gemspec b/activejob/activejob.gemspec index 71e32f695b..be6292f737 100644 --- a/activejob/activejob.gemspec +++ b/activejob/activejob.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.summary = "Job framework with pluggable queues." s.description = "Declare job classes that can be run by a variety of queueing backends." - s.required_ruby_version = ">= 2.2.2" + s.required_ruby_version = ">= 2.4.1" s.license = "MIT" diff --git a/activejob/lib/active_job.rb b/activejob/lib/active_job.rb index 56dab66544..01fab4d918 100644 --- a/activejob/lib/active_job.rb +++ b/activejob/lib/active_job.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true #-- -# Copyright (c) 2014-2017 David Heinemeier Hansson +# Copyright (c) 2014-2018 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -25,7 +25,7 @@ require "active_support" require "active_support/rails" -require_relative "active_job/version" +require "active_job/version" require "global_id" module ActiveJob @@ -33,6 +33,7 @@ module ActiveJob autoload :Base autoload :QueueAdapters + autoload :Serializers autoload :ConfiguredJob autoload :TestCase autoload :TestHelper diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb index de11e7fcb1..86bb0c5540 100644 --- a/activejob/lib/active_job/arguments.rb +++ b/activejob/lib/active_job/arguments.rb @@ -14,8 +14,8 @@ module ActiveJob end # Raised when an unsupported argument type is set as a job argument. We - # currently support NilClass, Integer, Fixnum, Float, String, TrueClass, FalseClass, - # Bignum, BigDecimal, and objects that can be represented as GlobalIDs (ex: Active Record). + # currently support NilClass, Integer, Float, String, TrueClass, FalseClass, + # BigDecimal, and objects that can be represented as GlobalIDs (ex: Active Record). # Raised if you set the key for a Hash something else than a string or # a symbol. Also raised when trying to serialize an object which can't be # identified with a Global ID - such as an unpersisted Active Record model. @@ -25,7 +25,6 @@ module ActiveJob extend self # :nodoc: TYPE_WHITELIST = [ NilClass, String, Integer, Float, BigDecimal, TrueClass, FalseClass ] - TYPE_WHITELIST.push(Fixnum, Bignum) unless 1.class == Integer # Serializes a set of arguments. Whitelisted types are returned # as-is. Arrays/Hashes are serialized element by element. @@ -44,13 +43,24 @@ module ActiveJob end private + # :nodoc: GLOBALID_KEY = "_aj_globalid".freeze # :nodoc: SYMBOL_KEYS_KEY = "_aj_symbol_keys".freeze # :nodoc: WITH_INDIFFERENT_ACCESS_KEY = "_aj_hash_with_indifferent_access".freeze - private_constant :GLOBALID_KEY, :SYMBOL_KEYS_KEY, :WITH_INDIFFERENT_ACCESS_KEY + # :nodoc: + OBJECT_SERIALIZER_KEY = "_aj_serialized" + + # :nodoc: + RESERVED_KEYS = [ + GLOBALID_KEY, GLOBALID_KEY.to_sym, + SYMBOL_KEYS_KEY, SYMBOL_KEYS_KEY.to_sym, + OBJECT_SERIALIZER_KEY, OBJECT_SERIALIZER_KEY.to_sym, + WITH_INDIFFERENT_ACCESS_KEY, WITH_INDIFFERENT_ACCESS_KEY.to_sym, + ] + private_constant :RESERVED_KEYS def serialize_argument(argument) case argument @@ -70,7 +80,7 @@ module ActiveJob result[SYMBOL_KEYS_KEY] = symbol_keys result else - raise SerializationError.new("Unsupported argument type: #{argument.class.name}") + Serializers.serialize(argument) end end @@ -85,6 +95,8 @@ module ActiveJob when Hash if serialized_global_id?(argument) deserialize_global_id argument + elsif custom_serialized?(argument) + Serializers.deserialize(argument) else deserialize_hash(argument) end @@ -101,6 +113,10 @@ module ActiveJob GlobalID::Locator.locate hash[GLOBALID_KEY] end + def custom_serialized?(hash) + hash.key?(OBJECT_SERIALIZER_KEY) + end + def serialize_hash(argument) argument.each_with_object({}) do |(key, value), hash| hash[serialize_hash_key(key)] = serialize_argument(value) @@ -117,14 +133,6 @@ module ActiveJob result end - # :nodoc: - RESERVED_KEYS = [ - GLOBALID_KEY, GLOBALID_KEY.to_sym, - SYMBOL_KEYS_KEY, SYMBOL_KEYS_KEY.to_sym, - WITH_INDIFFERENT_ACCESS_KEY, WITH_INDIFFERENT_ACCESS_KEY.to_sym, - ] - private_constant :RESERVED_KEYS - def serialize_hash_key(key) case key when *RESERVED_KEYS diff --git a/activejob/lib/active_job/base.rb b/activejob/lib/active_job/base.rb index 6af41260db..2b2a59e969 100644 --- a/activejob/lib/active_job/base.rb +++ b/activejob/lib/active_job/base.rb @@ -1,15 +1,16 @@ # frozen_string_literal: true -require_relative "core" -require_relative "queue_adapter" -require_relative "queue_name" -require_relative "queue_priority" -require_relative "enqueuing" -require_relative "execution" -require_relative "callbacks" -require_relative "exceptions" -require_relative "logging" -require_relative "translation" +require "active_job/core" +require "active_job/queue_adapter" +require "active_job/queue_name" +require "active_job/queue_priority" +require "active_job/enqueuing" +require "active_job/execution" +require "active_job/callbacks" +require "active_job/exceptions" +require "active_job/logging" +require "active_job/timezones" +require "active_job/translation" module ActiveJob #:nodoc: # = Active Job @@ -59,6 +60,7 @@ module ActiveJob #:nodoc: # * SerializationError - Error class for serialization errors. class Base include Core + include Serializers include QueueAdapter include QueueName include QueuePriority @@ -67,6 +69,7 @@ module ActiveJob #:nodoc: include Callbacks include Exceptions include Logging + include Timezones include Translation ActiveSupport.run_load_hooks(:active_job, self) diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb index c4e12fc518..da841ae45b 100644 --- a/activejob/lib/active_job/core.rb +++ b/activejob/lib/active_job/core.rb @@ -31,6 +31,9 @@ module ActiveJob # I18n.locale to be used during the job. attr_accessor :locale + + # Timezone to be used during the job. + attr_accessor :timezone end # These methods will be included into any Active Job object, adding @@ -87,7 +90,8 @@ module ActiveJob "priority" => priority, "arguments" => serialize_arguments(arguments), "executions" => executions, - "locale" => I18n.locale.to_s + "locale" => I18n.locale.to_s, + "timezone" => Time.zone.try(:name) } end @@ -97,17 +101,23 @@ module ActiveJob # ==== Examples # # class DeliverWebhookJob < ActiveJob::Base + # attr_writer :attempt_number + # + # def attempt_number + # @attempt_number ||= 0 + # end + # # def serialize - # super.merge('attempt_number' => (@attempt_number || 0) + 1) + # super.merge('attempt_number' => attempt_number + 1) # end # # def deserialize(job_data) # super - # @attempt_number = job_data['attempt_number'] + # self.attempt_number = job_data['attempt_number'] # end # - # rescue_from(TimeoutError) do |exception| - # raise exception if @attempt_number > 5 + # rescue_from(Timeout::Error) do |exception| + # raise exception if attempt_number > 5 # retry_job(wait: 10) # end # end @@ -119,6 +129,7 @@ module ActiveJob self.serialized_arguments = job_data["arguments"] self.executions = job_data["executions"] self.locale = job_data["locale"] || I18n.locale.to_s + self.timezone = job_data["timezone"] || Time.zone.try(:name) end private diff --git a/activejob/lib/active_job/enqueuing.rb b/activejob/lib/active_job/enqueuing.rb index c0d016853c..53cb98fc71 100644 --- a/activejob/lib/active_job/enqueuing.rb +++ b/activejob/lib/active_job/enqueuing.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "arguments" +require "active_job/arguments" module ActiveJob # Provides behavior for enqueuing jobs. diff --git a/activejob/lib/active_job/exceptions.rb b/activejob/lib/active_job/exceptions.rb index dfc74deb1a..ae700848d0 100644 --- a/activejob/lib/active_job/exceptions.rb +++ b/activejob/lib/active_job/exceptions.rb @@ -49,7 +49,7 @@ module ActiveJob retry_job wait: determine_delay(wait), queue: queue, priority: priority else if block_given? - yield self, exception + yield self, error else logger.error "Stopped retrying #{self.class} due to a #{exception}, which reoccurred on #{executions} attempts. The original exception was #{error.cause.inspect}." raise error @@ -61,18 +61,28 @@ module ActiveJob # Discard the job with no attempts to retry, if the exception is raised. This is useful when the subject of the job, # like an Active Record, is no longer available, and the job is thus no longer relevant. # + # 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. + # # ==== Example # # class SearchIndexingJob < ActiveJob::Base # discard_on ActiveJob::DeserializationError + # discard_on(CustomAppException) do |job, exception| + # ExceptionNotifier.caught(exception) + # end # # def perform(record) # # Will raise ActiveJob::DeserializationError if the record can't be deserialized + # # Might raise CustomAppException for something domain specific # end # end def discard_on(exception) rescue_from exception do |error| - logger.error "Discarded #{self.class} due to a #{exception}. The original exception was #{error.cause.inspect}." + if block_given? + yield self, exception + else + logger.error "Discarded #{self.class} due to a #{exception}. The original exception was #{error.cause.inspect}." + end end end end diff --git a/activejob/lib/active_job/execution.rb b/activejob/lib/active_job/execution.rb index 85e050b489..d75be376ec 100644 --- a/activejob/lib/active_job/execution.rb +++ b/activejob/lib/active_job/execution.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "active_support/rescuable" -require_relative "arguments" +require "active_job/arguments" module ActiveJob module Execution diff --git a/activejob/lib/active_job/gem_version.rb b/activejob/lib/active_job/gem_version.rb index 7ee61780e1..770f70dc5e 100644 --- a/activejob/lib/active_job/gem_version.rb +++ b/activejob/lib/active_job/gem_version.rb @@ -7,8 +7,8 @@ module ActiveJob end module VERSION - MAJOR = 5 - MINOR = 2 + MAJOR = 6 + MINOR = 0 TINY = 0 PRE = "alpha" diff --git a/activejob/lib/active_job/logging.rb b/activejob/lib/active_job/logging.rb index f53b7eaee5..3312857ac7 100644 --- a/activejob/lib/active_job/logging.rb +++ b/activejob/lib/active_job/logging.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "active_support/core_ext/hash/transform_values" require "active_support/core_ext/string/filters" require "active_support/tagged_logging" require "active_support/logger" @@ -12,13 +11,13 @@ module ActiveJob included do cattr_accessor :logger, default: ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT)) - around_enqueue do |_, block, _| + around_enqueue do |_, block| tag_logger do block.call end end - around_perform do |job, block, _| + around_perform do |job, block| tag_logger(job.class.name, job.job_id) do payload = { adapter: job.class.queue_adapter, job: job } ActiveSupport::Notifications.instrument("perform_start.active_job", payload.dup) diff --git a/activejob/lib/active_job/queue_adapter.rb b/activejob/lib/active_job/queue_adapter.rb index dd05800baf..006a683b85 100644 --- a/activejob/lib/active_job/queue_adapter.rb +++ b/activejob/lib/active_job/queue_adapter.rb @@ -29,28 +29,22 @@ module ActiveJob # Specify the backend queue provider. The default queue adapter # is the +:async+ queue. See QueueAdapters for more # information. - def queue_adapter=(name_or_adapter_or_class) - interpret_adapter(name_or_adapter_or_class) - end - - private - - def interpret_adapter(name_or_adapter_or_class) - case name_or_adapter_or_class - when Symbol, String - assign_adapter(name_or_adapter_or_class.to_s, - ActiveJob::QueueAdapters.lookup(name_or_adapter_or_class).new) + def queue_adapter=(name_or_adapter) + case name_or_adapter + when Symbol, String + queue_adapter = ActiveJob::QueueAdapters.lookup(name_or_adapter).new + assign_adapter(name_or_adapter.to_s, queue_adapter) + else + if queue_adapter?(name_or_adapter) + adapter_name = "#{name_or_adapter.class.name.demodulize.remove('Adapter').underscore}" + assign_adapter(adapter_name, name_or_adapter) else - if queue_adapter?(name_or_adapter_or_class) - adapter_name = "#{name_or_adapter_or_class.class.name.demodulize.remove('Adapter').underscore}" - assign_adapter(adapter_name, - name_or_adapter_or_class) - else - raise ArgumentError - end + raise ArgumentError end end + end + private def assign_adapter(adapter_name, queue_adapter) self._queue_adapter_name = adapter_name self._queue_adapter = queue_adapter diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb index c1a1d3c510..7854467cc1 100644 --- a/activejob/lib/active_job/queue_adapters.rb +++ b/activejob/lib/active_job/queue_adapters.rb @@ -7,7 +7,6 @@ module ActiveJob # # * {Backburner}[https://github.com/nesquena/backburner] # * {Delayed Job}[https://github.com/collectiveidea/delayed_job] - # * {Qu}[https://github.com/bkeepers/qu] # * {Que}[https://github.com/chanks/que] # * {queue_classic}[https://github.com/QueueClassic/queue_classic] # * {Resque}[https://github.com/resque/resque] @@ -16,6 +15,7 @@ module ActiveJob # * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch] # * {Active Job Async Job}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html] # * {Active Job Inline}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html] + # * Please Note: We are not accepting pull requests for new adapters. See the README for more details. # # === Backends Features # @@ -23,7 +23,6 @@ module ActiveJob # |-------------------|-------|--------|------------|------------|---------|---------| # | Backburner | Yes | Yes | Yes | Yes | Job | Global | # | Delayed Job | Yes | Yes | Yes | Job | Global | Global | - # | Qu | Yes | Yes | No | No | No | Global | # | Que | Yes | Yes | Yes | Job | No | Job | # | queue_classic | Yes | Yes | Yes* | No | No | No | # | Resque | Yes | Yes | Yes (Gem) | Queue | Global | Yes | @@ -114,7 +113,6 @@ module ActiveJob autoload :InlineAdapter autoload :BackburnerAdapter autoload :DelayedJobAdapter - autoload :QuAdapter autoload :QueAdapter autoload :QueueClassicAdapter autoload :ResqueAdapter diff --git a/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb b/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb index 1978179948..8eeef32b99 100644 --- a/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb +++ b/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb @@ -34,6 +34,10 @@ module ActiveJob @job_data = job_data end + def display_name + "#{job_data['job_class']} [#{job_data['job_id']}] from DelayedJob(#{job_data['queue_name']}) with arguments: #{job_data['arguments']}" + end + def perform Base.execute(job_data) end diff --git a/activejob/lib/active_job/queue_adapters/qu_adapter.rb b/activejob/lib/active_job/queue_adapters/qu_adapter.rb deleted file mode 100644 index bd7003e177..0000000000 --- a/activejob/lib/active_job/queue_adapters/qu_adapter.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -require "qu" - -module ActiveJob - module QueueAdapters - # == Qu adapter for Active Job - # - # Qu is a Ruby library for queuing and processing background jobs. It is - # heavily inspired by delayed_job and Resque. Qu was created to overcome - # some shortcomings in the existing queuing libraries. - # The advantages of Qu are: Multiple backends (redis, mongo), jobs are - # requeued when worker is killed, resque-like API. - # - # Read more about Qu {here}[https://github.com/bkeepers/qu]. - # - # To use Qu set the queue_adapter config to +:qu+. - # - # Rails.application.config.active_job.queue_adapter = :qu - class QuAdapter - def enqueue(job, *args) #:nodoc: - qu_job = Qu::Payload.new(klass: JobWrapper, args: [job.serialize]).tap do |payload| - payload.instance_variable_set(:@queue, job.queue_name) - end.push - - # qu_job can be nil depending on the configured backend - job.provider_job_id = qu_job.id unless qu_job.nil? - qu_job - end - - def enqueue_at(job, timestamp, *args) #:nodoc: - 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" - end - - class JobWrapper < Qu::Job #:nodoc: - def initialize(job_data) - @job_data = job_data - end - - def perform - Base.execute @job_data - end - end - end - end -end diff --git a/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb b/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb index 5a1135854b..f726e6ad93 100644 --- a/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb +++ b/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb @@ -18,7 +18,7 @@ module ActiveJob # Rails.application.config.active_job.queue_adapter = :sidekiq class SidekiqAdapter def enqueue(job) #:nodoc: - #Sidekiq::Client does not support symbols as keys + # Sidekiq::Client does not support symbols as keys job.provider_job_id = Sidekiq::Client.push \ "class" => JobWrapper, "wrapped" => job.class.to_s, diff --git a/activejob/lib/active_job/railtie.rb b/activejob/lib/active_job/railtie.rb index 7b0742a6d2..d0294854d3 100644 --- a/activejob/lib/active_job/railtie.rb +++ b/activejob/lib/active_job/railtie.rb @@ -7,17 +7,28 @@ module ActiveJob # = Active Job Railtie class Railtie < Rails::Railtie # :nodoc: config.active_job = ActiveSupport::OrderedOptions.new + config.active_job.custom_serializers = [] initializer "active_job.logger" do ActiveSupport.on_load(:active_job) { self.logger = ::Rails.logger } end + initializer "active_job.custom_serializers" do |app| + config.after_initialize do + custom_serializers = app.config.active_job.delete(:custom_serializers) + ActiveJob::Serializers.add_serializers custom_serializers + end + end + initializer "active_job.set_configs" do |app| options = app.config.active_job options.queue_adapter ||= :async ActiveSupport.on_load(:active_job) do - options.each { |k, v| send("#{k}=", v) } + options.each do |k, v| + k = "#{k}=" + send(k, v) if respond_to? k + end end end diff --git a/activejob/lib/active_job/serializers.rb b/activejob/lib/active_job/serializers.rb new file mode 100644 index 0000000000..df66e66659 --- /dev/null +++ b/activejob/lib/active_job/serializers.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require "set" + +module ActiveJob + # The <tt>ActiveJob::Serializers</tt> module is used to store a list of known serializers + # and to add new ones. It also has helpers to serialize/deserialize objects. + module Serializers # :nodoc: + extend ActiveSupport::Autoload + extend ActiveSupport::Concern + + autoload :ObjectSerializer + autoload :SymbolSerializer + autoload :DurationSerializer + autoload :DateTimeSerializer + autoload :DateSerializer + autoload :TimeWithZoneSerializer + autoload :TimeSerializer + + mattr_accessor :_additional_serializers + self._additional_serializers = Set.new + + class << self + # Returns serialized representative of the passed object. + # Will look up through all known serializers. + # Raises <tt>ActiveJob::SerializationError</tt> if it can't find a proper serializer. + def serialize(argument) + serializer = serializers.detect { |s| s.serialize?(argument) } + raise SerializationError.new("Unsupported argument type: #{argument.class.name}") unless serializer + serializer.serialize(argument) + end + + # Returns deserialized object. + # Will look up through all known serializers. + # If no serializer found will raise <tt>ArgumentError</tt>. + def deserialize(argument) + serializer_name = argument[Arguments::OBJECT_SERIALIZER_KEY] + raise ArgumentError, "Serializer name is not present in the argument: #{argument.inspect}" unless serializer_name + + serializer = serializer_name.safe_constantize + raise ArgumentError, "Serializer #{serializer_name} is not known" unless serializer + + serializer.deserialize(argument) + end + + # Returns list of known serializers. + def serializers + self._additional_serializers + end + + # Adds new serializers to a list of known serializers. + def add_serializers(*new_serializers) + self._additional_serializers += new_serializers.flatten + end + end + + add_serializers SymbolSerializer, + DurationSerializer, + DateTimeSerializer, + DateSerializer, + TimeWithZoneSerializer, + TimeSerializer + end +end diff --git a/activejob/lib/active_job/serializers/date_serializer.rb b/activejob/lib/active_job/serializers/date_serializer.rb new file mode 100644 index 0000000000..e995d30faa --- /dev/null +++ b/activejob/lib/active_job/serializers/date_serializer.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module ActiveJob + module Serializers + class DateSerializer < ObjectSerializer # :nodoc: + def serialize(date) + super("value" => date.iso8601) + end + + def deserialize(hash) + Date.iso8601(hash["value"]) + end + + private + + def klass + Date + end + end + end +end diff --git a/activejob/lib/active_job/serializers/date_time_serializer.rb b/activejob/lib/active_job/serializers/date_time_serializer.rb new file mode 100644 index 0000000000..fe780a1978 --- /dev/null +++ b/activejob/lib/active_job/serializers/date_time_serializer.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module ActiveJob + module Serializers + class DateTimeSerializer < ObjectSerializer # :nodoc: + def serialize(time) + super("value" => time.iso8601) + end + + def deserialize(hash) + DateTime.iso8601(hash["value"]) + end + + private + + def klass + DateTime + end + end + end +end diff --git a/activejob/lib/active_job/serializers/duration_serializer.rb b/activejob/lib/active_job/serializers/duration_serializer.rb new file mode 100644 index 0000000000..715fe27a5c --- /dev/null +++ b/activejob/lib/active_job/serializers/duration_serializer.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module ActiveJob + module Serializers + class DurationSerializer < ObjectSerializer # :nodoc: + def serialize(duration) + super("value" => duration.value, "parts" => Arguments.serialize(duration.parts)) + end + + def deserialize(hash) + value = hash["value"] + parts = Arguments.deserialize(hash["parts"]) + + klass.new(value, parts) + end + + private + + def klass + ActiveSupport::Duration + end + end + end +end diff --git a/activejob/lib/active_job/serializers/object_serializer.rb b/activejob/lib/active_job/serializers/object_serializer.rb new file mode 100644 index 0000000000..6d280969be --- /dev/null +++ b/activejob/lib/active_job/serializers/object_serializer.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module ActiveJob + module Serializers + # Base class for serializing and deserializing custom objects. + # + # Example: + # + # class MoneySerializer < ActiveJob::Serializers::ObjectSerializer + # def serialize(money) + # super("amount" => money.amount, "currency" => money.currency) + # end + # + # def deserialize(hash) + # Money.new(hash["amount"], hash["currency"]) + # end + # + # private + # + # def klass + # Money + # end + # end + class ObjectSerializer + include Singleton + + class << self + delegate :serialize?, :serialize, :deserialize, to: :instance + end + + # Determines if an argument should be serialized by a serializer. + def serialize?(argument) + argument.is_a?(klass) + end + + # Serializes an argument to a JSON primitive type. + def serialize(hash) + { Arguments::OBJECT_SERIALIZER_KEY => self.class.name }.merge!(hash) + end + + # Deserializes an argument from a JSON primitive type. + def deserialize(_argument) + raise NotImplementedError + end + + private + + # The class of the object that will be serialized. + def klass # :doc: + raise NotImplementedError + end + end + end +end diff --git a/activejob/lib/active_job/serializers/symbol_serializer.rb b/activejob/lib/active_job/serializers/symbol_serializer.rb new file mode 100644 index 0000000000..7e1f9553a2 --- /dev/null +++ b/activejob/lib/active_job/serializers/symbol_serializer.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module ActiveJob + module Serializers + class SymbolSerializer < ObjectSerializer # :nodoc: + def serialize(argument) + super("value" => argument.to_s) + end + + def deserialize(argument) + argument["value"].to_sym + end + + private + + def klass + Symbol + end + end + end +end diff --git a/activejob/lib/active_job/serializers/time_serializer.rb b/activejob/lib/active_job/serializers/time_serializer.rb new file mode 100644 index 0000000000..fe20772f35 --- /dev/null +++ b/activejob/lib/active_job/serializers/time_serializer.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module ActiveJob + module Serializers + class TimeSerializer < ObjectSerializer # :nodoc: + def serialize(time) + super("value" => time.iso8601) + end + + def deserialize(hash) + Time.iso8601(hash["value"]) + end + + private + + def klass + Time + end + end + end +end diff --git a/activejob/lib/active_job/serializers/time_with_zone_serializer.rb b/activejob/lib/active_job/serializers/time_with_zone_serializer.rb new file mode 100644 index 0000000000..43017fc75b --- /dev/null +++ b/activejob/lib/active_job/serializers/time_with_zone_serializer.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module ActiveJob + module Serializers + class TimeWithZoneSerializer < ObjectSerializer # :nodoc: + def serialize(time) + super("value" => time.iso8601) + end + + def deserialize(hash) + Time.iso8601(hash["value"]).in_time_zone + end + + private + + def klass + ActiveSupport::TimeWithZone + end + end + end +end diff --git a/activejob/lib/active_job/timezones.rb b/activejob/lib/active_job/timezones.rb new file mode 100644 index 0000000000..ac018eb752 --- /dev/null +++ b/activejob/lib/active_job/timezones.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module ActiveJob + module Timezones #:nodoc: + extend ActiveSupport::Concern + + included do + around_perform do |job, block| + Time.use_zone(job.timezone, &block) + end + end + end +end diff --git a/activejob/lib/active_job/translation.rb b/activejob/lib/active_job/translation.rb index fb45c80d67..0fd9b9fc06 100644 --- a/activejob/lib/active_job/translation.rb +++ b/activejob/lib/active_job/translation.rb @@ -5,7 +5,7 @@ module ActiveJob extend ActiveSupport::Concern included do - around_perform do |job, block, _| + around_perform do |job, block| I18n.with_locale(job.locale, &block) end end diff --git a/activejob/lib/rails/generators/job/job_generator.rb b/activejob/lib/rails/generators/job/job_generator.rb index 69b4fe7d26..03346a7f12 100644 --- a/activejob/lib/rails/generators/job/job_generator.rb +++ b/activejob/lib/rails/generators/job/job_generator.rb @@ -28,6 +28,10 @@ module Rails # :nodoc: end private + def file_name + @_file_name ||= super.sub(/_job\z/i, "") + end + def application_job_file_name @application_job_file_name ||= if mountable_engine? "app/jobs/#{namespaced_path}/application_job.rb" diff --git a/activejob/lib/rails/generators/job/templates/application_job.rb b/activejob/lib/rails/generators/job/templates/application_job.rb.tt index f93745a31a..f93745a31a 100644 --- a/activejob/lib/rails/generators/job/templates/application_job.rb +++ b/activejob/lib/rails/generators/job/templates/application_job.rb.tt diff --git a/activejob/lib/rails/generators/job/templates/job.rb b/activejob/lib/rails/generators/job/templates/job.rb.tt index 4ad2914a45..4ad2914a45 100644 --- a/activejob/lib/rails/generators/job/templates/job.rb +++ b/activejob/lib/rails/generators/job/templates/job.rb.tt diff --git a/activejob/test/adapters/qu.rb b/activejob/test/adapters/qu.rb deleted file mode 100644 index 5b471fa347..0000000000 --- a/activejob/test/adapters/qu.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -require "qu-immediate" - -ActiveJob::Base.queue_adapter = :qu diff --git a/activejob/test/cases/argument_serialization_test.rb b/activejob/test/cases/argument_serialization_test.rb index 13e6fcb727..e5f1f087fe 100644 --- a/activejob/test/cases/argument_serialization_test.rb +++ b/activejob/test/cases/argument_serialization_test.rb @@ -12,7 +12,10 @@ class ArgumentSerializationTest < ActiveSupport::TestCase end [ nil, 1, 1.0, 1_000_000_000_000_000_000_000, - "a", true, false, BigDecimal.new(5), + "a", true, false, BigDecimal(5), + :a, 1.day, Date.new(2001, 2, 3), Time.new(2002, 10, 31, 2, 2, 2, "+02:00"), + DateTime.new(2001, 2, 3, 4, 5, 6, "+03:00"), + ActiveSupport::TimeWithZone.new(Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone["UTC"]), [ 1, "a" ], { "a" => 1 } ].each do |arg| @@ -21,7 +24,7 @@ class ArgumentSerializationTest < ActiveSupport::TestCase end end - [ :a, Object.new, self, Person.find("5").to_gid ].each do |arg| + [ Object.new, self, Person.find("5").to_gid ].each do |arg| test "does not serialize #{arg.class}" do assert_raises ActiveJob::SerializationError do ActiveJob::Arguments.serialize [ arg ] @@ -46,6 +49,49 @@ class ArgumentSerializationTest < ActiveSupport::TestCase assert_arguments_roundtrip([a: 1, "b" => 2]) end + test "serialize a hash" do + symbol_key = { a: 1 } + string_key = { "a" => 1 } + indifferent_access = { a: 1 }.with_indifferent_access + + assert_equal( + { "a" => 1, "_aj_symbol_keys" => ["a"] }, + ActiveJob::Arguments.serialize([symbol_key]).first + ) + assert_equal( + { "a" => 1, "_aj_symbol_keys" => [] }, + ActiveJob::Arguments.serialize([string_key]).first + ) + assert_equal( + { "a" => 1, "_aj_hash_with_indifferent_access" => true }, + ActiveJob::Arguments.serialize([indifferent_access]).first + ) + end + + test "deserialize a hash" do + symbol_key = { "a" => 1, "_aj_symbol_keys" => ["a"] } + string_key = { "a" => 1, "_aj_symbol_keys" => [] } + another_string_key = { "a" => 1 } + indifferent_access = { "a" => 1, "_aj_hash_with_indifferent_access" => true } + + assert_equal( + { a: 1 }, + ActiveJob::Arguments.deserialize([symbol_key]).first + ) + assert_equal( + { "a" => 1 }, + ActiveJob::Arguments.deserialize([string_key]).first + ) + assert_equal( + { "a" => 1 }, + ActiveJob::Arguments.deserialize([another_string_key]).first + ) + assert_equal( + { "a" => 1 }, + ActiveJob::Arguments.deserialize([indifferent_access]).first + ) + end + test "should maintain hash with indifferent access" do symbol_key = { a: 1 } string_key = { "a" => 1 } @@ -56,6 +102,14 @@ class ArgumentSerializationTest < ActiveSupport::TestCase assert_instance_of ActiveSupport::HashWithIndifferentAccess, perform_round_trip([indifferent_access]).first end + test "should maintain time with zone" do + Time.use_zone "Alaska" do + time_with_zone = Time.new(2002, 10, 31, 2, 2, 2).in_time_zone + assert_instance_of ActiveSupport::TimeWithZone, perform_round_trip([time_with_zone]).first + assert_arguments_unchanged time_with_zone + end + end + test "should disallow non-string/symbol hash keys" do assert_raises ActiveJob::SerializationError do ActiveJob::Arguments.serialize [ { 1 => 2 } ] diff --git a/activejob/test/cases/exceptions_test.rb b/activejob/test/cases/exceptions_test.rb index 7a3c372143..bc33d79f61 100644 --- a/activejob/test/cases/exceptions_test.rb +++ b/activejob/test/cases/exceptions_test.rb @@ -58,10 +58,17 @@ class ExceptionsTest < ActiveJob::TestCase end end + test "custom handling of discarded job" do + perform_enqueued_jobs do + RetryJob.perform_later "CustomDiscardableError", 2 + assert_equal "Dealt with a job that was discarded in a custom way", JobBuffer.last_value + end + end + test "custom handling of job that exceeds retry attempts" do perform_enqueued_jobs do RetryJob.perform_later "CustomCatchError", 6 - assert_equal "Dealt with a job that failed to retry in a custom way after 6 attempts", JobBuffer.last_value + assert_equal "Dealt with a job that failed to retry in a custom way after 6 attempts. Message: CustomCatchError", JobBuffer.last_value end end diff --git a/activejob/test/cases/job_serialization_test.rb b/activejob/test/cases/job_serialization_test.rb index 440051c427..5c9994508e 100644 --- a/activejob/test/cases/job_serialization_test.rb +++ b/activejob/test/cases/job_serialization_test.rb @@ -54,4 +54,11 @@ class JobSerializationTest < ActiveSupport::TestCase job.provider_job_id = "some value set by adapter" assert_equal job.provider_job_id, job.serialize["provider_job_id"] end + + test "serialize stores the current timezone" do + Time.use_zone "Hawaii" do + job = HelloJob.new + assert_equal "Hawaii", job.serialize["timezone"] + end + end end diff --git a/activejob/test/cases/serializers_test.rb b/activejob/test/cases/serializers_test.rb new file mode 100644 index 0000000000..bee0c061bd --- /dev/null +++ b/activejob/test/cases/serializers_test.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +require "helper" +require "active_job/serializers" + +class SerializersTest < ActiveSupport::TestCase + class DummyValueObject + attr_accessor :value + + def initialize(value) + @value = value + end + + def ==(other) + self.value == other.value + end + end + + class DummySerializer < ActiveJob::Serializers::ObjectSerializer + def serialize(object) + super({ "value" => object.value }) + end + + def deserialize(hash) + DummyValueObject.new(hash["value"]) + end + + private + + def klass + DummyValueObject + end + end + + setup do + @value_object = DummyValueObject.new 123 + @original_serializers = ActiveJob::Serializers.serializers + end + + teardown do + ActiveJob::Serializers._additional_serializers = @original_serializers + end + + test "can't serialize unknown object" do + assert_raises ActiveJob::SerializationError do + ActiveJob::Serializers.serialize @value_object + end + end + + test "will serialize objects with serializers registered" do + ActiveJob::Serializers.add_serializers DummySerializer + + assert_equal( + { "_aj_serialized" => "SerializersTest::DummySerializer", "value" => 123 }, + ActiveJob::Serializers.serialize(@value_object) + ) + end + + test "won't deserialize unknown hash" do + hash = { "_dummy_serializer" => 123, "_aj_symbol_keys" => [] } + error = assert_raises(ArgumentError) do + ActiveJob::Serializers.deserialize(hash) + end + assert_equal( + 'Serializer name is not present in the argument: {"_dummy_serializer"=>123, "_aj_symbol_keys"=>[]}', + error.message + ) + end + + test "won't deserialize unknown serializer" do + hash = { "_aj_serialized" => "DoNotExist", "value" => 123 } + error = assert_raises(ArgumentError) do + ActiveJob::Serializers.deserialize(hash) + end + assert_equal( + "Serializer DoNotExist is not known", + error.message + ) + end + + test "will deserialize know serialized objects" do + ActiveJob::Serializers.add_serializers DummySerializer + hash = { "_aj_serialized" => "SerializersTest::DummySerializer", "value" => 123 } + assert_equal DummyValueObject.new(123), ActiveJob::Serializers.deserialize(hash) + end + + test "adds new serializer" do + ActiveJob::Serializers.add_serializers DummySerializer + assert ActiveJob::Serializers.serializers.include?(DummySerializer) + end + + test "can't add serializer with the same key twice" do + ActiveJob::Serializers.add_serializers DummySerializer + assert_no_difference(-> { ActiveJob::Serializers.serializers.size }) do + ActiveJob::Serializers.add_serializers DummySerializer + end + end +end diff --git a/activejob/test/cases/timezones_test.rb b/activejob/test/cases/timezones_test.rb new file mode 100644 index 0000000000..e2095b020d --- /dev/null +++ b/activejob/test/cases/timezones_test.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require "helper" +require "jobs/timezone_dependent_job" + +class TimezonesTest < ActiveSupport::TestCase + setup do + JobBuffer.clear + end + + test "it performs the job in the given timezone" do + job = TimezoneDependentJob.new("2018-01-01T00:00:00Z") + job.timezone = "London" + job.perform_now + + assert_equal "Happy New Year!", JobBuffer.last_value + + job = TimezoneDependentJob.new("2018-01-01T00:00:00Z") + job.timezone = "Eastern Time (US & Canada)" + job.perform_now + + assert_equal "Just 5 hours to go", JobBuffer.last_value + end +end diff --git a/activejob/test/integration/queuing_test.rb b/activejob/test/integration/queuing_test.rb index 0d8aa336a6..32afb5ca62 100644 --- a/activejob/test/integration/queuing_test.rb +++ b/activejob/test/integration/queuing_test.rb @@ -45,6 +45,13 @@ class QueuingTest < ActiveSupport::TestCase end end + test "should supply a wrapped class name to DelayedJob" do + skip unless adapter_is?(:delayed_job) + ::HelloJob.perform_later + job = Delayed::Job.first + assert_match(/HelloJob \[[0-9a-f-]+\] from DelayedJob\(default\) with arguments: \[\]/, job.name) + end + test "resque JobWrapper should have instance variable queue" do skip unless adapter_is?(:resque) job = ::HelloJob.set(wait: 5.seconds).perform_later @@ -75,7 +82,7 @@ class QueuingTest < ActiveSupport::TestCase end test "should supply a provider_job_id when available for immediate jobs" do - skip unless adapter_is?(:async, :delayed_job, :sidekiq, :qu, :que, :queue_classic) + skip unless adapter_is?(:async, :delayed_job, :sidekiq, :que, :queue_classic) test_job = TestJob.perform_later @id assert test_job.provider_job_id, "Provider job id should be set by provider" end @@ -103,6 +110,22 @@ class QueuingTest < ActiveSupport::TestCase end end + test "current timezone is kept while running perform_later" do + skip if adapter_is?(:inline) + + begin + current_zone = Time.zone + Time.zone = "Hawaii" + + TestJob.perform_later @id + wait_for_jobs_to_finish_for(5.seconds) + assert job_executed + assert_equal "Hawaii", job_executed_in_timezone + ensure + Time.zone = current_zone + end + end + test "should run job with higher priority first" do skip unless adapter_is?(:delayed_job, :que) diff --git a/activejob/test/jobs/retry_job.rb b/activejob/test/jobs/retry_job.rb index a12d09779b..82b80fe12b 100644 --- a/activejob/test/jobs/retry_job.rb +++ b/activejob/test/jobs/retry_job.rb @@ -10,6 +10,7 @@ class ExponentialWaitTenAttemptsError < StandardError; end class CustomWaitTenAttemptsError < StandardError; end class CustomCatchError < StandardError; end class DiscardableError < StandardError; end +class CustomDiscardableError < StandardError; end class RetryJob < ActiveJob::Base retry_on DefaultsError @@ -17,8 +18,9 @@ class RetryJob < ActiveJob::Base retry_on ShortWaitTenAttemptsError, wait: 1.second, attempts: 10 retry_on ExponentialWaitTenAttemptsError, wait: :exponentially_longer, attempts: 10 retry_on CustomWaitTenAttemptsError, wait: ->(executions) { executions * 2 }, attempts: 10 - retry_on(CustomCatchError) { |job, exception| JobBuffer.add("Dealt with a job that failed to retry in a custom way after #{job.arguments.second} attempts") } + retry_on(CustomCatchError) { |job, exception| JobBuffer.add("Dealt with a job that failed to retry in a custom way after #{job.arguments.second} attempts. Message: #{exception.message}") } discard_on DiscardableError + discard_on(CustomDiscardableError) { |job, exception| JobBuffer.add("Dealt with a job that was discarded in a custom way") } def perform(raising, attempts) if executions < attempts diff --git a/activejob/test/jobs/timezone_dependent_job.rb b/activejob/test/jobs/timezone_dependent_job.rb new file mode 100644 index 0000000000..41f473d533 --- /dev/null +++ b/activejob/test/jobs/timezone_dependent_job.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require_relative "../support/job_buffer" + +class TimezoneDependentJob < ActiveJob::Base + def perform(now) + now = now.in_time_zone + new_year = localtime(2018, 1, 1) + + if now >= new_year + JobBuffer.add("Happy New Year!") + else + JobBuffer.add("Just #{(new_year - now).div(3600)} hours to go") + end + end + + private + + def localtime(*args) + Time.zone ? Time.zone.local(*args) : Time.utc(*args) + end +end diff --git a/activejob/test/support/delayed_job/delayed/backend/test.rb b/activejob/test/support/delayed_job/delayed/backend/test.rb index 4721c1cc17..1691896b7c 100644 --- a/activejob/test/support/delayed_job/delayed/backend/test.rb +++ b/activejob/test/support/delayed_job/delayed/backend/test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -#copied from https://github.com/collectiveidea/delayed_job/blob/master/spec/delayed/backend/test.rb +# copied from https://github.com/collectiveidea/delayed_job/blob/master/spec/delayed/backend/test.rb require "ostruct" # An in-memory backend suitable only for testing. Tries to behave as if it were an ORM. @@ -77,7 +77,7 @@ module Delayed self.locked_by = worker end - return true + true end def self.db_time_now diff --git a/activejob/test/support/integration/adapters/qu.rb b/activejob/test/support/integration/adapters/qu.rb deleted file mode 100644 index 67db03e279..0000000000 --- a/activejob/test/support/integration/adapters/qu.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -module QuJobsManager - def setup - require "qu-rails" - require "qu-redis" - ActiveJob::Base.queue_adapter = :qu - ENV["REDISTOGO_URL"] = "redis://127.0.0.1:6379/12" - backend = Qu::Backend::Redis.new - backend.namespace = "active_jobs_int_test" - Qu.backend = backend - Qu.logger = Rails.logger - Qu.interval = 0.5 - unless can_run? - puts "Cannot run integration tests for qu. To be able to run integration tests for qu you need to install and start redis.\n" - exit - end - end - - def clear_jobs - Qu.clear "integration_tests" - end - - def start_workers - @thread = Thread.new { Qu::Worker.new("integration_tests").start } - end - - def stop_workers - @thread.kill - end - - def can_run? - begin - Qu.backend.connection.client.connect - rescue - return false - end - true - end -end diff --git a/activejob/test/support/integration/adapters/resque.rb b/activejob/test/support/integration/adapters/resque.rb index 484b476567..2ed8302277 100644 --- a/activejob/test/support/integration/adapters/resque.rb +++ b/activejob/test/support/integration/adapters/resque.rb @@ -3,11 +3,12 @@ module ResqueJobsManager def setup ActiveJob::Base.queue_adapter = :resque - Resque.redis = Redis::Namespace.new "active_jobs_int_test", redis: Redis.connect(url: "redis://:password@127.0.0.1:6379/12", thread_safe: true) + Resque.redis = Redis::Namespace.new "active_jobs_int_test", redis: Redis.new(url: "redis://127.0.0.1:6379/12", thread_safe: true) Resque.logger = Rails.logger unless can_run? puts "Cannot run integration tests for resque. To be able to run integration tests for resque you need to install and start redis.\n" - exit + status = ENV["CI"] ? false : true + exit status end end @@ -41,11 +42,8 @@ module ResqueJobsManager end def can_run? - begin - Resque.redis.client.connect - rescue - return false - end - true + Resque.redis.ping == "PONG" + rescue + false end end diff --git a/activejob/test/support/integration/adapters/sidekiq.rb b/activejob/test/support/integration/adapters/sidekiq.rb index ceb7fb61f2..c79de12eaf 100644 --- a/activejob/test/support/integration/adapters/sidekiq.rb +++ b/activejob/test/support/integration/adapters/sidekiq.rb @@ -5,20 +5,13 @@ require "sidekiq/api" require "sidekiq/testing" Sidekiq::Testing.disable! -Sidekiq.configure_server do |config| - config.redis = { url: "redis://:password@127.0.0.1:6379/12" } -end - -Sidekiq.configure_client do |config| - config.redis = { url: "redis://:password@127.0.0.1:6379/12" } -end - module SidekiqJobsManager def setup ActiveJob::Base.queue_adapter = :sidekiq unless can_run? puts "Cannot run integration tests for sidekiq. To be able to run integration tests for sidekiq you need to install and start redis.\n" - exit + status = ENV["CI"] ? false : true + exit status end end @@ -58,6 +51,7 @@ module SidekiqJobsManager self_write.puts("TERM") end + require "sidekiq/cli" require "sidekiq/launcher" sidekiq = Sidekiq::Launcher.new(queues: ["integration_tests"], environment: "test", diff --git a/activejob/test/support/integration/dummy_app_template.rb b/activejob/test/support/integration/dummy_app_template.rb index ac382bd1b7..b56dd3e591 100644 --- a/activejob/test/support/integration/dummy_app_template.rb +++ b/activejob/test/support/integration/dummy_app_template.rb @@ -4,8 +4,6 @@ if ENV["AJ_ADAPTER"] == "delayed_job" generate "delayed_job:active_record", "--quiet" end -rails_command("db:migrate") - initializer "activejob.rb", <<-CODE require "#{File.expand_path("jobs_manager.rb", __dir__)}" JobsManager.current_manager.setup @@ -23,6 +21,7 @@ class TestJob < ActiveJob::Base File.open(Rails.root.join("tmp/\#{x}.new"), "wb+") do |f| f.write Marshal.dump({ "locale" => I18n.locale.to_s || "en", + "timezone" => Time.zone.try(:name) || "UTC", "executed_at" => Time.now.to_r }) end diff --git a/activejob/test/support/integration/helper.rb b/activejob/test/support/integration/helper.rb index a058da141f..a02d874e2e 100644 --- a/activejob/test/support/integration/helper.rb +++ b/activejob/test/support/integration/helper.rb @@ -18,6 +18,7 @@ Rails::Generators::AppGenerator.start args require "#{dummy_app_path}/config/environment.rb" ActiveRecord::Migrator.migrations_paths = [ Rails.root.join("db/migrate").to_s ] +ActiveRecord::Tasks::DatabaseTasks.migrate require "rails/test_help" Rails.backtrace_cleaner.remove_silencers! diff --git a/activejob/test/support/integration/test_case_helpers.rb b/activejob/test/support/integration/test_case_helpers.rb index f02a32a38e..3d9b265b66 100644 --- a/activejob/test/support/integration/test_case_helpers.rb +++ b/activejob/test/support/integration/test_case_helpers.rb @@ -62,4 +62,8 @@ module TestCaseHelpers def job_executed_in_locale(id = @id) job_data(id)["locale"] end + + def job_executed_in_timezone(id = @id) + job_data(id)["timezone"] + end end |