From 2423ca49995a3ade274d32227de84b2bd7ba2cc0 Mon Sep 17 00:00:00 2001 From: "Ian C. Anderson" Date: Thu, 25 Aug 2016 15:21:27 -0400 Subject: [PATCH] Support key transformation for Attributes adapter (#1889) The `:attributes` adapter is the default one, but it did not support key transformation. This was very surprising behavior, since the "Configuration Options" page in the guides didn't mention that this behavior was not supported by the attributes adapter. This commit adds key transform support to the attributes adapter, and adds documentation about the default transform for the attributes adapter (which is `:unaltered`). This commit also handles arrays when transforming keys, which was needed in the case where you're serializing a collection with the Attributes adapter. With the JSON adapter, it was always guaranteed to pass a hash to the KeyTransform functions because of the top-level key. Since there is no top-level key for the Attributes adapter, the return value could be an array. --- CHANGELOG.md | 1 + docs/general/configuration_options.md | 1 + .../adapter/attributes.rb | 4 +- lib/active_model_serializers/key_transform.rb | 4 ++ .../key_transform_test.rb | 32 ++++++++++++++ test/adapter/attributes_test.rb | 43 +++++++++++++++++++ 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 test/adapter/attributes_test.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 751da693..49b3aab2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Features: - [#1791](https://github.com/rails-api/active_model_serializers/pull/1791) (@bf4, @youroff, @NullVoxPopuli) - Added `jsonapi_namespace_separator` config option. +- [#1889](https://github.com/rails-api/active_model_serializers/pull/1889) Support key transformation for Attributes adapter (@iancanderson, @danbee) Fixes: diff --git a/docs/general/configuration_options.md b/docs/general/configuration_options.md index e4ef79ae..b83399a2 100644 --- a/docs/general/configuration_options.md +++ b/docs/general/configuration_options.md @@ -46,6 +46,7 @@ Each adapter has a default key transform configured: | Adapter | Default Key Transform | |----|----| +| `Attributes` | `:unaltered` | | `Json` | `:unaltered` | | `JsonApi` | `:dash` | diff --git a/lib/active_model_serializers/adapter/attributes.rb b/lib/active_model_serializers/adapter/attributes.rb index 9ddf8516..79ca7b5f 100644 --- a/lib/active_model_serializers/adapter/attributes.rb +++ b/lib/active_model_serializers/adapter/attributes.rb @@ -4,7 +4,9 @@ module ActiveModelSerializers def serializable_hash(options = nil) options = serialization_options(options) options[:fields] ||= instance_options[:fields] - serializer.serializable_hash(instance_options, options, self) + serialized_hash = serializer.serializable_hash(instance_options, options, self) + + self.class.transform_key_casing!(serialized_hash, instance_options) end end end diff --git a/lib/active_model_serializers/key_transform.rb b/lib/active_model_serializers/key_transform.rb index 89143cee..d0e648e5 100644 --- a/lib/active_model_serializers/key_transform.rb +++ b/lib/active_model_serializers/key_transform.rb @@ -11,6 +11,7 @@ module ActiveModelSerializers # @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize} def camel(value) case value + when Array then value.map { |item| camel(item) } when Hash then value.deep_transform_keys! { |key| camel(key) } when Symbol then camel(value.to_s).to_sym when String then value.underscore.camelize @@ -25,6 +26,7 @@ module ActiveModelSerializers # @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize} def camel_lower(value) case value + when Array then value.map { |item| camel_lower(item) } when Hash then value.deep_transform_keys! { |key| camel_lower(key) } when Symbol then camel_lower(value.to_s).to_sym when String then value.underscore.camelize(:lower) @@ -40,6 +42,7 @@ module ActiveModelSerializers # @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L185-L187 ActiveSupport::Inflector.dasherize} def dash(value) case value + when Array then value.map { |item| dash(item) } when Hash then value.deep_transform_keys! { |key| dash(key) } when Symbol then dash(value.to_s).to_sym when String then value.underscore.dasherize @@ -55,6 +58,7 @@ module ActiveModelSerializers # @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L89-L98 ActiveSupport::Inflector.underscore} def underscore(value) case value + when Array then value.map { |item| underscore(item) } when Hash then value.deep_transform_keys! { |key| underscore(key) } when Symbol then underscore(value.to_s).to_sym when String then value.underscore diff --git a/test/active_model_serializers/key_transform_test.rb b/test/active_model_serializers/key_transform_test.rb index c25f6ca4..b4ff4d31 100644 --- a/test/active_model_serializers/key_transform_test.rb +++ b/test/active_model_serializers/key_transform_test.rb @@ -60,6 +60,14 @@ module ActiveModelSerializers { value: nil, expected: nil + }, + { + value: [ + { some_value: 'value' } + ], + expected: [ + { SomeValue: 'value' } + ] } ] scenarios.each do |s| @@ -126,6 +134,14 @@ module ActiveModelSerializers { value: nil, expected: nil + }, + { + value: [ + { some_value: 'value' } + ], + expected: [ + { someValue: 'value' } + ] } ] scenarios.each do |s| @@ -188,6 +204,14 @@ module ActiveModelSerializers { value: nil, expected: nil + }, + { + value: [ + { 'some_value' => 'value' } + ], + expected: [ + { 'some-value' => 'value' } + ] } ] scenarios.each do |s| @@ -254,6 +278,14 @@ module ActiveModelSerializers { value: nil, expected: nil + }, + { + value: [ + { 'some-value' => 'value' } + ], + expected: [ + { 'some_value' => 'value' } + ] } ] scenarios.each do |s| diff --git a/test/adapter/attributes_test.rb b/test/adapter/attributes_test.rb new file mode 100644 index 00000000..dee0c775 --- /dev/null +++ b/test/adapter/attributes_test.rb @@ -0,0 +1,43 @@ +require 'test_helper' + +module ActiveModelSerializers + module Adapter + class AttributesTest < ActiveSupport::TestCase + class Person + include ActiveModel::Model + include ActiveModel::Serialization + + attr_accessor :first_name, :last_name + end + + class PersonSerializer < ActiveModel::Serializer + attributes :first_name, :last_name + end + + def setup + ActionController::Base.cache_store.clear + end + + def test_serializable_hash + person = Person.new(first_name: 'Arthur', last_name: 'Dent') + serializer = PersonSerializer.new(person) + adapter = ActiveModelSerializers::Adapter::Attributes.new(serializer) + + assert_equal({ first_name: 'Arthur', last_name: 'Dent' }, + adapter.serializable_hash) + end + + def test_serializable_hash_with_transform_key_casing + person = Person.new(first_name: 'Arthur', last_name: 'Dent') + serializer = PersonSerializer.new(person) + adapter = ActiveModelSerializers::Adapter::Attributes.new( + serializer, + key_transform: :camel_lower + ) + + assert_equal({ firstName: 'Arthur', lastName: 'Dent' }, + adapter.serializable_hash) + end + end + end +end