Merge pull request #1574 from remear/key-casing

Provide key transformation
This commit is contained in:
Ben Mills 2016-03-15 13:42:24 -06:00
commit 9e992358d8
15 changed files with 979 additions and 22 deletions

View File

@ -3,6 +3,7 @@
Breaking changes: Breaking changes:
Features: Features:
- [#1574](https://github.com/rails-api/active_model_serializers/pull/1574) Provide key translation. (@remear)
- [#1494](https://github.com/rails-api/active_model_serializers/pull/1494) Make serializers serializalbe - [#1494](https://github.com/rails-api/active_model_serializers/pull/1494) Make serializers serializalbe
(using the Attributes adapter by default). (@bf4) (using the Attributes adapter by default). (@bf4)
- [#1550](https://github.com/rails-api/active_model_serializers/pull/1550) Add - [#1550](https://github.com/rails-api/active_model_serializers/pull/1550) Add

View File

@ -2,26 +2,95 @@
# Configuration Options # Configuration Options
The following configuration options can be set on `ActiveModelSerializers.config`, The following configuration options can be set on
preferably inside an initializer. `ActiveModelSerializers.config`, preferably inside an initializer.
## General ## General
- `adapter`: The [adapter](adapters.md) to use. Possible values: `:attributes, :json, :json_api`. Default: `:attributes`. ##### adapter
- `serializer_lookup_enabled`: When `false`, serializers must be explicitly specified. Default: `true`
The [adapter](adapters.md) to use.
Possible values:
- `:attributes` (default)
- `:json`
- `:json_api`
##### serializer_lookup_enabled
Enable automatic serializer lookup.
Possible values:
- `true` (default)
- `false`
When `false`, serializers must be explicitly specified.
##### key_transform
The [key transform](key_transform.md) to use.
Possible values:
- `:camel` - ExampleKey
- `:camel_lower` - exampleKey
- `:dashed` - example-key
- `:unaltered` - the original, unaltered key
- `nil` - use the adapter default
Each adapter has a default key transform configured:
- `Json` - `:unaltered`
- `JsonApi` - `:dashed`
`config.key_transform` is a global override of the adapter default. Adapters
still prefer the render option `:key_transform` over this setting.
## JSON API ## JSON API
- `jsonapi_resource_type`: Whether the `type` attributes of resources should be singular or plural. Possible values: `:singular, :plural`. Default: `:plural`.
- `jsonapi_include_toplevel_object`: Whether to include a [top level JSON API member](http://jsonapi.org/format/#document-jsonapi-object) ##### jsonapi_resource_type
in the response document.
Default: `false`. Sets whether the [type](http://jsonapi.org/format/#document-resource-identifier-objects)
- Used when `jsonapi_include_toplevel_object` is `true`: of the resource should be `singularized` or `pluralized` when it is not
- `jsonapi_version`: The latest version of the spec the API conforms to. [explicitly specified by the serializer](https://github.com/rails-api/active_model_serializers/blob/master/docs/general/serializers.md#type)
Default: `'1.0'`.
- `jsonapi_toplevel_meta`: Optional metadata. Not included if empty. Possible values:
Default: `{}`.
- `:singular`
- `:plural` (default)
##### jsonapi_include_toplevel_object
Include a [top level jsonapi member](http://jsonapi.org/format/#document-jsonapi-object)
in the response document.
Possible values:
- `true`
- `false` (default)
##### jsonapi_version
The latest version of the spec to which the API conforms.
Default: `'1.0'`.
*Used when `jsonapi_include_toplevel_object` is `true`*
##### jsonapi_toplevel_meta
Optional top-level metadata. Not included if empty.
Default: `{}`.
*Used when `jsonapi_include_toplevel_object` is `true`*
## Hooks ## Hooks
To run a hook when ActiveModelSerializers is loaded, use `ActiveSupport.on_load(:action_controller) do end` To run a hook when ActiveModelSerializers is loaded, use
`ActiveSupport.on_load(:action_controller) do end`

View File

@ -0,0 +1,34 @@
[Back to Guides](../README.md)
# Key Transforms
Key transforms modify the keys in serialized responses.
Provided key transforms:
- `:camel` - ExampleKey
- `:camel_lower` - exampleKey
- `:dashed` - example-key
- `:unaltered` - the original, unaltered key
- `nil` - use the adapter default
Key translation precedence is as follows:
##### SerializableResource option
`key_transform` is provided as an option via render.
```render json: posts, each_serializer: PostSerializer, key_transform: :camel_lower```
##### Configuration option
`key_transform` is set in `ActiveModelSerializers.config.key_transform`.
```ActiveModelSerializers.config.key_transform = :camel_lower```
##### Adapter default
Each adapter has a default key transform configured:
- `Json` - `:unaltered`
- `JsonApi` - `:dashed`

View File

@ -79,6 +79,12 @@ PR please :)
PR please :) PR please :)
#### key_transform
```render json: posts, each_serializer: PostSerializer, key_transform: :camel_lower```
See [Key Transforms](key_transforms.md) for more informaiton.
#### meta #### meta
A `meta` member can be used to include non-standard meta-information. `meta` can A `meta` member can be used to include non-standard meta-information. `meta` can

View File

@ -56,7 +56,9 @@ module ActionController
[:_render_option_json, :_render_with_renderer_json].each do |renderer_method| [:_render_option_json, :_render_with_renderer_json].each do |renderer_method|
define_method renderer_method do |resource, options| define_method renderer_method do |resource, options|
options.fetch(:serialization_context) { options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request) } options.fetch(:serialization_context) do
options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request, options)
end
serializable_resource = get_serializer(resource, options) serializable_resource = get_serializer(resource, options)
super(serializable_resource, options) super(serializable_resource, options)
end end

View File

@ -26,6 +26,7 @@ module ActiveModel
# Make JSON API top-level jsonapi member opt-in # Make JSON API top-level jsonapi member opt-in
# ref: http://jsonapi.org/format/#document-top-level # ref: http://jsonapi.org/format/#document-top-level
config.jsonapi_include_toplevel_object = false config.jsonapi_include_toplevel_object = false
config.key_transform = nil
config.schema_path = 'test/support/schemas' config.schema_path = 'test/support/schemas'
end end

View File

@ -1,3 +1,5 @@
require 'active_model_serializers/key_transform'
module ActiveModelSerializers module ActiveModelSerializers
module Adapter module Adapter
class Base class Base
@ -51,6 +53,27 @@ module ActiveModelSerializers
json[meta_key] = meta unless meta.blank? json[meta_key] = meta unless meta.blank?
json json
end end
def default_key_transform
:unaltered
end
# Determines the transform to use in order of precedence:
# serialization context, global config, adapter default.
#
# @param serialization_context [Object] the SerializationContext
# @return [Symbol] the transform to use
def key_transform(serialization_context)
serialization_context.key_transform ||
ActiveModelSerializers.config.key_transform ||
default_key_transform
end
def transform_key_casing!(value, serialization_context)
return value unless serialization_context
transform = key_transform(serialization_context)
KeyTransform.send(transform, value)
end
end end
end end
end end

View File

@ -3,7 +3,8 @@ module ActiveModelSerializers
class Json < Base class Json < Base
def serializable_hash(options = nil) def serializable_hash(options = nil)
options ||= {} options ||= {}
{ root => Attributes.new(serializer, instance_options).serializable_hash(options) } serialized_hash = { root => Attributes.new(serializer, instance_options).serializable_hash(options) }
transform_key_casing!(serialized_hash, options[:serialization_context])
end end
end end
end end

View File

@ -37,15 +37,20 @@ module ActiveModelSerializers
@fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields)) @fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
end end
def default_key_transform
:dashed
end
# {http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure} # {http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure}
# {http://jsonapi.org/format/#document-top-level data and errors MUST NOT coexist in the same document.} # {http://jsonapi.org/format/#document-top-level data and errors MUST NOT coexist in the same document.}
def serializable_hash(options = nil) def serializable_hash(options = nil)
options ||= {} options ||= {}
if serializer.success? document = if serializer.success?
success_document(options) success_document(options)
else else
failure_document failure_document
end end
transform_key_casing!(document, options[:serialization_context])
end end
# {http://jsonapi.org/format/#document-top-level Primary data} # {http://jsonapi.org/format/#document-top-level Primary data}

