Skip to content

Commit 3d2b759

Browse files
committed
Add global config option for key transform
1 parent 8e699e1 commit 3d2b759

File tree

4 files changed

+173
-9
lines changed

4 files changed

+173
-9
lines changed

lib/active_model/serializer/configuration.rb

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def config.array_serializer
2626
# Make JSON API top-level jsonapi member opt-in
2727
# ref: http://jsonapi.org/format/#document-top-level
2828
config.jsonapi_include_toplevel_object = false
29+
config.key_transform = nil
2930

3031
config.schema_path = 'test/support/schemas'
3132
end

lib/active_model_serializers/adapter/base.rb

+12-1
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,20 @@ def default_key_transform
5959
:unaltered
6060
end
6161

62+
# Determines the transform to use in order of precedence:
63+
# serialization context, global config, adapter default.
64+
#
65+
# @param serialization_context [Object] the SerializationContext
66+
# @return [Symbol] the transform to use
67+
def key_transform(serialization_context)
68+
serialization_context.key_transform ||
69+
ActiveModelSerializers.config.key_transform ||
70+
default_key_transform
71+
end
72+
6273
def transform_key_casing!(value, serialization_context)
6374
return value unless serialization_context
64-
transform = serialization_context.key_transform || default_key_transform
75+
transform = key_transform(serialization_context)
6576
KeyTransform.send(transform, value)
6677
end
6778
end

test/adapter/json/key_case_test.rb

+20
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,26 @@ def test_key_transform_default
3232
}, @adapter.serializable_hash(@options))
3333
end
3434

35+
def test_key_transform_global_config
36+
mock_request
37+
result = with_config(key_transform: :camel_lower) do
38+
@adapter.serializable_hash(@options)
39+
end
40+
assert_equal({
41+
blog: { id: 1, specialAttribute: 'neat', articles: nil }
42+
}, result)
43+
end
44+
45+
def test_key_transform_serialization_ctx_overrides_global_config
46+
mock_request(:camel)
47+
result = with_config(key_transform: :camel_lower) do
48+
@adapter.serializable_hash(@options)
49+
end
50+
assert_equal({
51+
Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil }
52+
}, result)
53+
end
54+
3555
def test_key_transform_undefined
3656
mock_request(:blam)
3757
result = nil

test/adapter/json_api/key_case_test.rb

+140-8
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,78 @@ def test_success_document_key_transform_default
9898
}, result)
9999
end
100100

101+
def test_success_document_key_transform_global_config
102+
mock_request
103+
result = with_config(key_transform: :camel_lower) do
104+
serializer = PostSerializer.new(@post)
105+
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
106+
adapter.serializable_hash(@options)
107+
end
108+
assert_equal({
109+
data: {
110+
id: '1337',
111+
type: 'posts',
112+
attributes: {
113+
title: 'Title 1',
114+
body: 'Body 1',
115+
publishAt: @publish_at
116+
},
117+
relationships: {
118+
author: {
119+
data: { id: '1', type: 'authors' }
120+
},
121+
comments: {
122+
data: [
123+
{ id: '7', type: 'comments' },
124+
{ id: '12', type: 'comments' }
125+
] }
126+
},
127+
links: {
128+
self: 'http://example.com/posts/1337',
129+
postAuthors: 'http://example.com/posts/1337/authors',
130+
subscriberComments: 'http://example.com/posts/1337/comments'
131+
},
132+
meta: { rating: 5, favoriteCount: 10 }
133+
}
134+
}, result)
135+
end
136+
137+
def test_success_doc_key_transform_serialization_ctx_overrides_global
138+
mock_request(:camel)
139+
result = with_config(key_transform: :camel_lower) do
140+
serializer = PostSerializer.new(@post)
141+
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
142+
adapter.serializable_hash(@options)
143+
end
144+
assert_equal({
145+
Data: {
146+
Id: '1337',
147+
Type: 'posts',
148+
Attributes: {
149+
Title: 'Title 1',
150+
Body: 'Body 1',
151+
PublishAt: @publish_at
152+
},
153+
Relationships: {
154+
Author: {
155+
Data: { Id: '1', Type: 'authors' }
156+
},
157+
Comments: {
158+
Data: [
159+
{ Id: '7', Type: 'comments' },
160+
{ Id: '12', Type: 'comments' }
161+
] }
162+
},
163+
Links: {
164+
Self: 'http://example.com/posts/1337',
165+
PostAuthors: 'http://example.com/posts/1337/authors',
166+
SubscriberComments: 'http://example.com/posts/1337/comments'
167+
},
168+
Meta: { Rating: 5, FavoriteCount: 10 }
169+
}
170+
}, result)
171+
end
172+
101173
def test_success_document_key_transform_dashed
102174
mock_request(:dashed)
103175
serializer = PostSerializer.new(@post)
@@ -245,22 +317,76 @@ def test_success_document_key_transform_camel_lower
245317

