lib/active_job/arguments.rb in activejob-4.2.0.beta1 vs lib/active_job/arguments.rb in activejob-4.2.0.beta2

- old
+ new

@@ -1,62 +1,105 @@ module ActiveJob + # Raised when an exception is raised during job arguments deserialization. + # + # Wraps the original exception raised as +original_exception+. class DeserializationError < StandardError attr_reader :original_exception - def initialize(e) - super ("Error while trying to deserialize arguments: #{e.message}") + def initialize(e) #:nodoc: + super("Error while trying to deserialize arguments: #{e.message}") @original_exception = e set_backtrace e.backtrace end end + # Raised when an unsupported argument type is being set as job argument. We + # currently support NilClass, Fixnum, Float, String, TrueClass, FalseClass, + # Bignum and object that can be represented as GlobalIDs (ex: Active Record). + # Also raised if you set the key for a Hash something else than a string or + # a symbol. + class SerializationError < ArgumentError + end + module Arguments extend self TYPE_WHITELIST = [ NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum ] def serialize(arguments) arguments.map { |argument| serialize_argument(argument) } end def deserialize(arguments) arguments.map { |argument| deserialize_argument(argument) } + rescue => e + raise DeserializationError.new(e) end private + GLOBALID_KEY = '_aj_globalid'.freeze + private_constant :GLOBALID_KEY + def serialize_argument(argument) case argument - when GlobalID::Identification - argument.global_id.to_s when *TYPE_WHITELIST argument + when GlobalID::Identification + { GLOBALID_KEY => argument.to_global_id.to_s } when Array - serialize(argument) + argument.map { |arg| serialize_argument(arg) } when Hash - Hash[ argument.map { |key, value| [ serialize_hash_key(key), serialize_argument(value) ] } ] + argument.each_with_object({}) do |(key, value), hash| + hash[serialize_hash_key(key)] = serialize_argument(value) + end else - raise "Unsupported argument type: #{argument.class.name}" + raise SerializationError.new("Unsupported argument type: #{argument.class.name}") end end def deserialize_argument(argument) case argument + when String + GlobalID::Locator.locate(argument) || argument + when *TYPE_WHITELIST + argument when Array - deserialize(argument) + argument.map { |arg| deserialize_argument(arg) } when Hash - Hash[ argument.map { |key, value| [ key, deserialize_argument(value) ] } ].with_indifferent_access + if serialized_global_id?(argument) + deserialize_global_id argument + else + deserialize_hash argument + end else - GlobalID::Locator.locate(argument) || argument + raise ArgumentError, "Can only deserialize primitive arguments: #{argument.inspect}" end - rescue => e - raise DeserializationError.new(e) end + def serialized_global_id?(hash) + hash.size == 1 and hash.include?(GLOBALID_KEY) + end + + def deserialize_global_id(hash) + GlobalID::Locator.locate hash[GLOBALID_KEY] + end + + def deserialize_hash(serialized_hash) + serialized_hash.each_with_object({}.with_indifferent_access) do |(key, value), hash| + hash[key] = deserialize_argument(value) + end + end + + RESERVED_KEYS = [GLOBALID_KEY, GLOBALID_KEY.to_sym] + private_constant :RESERVED_KEYS + def serialize_hash_key(key) case key + when *RESERVED_KEYS + raise SerializationError.new("Can't serialize a Hash with reserved key #{key.inspect}") when String, Symbol key.to_s else - raise "Unsupported hash key type: #{key.class.name}" + raise SerializationError.new("Only string and symbol hash keys may be serialized as job arguments, but #{key.inspect} is a #{key.class}") end end end end