View File

@ -0,0 +1,40 @@
require 'active_support/core_ext/hash/keys'
module ActiveModelSerializers
module KeyTransform
module_function
# Transforms keys to UpperCamelCase or PascalCase.
#
# @example:
# "some_key" => "SomeKey",
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize}
def camel(hash)
hash.deep_transform_keys! { |key| key.to_s.camelize.to_sym }
end
# Transforms keys to camelCase.
#
# @example:
# "some_key" => "someKey",
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize}
def camel_lower(hash)
hash.deep_transform_keys! { |key| key.to_s.camelize(:lower).to_sym }
end
# Transforms keys to dashed-case.
# This is the default case for the JsonApi adapter.
#
# @example:
# "some_key" => "some-key",
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L185-L187 ActiveSupport::Inflector.dasherize}
def dashed(hash)
hash.deep_transform_keys! { |key| key.to_s.dasherize.to_sym }
end
# Returns the hash unaltered
def unaltered(hash)
hash
end
end
end

View File

@ -4,13 +4,14 @@ module ActiveModelSerializers
attr_writer :url_helpers, :default_url_options attr_writer :url_helpers, :default_url_options
end end
attr_reader :request_url, :query_parameters attr_reader :request_url, :query_parameters, :key_transform
def initialize(request, options = {}) def initialize(request, options = {})
@request_url = request.original_url[/\A[^?]+/] @request_url = request.original_url[/\A[^?]+/]
@query_parameters = request.query_parameters @query_parameters = request.query_parameters
@url_helpers = options.delete(:url_helpers) || self.class.url_helpers @url_helpers = options.delete(:url_helpers) || self.class.url_helpers
@default_url_options = options.delete(:default_url_options) || self.class.default_url_options @default_url_options = options.delete(:default_url_options) || self.class.default_url_options
@key_transform = options.delete(:key_transform)
end end
def self.url_helpers def self.url_helpers