246318
def test_error_document_key_transform_default
247319
mock_request
248-
249320
resource = ModelWithErrors.new
250321
resource.errors.add(:published_at, 'must be in the future')
251322
resource.errors.add(:title, 'must be longer')
252-
253323
serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
254324
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
255325
result = adapter.serializable_hash(@options)
256-
257326
expected_errors_object =
258327
{ :errors =>
259328
[
260-
{ :source => { :pointer => '/data/attributes/published_at' }, :detail => 'must be in the future' },
261-
{ :source => { :pointer => '/data/attributes/title' }, :detail => 'must be longer' }
329+
{
330+
:source => { :pointer => '/data/attributes/published_at' },
331+
:detail => 'must be in the future' },
332+
{
333+
:source => { :pointer => '/data/attributes/title' },
334+
:detail => 'must be longer'
335+
}
262336
]
263-
}
337+
}
338+
assert_equal expected_errors_object, result
339+
end
340+
341+
def test_error_document_key_transform_global_config
342+
mock_request
343+
result = with_config(key_transform: :camel) do
344+
resource = ModelWithErrors.new
345+
resource.errors.add(:published_at, 'must be in the future')
346+
resource.errors.add(:title, 'must be longer')
347+
serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
348+
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
349+
adapter.serializable_hash(@options)
350+
end
351+
expected_errors_object =
352+
{ :Errors =>
353+
[
354+
{
355+
:Source => { :Pointer => '/data/attributes/published_at' },
356+
:Detail => 'must be in the future'
357+
},
358+
{
359+
:Source => { :Pointer => '/data/attributes/title' },
360+
:Detail => 'must be longer'
361+
}
362+
]
363+
}
364+
assert_equal expected_errors_object, result
365+
end
366+
367+
def test_error_document_key_transform_serialization_ctx_overrides_global
368+
mock_request(:camel)
369+
result = with_config(key_transform: :camel_lower) do
370+
resource = ModelWithErrors.new
371+
resource.errors.add(:published_at, 'must be in the future')
372+
resource.errors.add(:title, 'must be longer')
373+
serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
374+
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
375+
adapter.serializable_hash(@options)
376+
end
377+
expected_errors_object =
378+
{ :Errors =>
379+
[
380+
{
381+
:Source => { :Pointer => '/data/attributes/published_at' },
382+
:Detail => 'must be in the future'
383+
},
384+
{
385+
:Source => { :Pointer => '/data/attributes/title' },
386+
:Detail => 'must be longer'
387+
}
388+
]
389+
}
264390
assert_equal expected_errors_object, result
265391
end
266392

@@ -278,8 +404,14 @@ def test_error_document_key_transform_dashed
278404
expected_errors_object =
279405
{ :errors =>
280406
[
281-
{ :source => { :pointer => '/data/attributes/published_at' }, :detail => 'must be in the future' },
282-
{ :source => { :pointer => '/data/attributes/title' }, :detail => 'must be longer' }
407+
{
408+
:source => { :pointer => '/data/attributes/published_at' },
409+
:detail => 'must be in the future'
410+
},
411+
{
412+
:source => { :pointer => '/data/attributes/title' },
413+
:detail => 'must be longer'
414+
}
283415
]
284416
}
285417
assert_equal expected_errors_object, result

0 commit comments

Comments
 (0)