From afe786d19ac31ee70228d22cbed4602d9abe320b Mon Sep 17 00:00:00 2001 From: Moritz Lawitschka Date: Tue, 29 Mar 2016 22:03:28 +0200 Subject: [PATCH] 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. --- .../adapter/json_api/deserialization.rb | 18 ++++++++++++------ lib/active_model_serializers/key_transform.rb | 10 ++++++++++ .../json_api/deserialization_test.rb | 17 +++++++++++++++-- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/lib/active_model_serializers/adapter/json_api/deserialization.rb b/lib/active_model_serializers/adapter/json_api/deserialization.rb index f5244355..ff1d3785 100644 --- a/lib/active_model_serializers/adapter/json_api/deserialization.rb +++ b/lib/active_model_serializers/adapter/json_api/deserialization.rb @@ -155,7 +155,7 @@ module ActiveModelSerializers # @api private def parse_attributes(attributes, options) - attributes + transform_keys(attributes, options) .map { |(k, v)| { field_key(k, options) => v } } .reduce({}, :merge) end @@ -182,23 +182,29 @@ module ActiveModelSerializers prefix_key = field_key(assoc_name, options).to_s.singularize hash = 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 - { "#{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 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 end # @api private def parse_relationships(relationships, options) - relationships - .map { |(k, v)| parse_relationship(k, v['data'], options) } + transform_keys(relationships, options) + .map { |(k, v)| parse_relationship(k, v[:data], options) } .reduce({}, :merge) end + + # @api private + def transform_keys(hash, options) + transform = options[:key_transform] || :underscore + KeyTransform.send(transform, hash) + end end end end diff --git a/lib/active_model_serializers/key_transform.rb b/lib/active_model_serializers/key_transform.rb index 03cf9cef..cf120547 100644 --- a/lib/active_model_serializers/key_transform.rb +++ b/lib/active_model_serializers/key_transform.rb @@ -32,6 +32,16 @@ module ActiveModelSerializers hash.deep_transform_keys! { |key| key.to_s.dasherize.to_sym } 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 def unaltered(hash) hash diff --git a/test/action_controller/json_api/deserialization_test.rb b/test/action_controller/json_api/deserialization_test.rb index 8a57d594..0528dc55 100644 --- a/test/action_controller/json_api/deserialization_test.rb +++ b/test/action_controller/json_api/deserialization_test.rb @@ -20,7 +20,10 @@ module ActionController 'id' => 'zorglub', 'attributes' => { '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' => { 'author' => { @@ -34,6 +37,12 @@ module ActionController { 'type' => 'comments', 'id' => '1' }, { 'type' => 'comments', 'id' => '2' } ] + }, + 'related-images' => { + 'data' => [ + { 'type' => 'image', 'id' => '7' }, + { 'type' => 'image', 'id' => '8' } + ] } } } @@ -46,9 +55,13 @@ module ActionController 'id' => 'zorglub', 'title' => 'Ember Hamster', 'src' => 'http://example.com/images/productivity.png', + 'image_width' => '200', + 'image_height' => '200', + 'image_size' => '1024', 'author_id' => nil, 'photographer_id' => '9', - 'comment_ids' => %w(1 2) + 'comment_ids' => %w(1 2), + 'related_image_ids' => %w(7 8) } assert_equal(expected, response)