View File

@ -0,0 +1,180 @@
require 'test_helper'
module ActionController
module Serialization
class JsonApi
class KeyTransformTest < ActionController::TestCase
class KeyTransformTestController < ActionController::Base
Post = Class.new(::Model)
class PostSerializer < ActiveModel::Serializer
type 'posts'
attributes :title, :body, :publish_at
belongs_to :author
has_many :comments
link(:post_authors) { 'https://example.com/post_authors' }
meta do
{
rating: 5,
favorite_count: 10
}
end
end
Author = Class.new(::Model)
class AuthorSerializer < ActiveModel::Serializer
type 'authors'
attributes :first_name, :last_name
end
Comment = Class.new(::Model)
class CommentSerializer < ActiveModel::Serializer
type 'comments'
attributes :body
belongs_to :author
end
def setup_post
ActionController::Base.cache_store.clear
@author = Author.new(id: 1, first_name: 'Bob', last_name: 'Jones')
@comment1 = Comment.new(id: 7, body: 'cool', author: @author)
@comment2 = Comment.new(id: 12, body: 'awesome', author: @author)
@post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1',
author: @author, comments: [@comment1, @comment2],
publish_at: '2020-03-16T03:55:25.291Z')
@comment1.post = @post
@comment2.post = @post
end
def render_resource_with_key_transform
setup_post
render json: @post, serializer: PostSerializer, adapter: :json_api,
key_transform: :camel
end
def render_resource_with_key_transform_nil
setup_post
render json: @post, serializer: PostSerializer, adapter: :json_api,
key_transform: nil
end
def render_resource_with_key_transform_with_global_config
setup_post
old_transform = ActiveModelSerializers.config.key_transform
ActiveModelSerializers.config.key_transform = :camel_lower
render json: @post, serializer: PostSerializer, adapter: :json_api
ActiveModelSerializers.config.key_transform = old_transform
end
end
tests KeyTransformTestController
def test_render_resource_with_key_transform
get :render_resource_with_key_transform
response = JSON.parse(@response.body)
expected = {
'Data' => {
'Id' => '1337',
'Type' => 'posts',
'Attributes' => {
'Title' => 'Title 1',
'Body' => 'Body 1',
'PublishAt' => '2020-03-16T03:55:25.291Z'
},
'Relationships' => {
'Author' => {
'Data' => {
'Id' => '1',
'Type' => 'authors'
}
},
'Comments' => {
'Data' => [
{ 'Id' => '7', 'Type' => 'comments' },
{ 'Id' => '12', 'Type' => 'comments' }
]
}
},
'Links' => {
'PostAuthors' => 'https://example.com/post_authors'
},
'Meta' => { 'Rating' => 5, 'FavoriteCount' => 10 }
}
}
assert_equal expected, response
end
def test_render_resource_with_key_transform_nil
get :render_resource_with_key_transform_nil
response = JSON.parse(@response.body)
expected = {
'data' => {
'id' => '1337',
'type' => 'posts',
'attributes' => {
'title' => 'Title 1',
'body' => 'Body 1',
'publish-at' => '2020-03-16T03:55:25.291Z'
},
'relationships' => {
'author' => {
'data' => {
'id' => '1',
'type' => 'authors'
}
},
'comments' => {
'data' => [
{ 'id' => '7', 'type' => 'comments' },
{ 'id' => '12', 'type' => 'comments' }
]
}
},
'links' => {
'post-authors' => 'https://example.com/post_authors'
},
'meta' => { 'rating' => 5, 'favorite-count' => 10 }
}
}
assert_equal expected, response
end
def test_render_resource_with_key_transform_with_global_config
get :render_resource_with_key_transform_with_global_config
response = JSON.parse(@response.body)
expected = {
'data' => {
'id' => '1337',
'type' => 'posts',
'attributes' => {
'title' => 'Title 1',
'body' => 'Body 1',
'publishAt' => '2020-03-16T03:55:25.291Z'
},
'relationships' => {
'author' => {
'data' => {
'id' => '1',
'type' => 'authors'
}
},
'comments' => {
'data' => [
{ 'id' => '7', 'type' => 'comments' },
{ 'id' => '12', 'type' => 'comments' }
]
}
},
'links' => {
'postAuthors' => 'https://example.com/post_authors'
},
'meta' => { 'rating' => 5, 'favoriteCount' => 10 }
}
}
assert_equal expected, response
end
end
end
end
end

