Skip to content

Commit 2423ca4

Browse files
iancandersonNullVoxPopuli
authored andcommitted
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.
1 parent 49ee823 commit 2423ca4

File tree

6 files changed

+84
-1
lines changed

6 files changed

+84
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Features:
88

99
- [#1791](https://github.com/rails-api/active_model_serializers/pull/1791) (@bf4, @youroff, @NullVoxPopuli)
1010
- Added `jsonapi_namespace_separator` config option.
11+
- [#1889](https://github.com/rails-api/active_model_serializers/pull/1889) Support key transformation for Attributes adapter (@iancanderson, @danbee)
1112

1213
Fixes:
1314

docs/general/configuration_options.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Each adapter has a default key transform configured:
4646

4747
| Adapter | Default Key Transform |
4848
|----|----|
49+
| `Attributes` | `:unaltered` |
4950
| `Json` | `:unaltered` |
5051
| `JsonApi` | `:dash` |
5152

lib/active_model_serializers/adapter/attributes.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ class Attributes < Base
44
def serializable_hash(options = nil)
55
options = serialization_options(options)
66
options[:fields] ||= instance_options[:fields]
7-
serializer.serializable_hash(instance_options, options, self)
7+
serialized_hash = serializer.serializable_hash(instance_options, options, self)
8+
9+
self.class.transform_key_casing!(serialized_hash, instance_options)
810
end
911
end
1012
end

lib/active_model_serializers/key_transform.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module KeyTransform
1111
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize}
1212
def camel(value)
1313
case value
14+
when Array then value.map { |item| camel(item) }
1415
when Hash then value.deep_transform_keys! { |key| camel(key) }
1516
when Symbol then camel(value.to_s).to_sym
1617
when String then value.underscore.camelize
@@ -25,6 +26,7 @@ def camel(value)
2526
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize}
2627
def camel_lower(value)
2728
case value
29+
when Array then value.map { |item| camel_lower(item) }
2830
when Hash then value.deep_transform_keys! { |key| camel_lower(key) }
2931
when Symbol then camel_lower(value.to_s).to_sym
3032
when String then value.underscore.camelize(:lower)
@@ -40,6 +42,7 @@ def camel_lower(value)
4042
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L185-L187 ActiveSupport::Inflector.dasherize}
4143
def dash(value)
4244
case value
45+
when Array then value.map { |item| dash(item) }
4346
when Hash then value.deep_transform_keys! { |key| dash(key) }
4447
when Symbol then dash(value.to_s).to_sym
4548
when String then value.underscore.dasherize
@@ -55,6 +58,7 @@ def dash(value)
5558
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L89-L98 ActiveSupport::Inflector.underscore}
5659
def underscore(value)
5760
case value
61+
when Array then value.map { |item| underscore(item) }
5862
when Hash then value.deep_transform_keys! { |key| underscore(key) }
5963
when Symbol then underscore(value.to_s).to_sym
6064
when String then value.underscore

test/active_model_serializers/key_transform_test.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ def test_camel
6060
{
6161
value: nil,
6262
expected: nil
63+
},
64+
{
65+
value: [
66+
{ some_value: 'value' }
67+
],
68+
expected: [
69+
{ SomeValue: 'value' }
70+
]
6371
}
6472
]
6573
scenarios.each do |s|
@@ -126,6 +134,14 @@ def test_camel_lower
126134
{
127135
value: nil,
128136
expected: nil
137+
},
138+
{
139+
value: [
140+
{ some_value: 'value' }
141+
],
142+
expected: [
143+
{ someValue: 'value' }
144+
]
129145
}
130146
]
131147
scenarios.each do |s|
@@ -188,6 +204,14 @@ def test_dash
188204
{
189205
value: nil,
190206
expected: nil
207+
},
208+
{
209+
value: [
210+
{ 'some_value' => 'value' }
211+
],
212+
expected: [
213+
{ 'some-value' => 'value' }
214+
]
191215
}
192216
]
193217
scenarios.each do |s|
@@ -254,6 +278,14 @@ def test_underscore
254278
{
255279
value: nil,
256280
expected: nil
281+
},
282+
{
283+
value: [
284+
{ 'some-value' => 'value' }
285+
],
286+
expected: [
287+
{ 'some_value' => 'value' }
288+
]
257289
}
258290
]
259291
scenarios.each do |s|

test/adapter/attributes_test.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
require 'test_helper'
2+
3+
module ActiveModelSerializers
4+
module Adapter
5+
class AttributesTest < ActiveSupport::TestCase
6+
class Person
7+
include ActiveModel::Model
8+
include ActiveModel::Serialization
9+
10+
attr_accessor :first_name, :last_name
11+
end
12+
13+
class PersonSerializer < ActiveModel::Serializer
14+
attributes :first_name, :last_name
15+
end
16+
17+
def setup
18+
ActionController::Base.cache_store.clear
19+
end
20+
21+
def test_serializable_hash
22+
person = Person.new(first_name: 'Arthur', last_name: 'Dent')
23+
serializer = PersonSerializer.new(person)
24+
adapter = ActiveModelSerializers::Adapter::Attributes.new(serializer)
25+
26+
assert_equal({ first_name: 'Arthur', last_name: 'Dent' },
27+
adapter.serializable_hash)
28+
end
29+
30+
def test_serializable_hash_with_transform_key_casing
31+
person = Person.new(first_name: 'Arthur', last_name: 'Dent')
32+
serializer = PersonSerializer.new(person)
33+
adapter = ActiveModelSerializers::Adapter::Attributes.new(
34+
serializer,
35+
key_transform: :camel_lower
36+
)
37+
38+
assert_equal({ firstName: 'Arthur', lastName: 'Dent' },
39+
adapter.serializable_hash)
40+
end
41+
end
42+
end
43+
end

0 commit comments

Comments
 (0)