diff options
author | Sean Griffin <sean@thoughtbot.com> | 2015-01-30 13:40:49 -0700 |
---|---|---|
committer | Sean Griffin <sean@thoughtbot.com> | 2015-01-30 13:43:39 -0700 |
commit | 31085a5cd47894b6fa397ca22fe8aa552d339ce8 (patch) | |
tree | 4602b9f66e5a878b0c661b348d7b4cb57f166614 /activejob/lib | |
parent | b93b39eff6829ee05ffec1cc8c505f69cbb53fdc (diff) | |
download | rails-31085a5cd47894b6fa397ca22fe8aa552d339ce8.tar.gz rails-31085a5cd47894b6fa397ca22fe8aa552d339ce8.tar.bz2 rails-31085a5cd47894b6fa397ca22fe8aa552d339ce8.zip |
Allow keyword arguments to work with ActiveJob
Unfortunately, the HashWithIndifferent access approach is insufficient
for our needs. It's perfectly reasonable to want to use keyword
arguments with Active Job, which we will see as a symbol keyed hash. For
Ruby to convert this back to keyword arguments, it must deserialize to a
symbol keyed hash.
There are two primary changes to the serialization behavior. We first
treat a HWIA separately, and mark it as such so we can convert it back
into a HWIA during deserialization.
For normal hashes, we keep a list of all symbol keys, and convert them
back to symbol keys after deserialization.
Fixes #18741.
Diffstat (limited to 'activejob/lib')
-rw-r--r-- | activejob/lib/active_job/arguments.rb | 49 |
1 files changed, 40 insertions, 9 deletions
diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb index 752be6898e..622c37098e 100644 --- a/activejob/lib/active_job/arguments.rb +++ b/activejob/lib/active_job/arguments.rb @@ -1,4 +1,4 @@ -require 'active_support/core_ext/hash/indifferent_access' +require 'active_support/core_ext/hash' module ActiveJob # Raised when an exception is raised during job arguments deserialization. @@ -44,7 +44,9 @@ module ActiveJob private GLOBALID_KEY = '_aj_globalid'.freeze - private_constant :GLOBALID_KEY + SYMBOL_KEYS_KEY = '_aj_symbol_keys'.freeze + WITH_INDIFFERENT_ACCESS_KEY = '_aj_hash_with_indifferent_access'.freeze + private_constant :GLOBALID_KEY, :SYMBOL_KEYS_KEY, :WITH_INDIFFERENT_ACCESS_KEY def serialize_argument(argument) case argument @@ -54,10 +56,15 @@ module ActiveJob { GLOBALID_KEY => argument.to_global_id.to_s } when Array argument.map { |arg| serialize_argument(arg) } + when ActiveSupport::HashWithIndifferentAccess + result = serialize_hash(argument) + result[WITH_INDIFFERENT_ACCESS_KEY] = serialize_argument(true) + result when Hash - argument.each_with_object({}) do |(key, value), hash| - hash[serialize_hash_key(key)] = serialize_argument(value) - end + symbol_keys = argument.each_key.grep(Symbol).map(&:to_s) + result = serialize_hash(argument) + result[SYMBOL_KEYS_KEY] = symbol_keys + result else raise SerializationError.new("Unsupported argument type: #{argument.class.name}") end @@ -75,7 +82,7 @@ module ActiveJob if serialized_global_id?(argument) deserialize_global_id argument else - deserialize_hash argument + deserialize_hash(argument) end else raise ArgumentError, "Can only deserialize primitive arguments: #{argument.inspect}" @@ -90,13 +97,27 @@ module ActiveJob GlobalID::Locator.locate hash[GLOBALID_KEY] end + def serialize_hash(argument) + argument.each_with_object({}) do |(key, value), hash| + hash[serialize_hash_key(key)] = serialize_argument(value) + end + end + def deserialize_hash(serialized_hash) - serialized_hash.each_with_object({}.with_indifferent_access) do |(key, value), hash| - hash[key] = deserialize_argument(value) + result = serialized_hash.transform_values { |v| deserialize_argument(v) } + if result.delete(WITH_INDIFFERENT_ACCESS_KEY) + result = result.with_indifferent_access + elsif symbol_keys = result.delete(SYMBOL_KEYS_KEY) + result = transform_symbol_keys(result, symbol_keys) end + result end - RESERVED_KEYS = [GLOBALID_KEY, GLOBALID_KEY.to_sym] + 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) @@ -109,5 +130,15 @@ module ActiveJob raise SerializationError.new("Only string and symbol hash keys may be serialized as job arguments, but #{key.inspect} is a #{key.class}") end end + + def transform_symbol_keys(hash, symbol_keys) + hash.transform_keys do |key| + if symbol_keys.include?(key) + key.to_sym + else + key + end + end + end end end |