View File

@ -0,0 +1,93 @@
require 'test_helper'
module ActiveModelSerializers
module Adapter
class Json
class KeyCaseTest < ActiveSupport::TestCase
def mock_request(key_transform = nil)
context = Minitest::Mock.new
context.expect(:request_url, URI)
context.expect(:query_parameters, {})
context.expect(:key_transform, key_transform)
@options = {}
@options[:serialization_context] = context
end
Post = Class.new(::Model)
class PostSerializer < ActiveModel::Serializer
attributes :id, :title, :body, :publish_at
end
def setup
ActionController::Base.cache_store.clear
@blog = Blog.new(id: 1, name: 'My Blog!!', special_attribute: 'neat')
serializer = CustomBlogSerializer.new(@blog)
@adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
end
def test_key_transform_default
mock_request
assert_equal({
blog: { id: 1, special_attribute: 'neat', articles: nil }
}, @adapter.serializable_hash(@options))
end
def test_key_transform_global_config
mock_request
result = with_config(key_transform: :camel_lower) do
@adapter.serializable_hash(@options)
end
assert_equal({
blog: { id: 1, specialAttribute: 'neat', articles: nil }
}, result)
end
def test_key_transform_serialization_ctx_overrides_global_config
mock_request(:camel)
result = with_config(key_transform: :camel_lower) do
@adapter.serializable_hash(@options)
end
assert_equal({
Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil }
}, result)
end
def test_key_transform_undefined
mock_request(:blam)
result = nil
assert_raises NoMethodError do
result = @adapter.serializable_hash(@options)
end
end
def test_key_transform_dashed
mock_request(:dashed)
assert_equal({
blog: { id: 1, :"special-attribute" => 'neat', articles: nil }
}, @adapter.serializable_hash(@options))
end
def test_key_transform_unaltered
mock_request(:unaltered)
assert_equal({
blog: { id: 1, special_attribute: 'neat', articles: nil }
}, @adapter.serializable_hash(@options))
end
def test_key_transform_camel
mock_request(:camel)
assert_equal({
Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil }
}, @adapter.serializable_hash(@options))
end
def test_key_transform_camel_lower
mock_request(:camel_lower)
assert_equal({
blog: { id: 1, specialAttribute: 'neat', articles: nil }
}, @adapter.serializable_hash(@options))
end
end
end
end
end

