Properly deserialize dasherized keys

The JSON API adapater dasherizes every key, but the deserializer left the keys
unaltered. Thus, the client had to send underscored keys in the request body in
order for Rails to properly match sent values to model attributes.

This commit adds automatic key transformation on deserialization. Per default the
deserializer transforms the keys to underscore, but this behaviour can also be
changed by including `key_transform` in the deserializer options.
This commit is contained in:
Moritz Lawitschka 2016-03-29 22:03:28 +02:00
parent c7b2916f37
commit afe786d19a
3 changed files with 37 additions and 8 deletions

View File

@ -155,7 +155,7 @@ module ActiveModelSerializers
# @api private # @api private
def parse_attributes(attributes, options) def parse_attributes(attributes, options)
attributes transform_keys(attributes, options)
.map { |(k, v)| { field_key(k, options) => v } } .map { |(k, v)| { field_key(k, options) => v } }
.reduce({}, :merge) .reduce({}, :merge)
end end
@ -182,23 +182,29 @@ module ActiveModelSerializers
prefix_key = field_key(assoc_name, options).to_s.singularize prefix_key = field_key(assoc_name, options).to_s.singularize
hash = hash =
if assoc_data.is_a?(Array) if assoc_data.is_a?(Array)
{ "#{prefix_key}_ids".to_sym => assoc_data.map { |ri| ri['id'] } } { "#{prefix_key}_ids".to_sym => assoc_data.map { |ri| ri[:id] } }
else else
{ "#{prefix_key}_id".to_sym => assoc_data ? assoc_data['id'] : nil } { "#{prefix_key}_id".to_sym => assoc_data && assoc_data.is_a?(Hash) ? assoc_data[:id] : nil }
end end
polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym) polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym)
hash["#{prefix_key}_type".to_sym] = assoc_data['type'] if polymorphic hash["#{prefix_key}_type".to_sym] = assoc_data[:type] if polymorphic
hash hash
end end
# @api private # @api private
def parse_relationships(relationships, options) def parse_relationships(relationships, options)
relationships transform_keys(relationships, options)
.map { |(k, v)| parse_relationship(k, v['data'], options) } .map { |(k, v)| parse_relationship(k, v[:data], options) }
.reduce({}, :merge) .reduce({}, :merge)
end end
# @api private
def transform_keys(hash, options)
transform = options[:key_transform] || :underscore
KeyTransform.send(transform, hash)
end
end end
end end
end end

View File

@ -32,6 +32,16 @@ module ActiveModelSerializers
hash.deep_transform_keys! { |key| key.to_s.dasherize.to_sym } hash.deep_transform_keys! { |key| key.to_s.dasherize.to_sym }
end end
# Transforms keys to underscore.
# This is the default case for deserialization in the JsonApi adapter.
#
# @example:
# "some-key" => "some_key",
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L89-L98 ActiveSupport::Inflector.underscore}
def underscore(hash)
hash.deep_transform_keys! { |key| key.to_s.underscore.to_sym }
end
# Returns the hash unaltered # Returns the hash unaltered
def unaltered(hash) def unaltered(hash)
hash hash

View File

@ -20,7 +20,10 @@ module ActionController
'id' => 'zorglub', 'id' => 'zorglub',
'attributes' => { 'attributes' => {
'title' => 'Ember Hamster', 'title' => 'Ember Hamster',
'src' => 'http://example.com/images/productivity.png' 'src' => 'http://example.com/images/productivity.png',
'image-width' => '200',
'imageHeight' => '200',
'ImageSize' => '1024'
}, },
'relationships' => { 'relationships' => {
'author' => { 'author' => {
@ -34,6 +37,12 @@ module ActionController
{ 'type' => 'comments', 'id' => '1' }, { 'type' => 'comments', 'id' => '1' },
{ 'type' => 'comments', 'id' => '2' } { 'type' => 'comments', 'id' => '2' }
] ]
},
'related-images' => {
'data' => [
{ 'type' => 'image', 'id' => '7' },
{ 'type' => 'image', 'id' => '8' }
]
} }
} }
} }
@ -46,9 +55,13 @@ module ActionController
'id' => 'zorglub', 'id' => 'zorglub',
'title' => 'Ember Hamster', 'title' => 'Ember Hamster',
'src' => 'http://example.com/images/productivity.png', 'src' => 'http://example.com/images/productivity.png',
'image_width' => '200',
'image_height' => '200',
'image_size' => '1024',
'author_id' => nil, 'author_id' => nil,
'photographer_id' => '9', 'photographer_id' => '9',
'comment_ids' => %w(1 2) 'comment_ids' => %w(1 2),
'related_image_ids' => %w(7 8)
} }
assert_equal(expected, response) assert_equal(expected, response)