View File

@ -0,0 +1,500 @@
require 'test_helper'
module ActiveModelSerializers
module Adapter
class JsonApi
class KeyCaseTest < ActiveSupport::TestCase
Post = Class.new(::Model)
class PostSerializer < ActiveModel::Serializer
type 'posts'
attributes :title, :body, :publish_at
belongs_to :author
has_many :comments
link(:self) { post_url(object.id) }
link(:post_authors) { post_authors_url(object.id) }
link(:subscriber_comments) { post_comments_url(object.id) }
meta do
{
rating: 5,
favorite_count: 10
}
end
end
Author = Class.new(::Model)
class AuthorSerializer < ActiveModel::Serializer
type 'authors'
attributes :first_name, :last_name
end
Comment = Class.new(::Model)
class CommentSerializer < ActiveModel::Serializer
type 'comments'
attributes :body
belongs_to :author
end
def mock_request(key_transform = nil)
context = Minitest::Mock.new
context.expect(:request_url, URI)
context.expect(:query_parameters, {})
context.expect(:key_transform, key_transform)
context.expect(:url_helpers, Rails.application.routes.url_helpers)
@options = {}
@options[:serialization_context] = context
end
def setup
Rails.application.routes.draw do
resources :posts do
resources :authors
resources :comments
end
end
@publish_at = 1.day.from_now
@author = Author.new(id: 1, first_name: 'Bob', last_name: 'Jones')
@comment1 = Comment.new(id: 7, body: 'cool', author: @author)
@comment2 = Comment.new(id: 12, body: 'awesome', author: @author)
@post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1',
author: @author, comments: [@comment1, @comment2],
publish_at: @publish_at)
@comment1.post = @post
@comment2.post = @post
end
def test_success_document_key_transform_default
mock_request
serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
result = adapter.serializable_hash(@options)
assert_equal({
data: {
id: '1337',
type: 'posts',
attributes: {
title: 'Title 1',
body: 'Body 1',
:"publish-at" => @publish_at
},
relationships: {
author: {
data: { id: '1', type: 'authors' }
},
comments: {
data: [
{ id: '7', type: 'comments' },
{ id: '12', type: 'comments' }
] }
},
links: {
self: 'http://example.com/posts/1337',
:"post-authors" => 'http://example.com/posts/1337/authors',
:"subscriber-comments" => 'http://example.com/posts/1337/comments'
},
meta: { rating: 5, :"favorite-count" => 10 }
}
}, result)
end
def test_success_document_key_transform_global_config
mock_request
result = with_config(key_transform: :camel_lower) do
serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
adapter.serializable_hash(@options)
end
assert_equal({
data: {
id: '1337',
type: 'posts',
attributes: {
title: 'Title 1',
body: 'Body 1',
publishAt: @publish_at
},
relationships: {
author: {
data: { id: '1', type: 'authors' }
},
comments: {
data: [
{ id: '7', type: 'comments' },
{ id: '12', type: 'comments' }
] }
},
links: {
self: 'http://example.com/posts/1337',
postAuthors: 'http://example.com/posts/1337/authors',
subscriberComments: 'http://example.com/posts/1337/comments'
},
meta: { rating: 5, favoriteCount: 10 }
}
}, result)
end
def test_success_doc_key_transform_serialization_ctx_overrides_global
mock_request(:camel)
result = with_config(key_transform: :camel_lower) do
serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
adapter.serializable_hash(@options)
end
assert_equal({
Data: {
Id: '1337',
Type: 'posts',
Attributes: {
Title: 'Title 1',
Body: 'Body 1',
PublishAt: @publish_at
},
Relationships: {
Author: {
Data: { Id: '1', Type: 'authors' }
},
Comments: {
Data: [
{ Id: '7', Type: 'comments' },
{ Id: '12', Type: 'comments' }
] }
},
Links: {
Self: 'http://example.com/posts/1337',
PostAuthors: 'http://example.com/posts/1337/authors',
SubscriberComments: 'http://example.com/posts/1337/comments'
},
Meta: { Rating: 5, FavoriteCount: 10 }
}
}, result)
end
def test_success_document_key_transform_dashed
mock_request(:dashed)
serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
result = adapter.serializable_hash(@options)
assert_equal({
data: {
id: '1337',
type: 'posts',
attributes: {
title: 'Title 1',
body: 'Body 1',
:"publish-at" => @publish_at
},
relationships: {
author: {
data: { id: '1', type: 'authors' }
},
comments: {
data: [
{ id: '7', type: 'comments' },
{ id: '12', type: 'comments' }
] }
},
links: {
self: 'http://example.com/posts/1337',
:"post-authors" => 'http://example.com/posts/1337/authors',
:"subscriber-comments" => 'http://example.com/posts/1337/comments'
},
meta: { rating: 5, :"favorite-count" => 10 }
}
}, result)
end
def test_success_document_key_transform_unaltered
mock_request(:unaltered)
serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
result = adapter.serializable_hash(@options)
assert_equal({
data: {
id: '1337',
type: 'posts',
attributes: {
title: 'Title 1',
body: 'Body 1',
publish_at: @publish_at
},
relationships: {
author: {
data: { id: '1', type: 'authors' }
},
comments: {
data: [
{ id: '7', type: 'comments' },
{ id: '12', type: 'comments' }
] }
},
links: {
self: 'http://example.com/posts/1337',
post_authors: 'http://example.com/posts/1337/authors',
subscriber_comments: 'http://example.com/posts/1337/comments'
},
meta: { rating: 5, favorite_count: 10 }
}
}, result)
end
def test_success_document_key_transform_undefined
mock_request(:zoot)
serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
assert_raises NoMethodError do
adapter.serializable_hash(@options)
end
end
def test_success_document_key_transform_camel
mock_request(:camel)
serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
result = adapter.serializable_hash(@options)
assert_equal({
Data: {
Id: '1337',
Type: 'posts',
Attributes: {
Title: 'Title 1',
Body: 'Body 1',
PublishAt: @publish_at
},
Relationships: {
Author: {
Data: { Id: '1', Type: 'authors' }
},
Comments: {
Data: [
{ Id: '7', Type: 'comments' },
{ Id: '12', Type: 'comments' }
] }
},
Links: {
Self: 'http://example.com/posts/1337',
PostAuthors: 'http://example.com/posts/1337/authors',
SubscriberComments: 'http://example.com/posts/1337/comments'
},
Meta: { Rating: 5, FavoriteCount: 10 }
}
}, result)
end
def test_success_document_key_transform_camel_lower
mock_request(:camel_lower)
serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
result = adapter.serializable_hash(@options)
assert_equal({
data: {
id: '1337',
type: 'posts',
attributes: {
title: 'Title 1',
body: 'Body 1',
publishAt: @publish_at
},
relationships: {
author: {
data: { id: '1', type: 'authors' }
},
comments: {
data: [
{ id: '7', type: 'comments' },
{ id: '12', type: 'comments' }
] }
},
links: {
self: 'http://example.com/posts/1337',
postAuthors: 'http://example.com/posts/1337/authors',
subscriberComments: 'http://example.com/posts/1337/comments'
},
meta: { rating: 5, favoriteCount: 10 }
}
}, result)
end
def test_error_document_key_transform_default
mock_request
resource = ModelWithErrors.new
resource.errors.add(:published_at, 'must be in the future')
resource.errors.add(:title, 'must be longer')
serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
result = adapter.serializable_hash(@options)
expected_errors_object =
{ :errors =>
[
{
:source => { :pointer => '/data/attributes/published_at' },
:detail => 'must be in the future' },
{
:source => { :pointer => '/data/attributes/title' },
:detail => 'must be longer'
}
]
}
assert_equal expected_errors_object, result
end
def test_error_document_key_transform_global_config
mock_request
result = with_config(key_transform: :camel) do
resource = ModelWithErrors.new
resource.errors.add(:published_at, 'must be in the future')
resource.errors.add(:title, 'must be longer')
serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
adapter.serializable_hash(@options)
end
expected_errors_object =
{ :Errors =>
[
{
:Source => { :Pointer => '/data/attributes/published_at' },
:Detail => 'must be in the future'
},
{
:Source => { :Pointer => '/data/attributes/title' },
:Detail => 'must be longer'
}
]
}
assert_equal expected_errors_object, result
end
def test_error_document_key_transform_serialization_ctx_overrides_global
mock_request(:camel)
result = with_config(key_transform: :camel_lower) do
resource = ModelWithErrors.new
resource.errors.add(:published_at, 'must be in the future')
resource.errors.add(:title, 'must be longer')
serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
adapter.serializable_hash(@options)
end
expected_errors_object =
{ :Errors =>
[
{
:Source => { :Pointer => '/data/attributes/published_at' },
:Detail => 'must be in the future'
},
{
:Source => { :Pointer => '/data/attributes/title' },
:Detail => 'must be longer'
}
]
}
assert_equal expected_errors_object, result
end
def test_error_document_key_transform_dashed
mock_request(:dashed)
resource = ModelWithErrors.new
resource.errors.add(:published_at, 'must be in the future')
resource.errors.add(:title, 'must be longer')
serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
result = adapter.serializable_hash(@options)
expected_errors_object =
{ :errors =>
[
{
:source => { :pointer => '/data/attributes/published_at' },
:detail => 'must be in the future'
},
{
:source => { :pointer => '/data/attributes/title' },
:detail => 'must be longer'
}
]
}
assert_equal expected_errors_object, result
end
def test_error_document_key_transform_unaltered
mock_request(:unaltered)
resource = ModelWithErrors.new
resource.errors.add(:published_at, 'must be in the future')
resource.errors.add(:title, 'must be longer')
serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
result = adapter.serializable_hash(@options)
expected_errors_object =
{ :errors =>
[
{ :source => { :pointer => '/data/attributes/published_at' }, :detail => 'must be in the future' },
{ :source => { :pointer => '/data/attributes/title' }, :detail => 'must be longer' }
]
}
assert_equal expected_errors_object, result
end
def test_error_document_key_transform_undefined
mock_request(:krazy)
resource = ModelWithErrors.new
resource.errors.add(:published_at, 'must be in the future')
resource.errors.add(:title, 'must be longer')
serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
assert_raises NoMethodError do
adapter.serializable_hash(@options)
end
end
def test_error_document_key_transform_camel
mock_request(:camel)
resource = ModelWithErrors.new
resource.errors.add(:published_at, 'must be in the future')
resource.errors.add(:title, 'must be longer')
serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
result = adapter.serializable_hash(@options)
expected_errors_object =
{ :Errors =>
[
{ :Source => { :Pointer => '/data/attributes/published_at' }, :Detail => 'must be in the future' },
{ :Source => { :Pointer => '/data/attributes/title' }, :Detail => 'must be longer' }
]
}
assert_equal expected_errors_object, result
end
def test_error_document_key_transform_camel_lower
mock_request(:camel_lower)
resource = ModelWithErrors.new
resource.errors.add(:published_at, 'must be in the future')
resource.errors.add(:title, 'must be longer')
serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
result = adapter.serializable_hash(@options)
expected_errors_object =
{ :errors =>
[
{ :source => { :pointer => '/data/attributes/published_at' }, :detail => 'must be in the future' },
{ :source => { :pointer => '/data/attributes/title' }, :detail => 'must be longer' }
]
}
assert_equal expected_errors_object, result
end
end
end
end
end

View File

@ -25,6 +25,7 @@ module ActiveModelSerializers
context = Minitest::Mock.new context = Minitest::Mock.new
context.expect(:request_url, original_url) context.expect(:request_url, original_url)
context.expect(:query_parameters, query_parameters) context.expect(:query_parameters, query_parameters)
context.expect(:key_transform, nil)
@options = {} @options = {}
@options[:serialization_context] = context @options[:serialization_context] = context
end end