Apply key transforms to keys referenced in values

This commit is contained in:
Ben Mills 2016-03-31 14:24:31 -06:00
parent d30aa4c44f
commit 3498647d1a
30 changed files with 579 additions and 223 deletions

View File

@ -2,7 +2,11 @@
Breaking changes: Breaking changes:
- [#1645](https://github.com/rails-api/active_model_serializers/pull/1645) Changed :dashed key transform to :dash. (@remear)
- [#1574](https://github.com/rails-api/active_model_serializers/pull/1574) Default key case for the JsonApi adapter changed to dashed. (@remear)
Features: Features:
- [#1645](https://github.com/rails-api/active_model_serializers/pull/1645) Transform keys referenced in values. (@remear)
- [#1650](https://github.com/rails-api/active_model_serializers/pull/1650) Fix serialization scope options `scope`, `scope_name` - [#1650](https://github.com/rails-api/active_model_serializers/pull/1650) Fix serialization scope options `scope`, `scope_name`
take precedence over `serialization_scope` in the controller. take precedence over `serialization_scope` in the controller.
Fix tests that required tearing down dynamic methods. (@bf4) Fix tests that required tearing down dynamic methods. (@bf4)

View File

@ -57,4 +57,10 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'grape', ['>= 0.13', '< 1.0'] spec.add_development_dependency 'grape', ['>= 0.13', '< 1.0']
spec.add_development_dependency 'json_schema' spec.add_development_dependency 'json_schema'
spec.add_development_dependency 'rake', ['>= 10.0', '< 12.0'] spec.add_development_dependency 'rake', ['>= 10.0', '< 12.0']
spec.post_install_message = <<-EOF
NOTE: The default key case for the JsonApi adapter has changed to dashed.
See https://github.com/rails-api/active_model_serializers/blob/master/docs/general/key_transform.md
for more information on configuring this behavior.
EOF
end end

View File

@ -30,20 +30,24 @@ When `false`, serializers must be explicitly specified.
##### key_transform ##### key_transform
The [key transform](key_transform.md) to use. The [key transform](key_transforms.md) to use.
Possible values:
- `:camel` - ExampleKey | Option | Result |
- `:camel_lower` - exampleKey |----|----|
- `:dashed` - example-key | `:camel` | ExampleKey |
- `:unaltered` - the original, unaltered key | `:camel_lower` | exampleKey |
- `nil` - use the adapter default | `:dash` | example-key |
| `:unaltered` | the original, unaltered key |
| `:underscore` | example_key |
| `nil` | use the adapter default |
Each adapter has a default key transform configured: Each adapter has a default key transform configured:
- `Json` - `:unaltered` | Adapter | Default Key Transform |
- `JsonApi` - `:dashed` |----|----|
| `Json` | `:unaltered` |
| `JsonApi` | `:dash` |
`config.key_transform` is a global override of the adapter default. Adapters `config.key_transform` is a global override of the adapter default. Adapters
still prefer the render option `:key_transform` over this setting. still prefer the render option `:key_transform` over this setting.

View File

@ -1,34 +0,0 @@
[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

@ -0,0 +1,40 @@
[Back to Guides](../README.md)
# Key Transforms
Key Transforms modify the casing of keys and keys referenced in values in
serialized responses.
Provided key transforms:
| Option | Result |
|----|----|
| `:camel` | ExampleKey |
| `:camel_lower` | exampleKey |
| `:dash` | example-key |
| `:unaltered` | the original, unaltered key |
| `:underscore` | example_key |
| `nil` | use the adapter default |
Key translation precedence is as follows:
##### Adapter 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 transform configured:
| Adapter | Default Key Transform |
|----|----|
| `Json` | `:unaltered` |
| `JsonApi` | `:dash` |

View File

@ -58,25 +58,32 @@ module ActiveModelSerializers
json json
end end
class << self
# Sets the default transform for the adapter.
#
# @return [Symbol] the default transform for the adapter
def default_key_transform def default_key_transform
:unaltered :unaltered
end end
# Determines the transform to use in order of precedence: # Determines the transform to use in order of precedence:
# serialization context, global config, adapter default. # adapter option, global config, adapter default.
# #
# @param serialization_context [Object] the SerializationContext # @param options [Object]
# @return [Symbol] the transform to use # @return [Symbol] the transform to use
def key_transform(serialization_context) def transform(options)
serialization_context.key_transform || return options[:key_transform] if options && options[:key_transform]
ActiveModelSerializers.config.key_transform || ActiveModelSerializers.config.key_transform || default_key_transform
default_key_transform
end end
def transform_key_casing!(value, serialization_context) # Transforms the casing of the supplied value.
return value unless serialization_context #
transform = key_transform(serialization_context) # @param value [Object] the value to be transformed
KeyTransform.send(transform, value) # @param options [Object] serializable resource options
# @return [Symbol] the default transform for the adapter
def transform_key_casing!(value, options)
KeyTransform.send(transform(options), value)
end
end end
end end
end end

View File

@ -4,7 +4,7 @@ module ActiveModelSerializers
def serializable_hash(options = nil) def serializable_hash(options = nil)
options ||= {} options ||= {}
serialized_hash = { 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]) self.class.transform_key_casing!(serialized_hash, options)
end end
end end
end end

View File

@ -37,8 +37,8 @@ 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 def self.default_key_transform
:dashed :dash
end 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}
@ -48,9 +48,9 @@ module ActiveModelSerializers
document = if serializer.success? document = if serializer.success?
success_document(options) success_document(options)
else else
failure_document failure_document(options)
end end
transform_key_casing!(document, options[:serialization_context]) self.class.transform_key_casing!(document, options)
end end
# {http://jsonapi.org/format/#document-top-level Primary data} # {http://jsonapi.org/format/#document-top-level Primary data}
@ -71,7 +71,7 @@ module ActiveModelSerializers
def success_document(options) def success_document(options)
is_collection = serializer.respond_to?(:each) is_collection = serializer.respond_to?(:each)
serializers = is_collection ? serializer : [serializer] serializers = is_collection ? serializer : [serializer]
primary_data, included = resource_objects_for(serializers) primary_data, included = resource_objects_for(serializers, options)
hash = {} hash = {}
# toplevel_data # toplevel_data
@ -148,7 +148,7 @@ module ActiveModelSerializers
# }.reject! {|_,v| v.nil? } # }.reject! {|_,v| v.nil? }
# prs: # prs:
# https://github.com/rails-api/active_model_serializers/pull/1004 # https://github.com/rails-api/active_model_serializers/pull/1004
def failure_document def failure_document(options)
hash = {} hash = {}
# PR Please :) # PR Please :)
# Jsonapi.add!(hash) # Jsonapi.add!(hash)
@ -163,10 +163,10 @@ module ActiveModelSerializers
# ] # ]
if serializer.respond_to?(:each) if serializer.respond_to?(:each)
hash[:errors] = serializer.flat_map do |error_serializer| hash[:errors] = serializer.flat_map do |error_serializer|
Error.resource_errors(error_serializer) Error.resource_errors(error_serializer, options)
end end
else else
hash[:errors] = Error.resource_errors(serializer) hash[:errors] = Error.resource_errors(serializer, options)
end end
hash hash
end end
@ -224,21 +224,21 @@ module ActiveModelSerializers
# [x] url helpers https://github.com/rails-api/active_model_serializers/issues/1269 # [x] url helpers https://github.com/rails-api/active_model_serializers/issues/1269
# meta # meta
# [x] https://github.com/rails-api/active_model_serializers/pull/1340 # [x] https://github.com/rails-api/active_model_serializers/pull/1340
def resource_objects_for(serializers) def resource_objects_for(serializers, options)
@primary = [] @primary = []
@included = [] @included = []
@resource_identifiers = Set.new @resource_identifiers = Set.new
serializers.each { |serializer| process_resource(serializer, true) } serializers.each { |serializer| process_resource(serializer, true, options) }
serializers.each { |serializer| process_relationships(serializer, @include_tree) } serializers.each { |serializer| process_relationships(serializer, @include_tree, options) }
[@primary, @included] [@primary, @included]
end end
def process_resource(serializer, primary) def process_resource(serializer, primary, options)
resource_identifier = ResourceIdentifier.new(serializer).as_json resource_identifier = ResourceIdentifier.new(serializer, options).as_json
return false unless @resource_identifiers.add?(resource_identifier) return false unless @resource_identifiers.add?(resource_identifier)
resource_object = resource_object_for(serializer) resource_object = resource_object_for(serializer, options)
if primary if primary
@primary << resource_object @primary << resource_object
else else
@ -248,21 +248,21 @@ module ActiveModelSerializers
true true
end end
def process_relationships(serializer, include_tree) def process_relationships(serializer, include_tree, options)
serializer.associations(include_tree).each do |association| serializer.associations(include_tree).each do |association|
process_relationship(association.serializer, include_tree[association.key]) process_relationship(association.serializer, include_tree[association.key], options)
end end
end end
def process_relationship(serializer, include_tree) def process_relationship(serializer, include_tree, options)
if serializer.respond_to?(:each) if serializer.respond_to?(:each)
serializer.each { |s| process_relationship(s, include_tree) } serializer.each { |s| process_relationship(s, include_tree, options) }
return return
end end
return unless serializer && serializer.object return unless serializer && serializer.object
return unless process_resource(serializer, false) return unless process_resource(serializer, false, options)
process_relationships(serializer, include_tree) process_relationships(serializer, include_tree, options)
end end
# {http://jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes} # {http://jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes}
@ -286,9 +286,9 @@ module ActiveModelSerializers
end end
# {http://jsonapi.org/format/#document-resource-objects Document Resource Objects} # {http://jsonapi.org/format/#document-resource-objects Document Resource Objects}
def resource_object_for(serializer) def resource_object_for(serializer, options)
resource_object = cache_check(serializer) do resource_object = cache_check(serializer) do
resource_object = ResourceIdentifier.new(serializer).as_json resource_object = ResourceIdentifier.new(serializer, options).as_json
requested_fields = fieldset && fieldset.fields_for(resource_object[:type]) requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
attributes = attributes_for(serializer, requested_fields) attributes = attributes_for(serializer, requested_fields)
@ -297,7 +297,7 @@ module ActiveModelSerializers
end end
requested_associations = fieldset.fields_for(resource_object[:type]) || '*' requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
relationships = relationships_for(serializer, requested_associations) relationships = relationships_for(serializer, requested_associations, options)
resource_object[:relationships] = relationships if relationships.any? resource_object[:relationships] = relationships if relationships.any?
links = links_for(serializer) links = links_for(serializer)
@ -425,15 +425,16 @@ module ActiveModelSerializers
# id: 'required-id', # id: 'required-id',
# meta: meta # meta: meta
# }.reject! {|_,v| v.nil? } # }.reject! {|_,v| v.nil? }
def relationships_for(serializer, requested_associations) def relationships_for(serializer, requested_associations, options)
include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(requested_associations) include_tree = ActiveModel::Serializer::IncludeTree.from_include_args(requested_associations)
serializer.associations(include_tree).each_with_object({}) do |association, hash| serializer.associations(include_tree).each_with_object({}) do |association, hash|
hash[association.key] = Relationship.new( hash[association.key] = Relationship.new(
serializer, serializer,
association.serializer, association.serializer,
association.options, options,
association.links, options: association.options,
association.meta links: association.links,
meta: association.meta
).as_json ).as_json
end end
end end

View File

@ -182,14 +182,14 @@ module ActiveModelSerializers
prefix_key = field_key(assoc_name, options).to_s.singularize prefix_key = field_key(assoc_name, options).to_s.singularize
hash = hash =
if assoc_data.is_a?(Array) 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 else
{ "#{prefix_key}_id".to_sym => assoc_data && assoc_data.is_a?(Hash) ? assoc_data[:id] : nil } { "#{prefix_key}_id".to_sym => assoc_data ? assoc_data['id'] : nil }
end end
polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym) polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym)
if polymorphic if polymorphic
hash["#{prefix_key}_type".to_sym] = assoc_data.present? ? assoc_data[:type] : nil hash["#{prefix_key}_type".to_sym] = assoc_data.present? ? assoc_data['type'] : nil
end end
hash hash
@ -198,7 +198,7 @@ module ActiveModelSerializers
# @api private # @api private
def parse_relationships(relationships, options) def parse_relationships(relationships, options)
transform_keys(relationships, options) transform_keys(relationships, options)
.map { |(k, v)| parse_relationship(k, v[:data], options) } .map { |(k, v)| parse_relationship(k, v['data'], options) }
.reduce({}, :merge) .reduce({}, :merge)
end end

View File

@ -10,8 +10,10 @@ module ActiveModelSerializers
# #
# @param [ActiveModel::Serializer::ErrorSerializer] error_serializer # @param [ActiveModel::Serializer::ErrorSerializer] error_serializer
# @return [Array<Symbol, Array<String>>] i.e. attribute_name, [attribute_errors] # @return [Array<Symbol, Array<String>>] i.e. attribute_name, [attribute_errors]
def self.resource_errors(error_serializer) def self.resource_errors(error_serializer, options)
error_serializer.as_json.flat_map do |attribute_name, attribute_errors| error_serializer.as_json.flat_map do |attribute_name, attribute_errors|
attribute_name = JsonApi.send(:transform_key_casing!, attribute_name,
options)
attribute_error_objects(attribute_name, attribute_errors) attribute_error_objects(attribute_name, attribute_errors)
end end
end end

View File

@ -6,21 +6,22 @@ module ActiveModelSerializers
# {http://jsonapi.org/format/#document-links Document Links} # {http://jsonapi.org/format/#document-links Document Links}
# {http://jsonapi.org/format/#document-resource-object-linkage Document Resource Relationship Linkage} # {http://jsonapi.org/format/#document-resource-object-linkage Document Resource Relationship Linkage}
# {http://jsonapi.org/format/#document-meta Docment Meta} # {http://jsonapi.org/format/#document-meta Docment Meta}
def initialize(parent_serializer, serializer, options = {}, links = {}, meta = nil) def initialize(parent_serializer, serializer, serializable_resource_options, args = {})
@object = parent_serializer.object @object = parent_serializer.object
@scope = parent_serializer.scope @scope = parent_serializer.scope
@association_options = args.fetch(:options, {})
@options = options @serializable_resource_options = serializable_resource_options
@data = data_for(serializer, options) @data = data_for(serializer)
@links = links.each_with_object({}) do |(key, value), hash| @links = args.fetch(:links, {}).each_with_object({}) do |(key, value), hash|
hash[key] = ActiveModelSerializers::Adapter::JsonApi::Link.new(parent_serializer, value).as_json hash[key] = ActiveModelSerializers::Adapter::JsonApi::Link.new(parent_serializer, value).as_json
end end
meta = args.fetch(:meta, nil)
@meta = meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta @meta = meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
end end
def as_json def as_json
hash = {} hash = {}
hash[:data] = data if options[:include_data] hash[:data] = data if association_options[:include_data]
links = self.links links = self.links
hash[:links] = links if links.any? hash[:links] = links if links.any?
meta = self.meta meta = self.meta
@ -31,17 +32,18 @@ module ActiveModelSerializers
protected protected
attr_reader :object, :scope, :data, :options, :links, :meta attr_reader :object, :scope, :data, :serializable_resource_options,
:association_options, :links, :meta
private private
def data_for(serializer, options) def data_for(serializer)
if serializer.respond_to?(:each) if serializer.respond_to?(:each)
serializer.map { |s| ResourceIdentifier.new(s).as_json } serializer.map { |s| ResourceIdentifier.new(s, serializable_resource_options).as_json }
elsif options[:virtual_value] elsif association_options[:virtual_value]
options[:virtual_value] association_options[:virtual_value]
elsif serializer && serializer.object elsif serializer && serializer.object
ResourceIdentifier.new(serializer).as_json ResourceIdentifier.new(serializer, serializable_resource_options).as_json
end end
end end
end end

View File

@ -3,9 +3,10 @@ module ActiveModelSerializers
class JsonApi class JsonApi
class ResourceIdentifier class ResourceIdentifier
# {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects} # {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects}
def initialize(serializer) def initialize(serializer, options)
@id = id_for(serializer) @id = id_for(serializer)
@type = type_for(serializer) @type = JsonApi.send(:transform_key_casing!, type_for(serializer),
options)
end end
def as_json def as_json

View File

@ -4,47 +4,67 @@ module ActiveModelSerializers
module KeyTransform module KeyTransform
module_function module_function
# Transforms keys to UpperCamelCase or PascalCase. # Transforms values to UpperCamelCase or PascalCase.
# #
# @example: # @example:
# "some_key" => "SomeKey", # "some_key" => "SomeKey",
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize} # @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize}
def camel(hash) def camel(value)
hash.deep_transform_keys! { |key| key.to_s.camelize.to_sym } case value
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
else value
end
end end
# Transforms keys to camelCase. # Transforms values to camelCase.
# #
# @example: # @example:
# "some_key" => "someKey", # "some_key" => "someKey",
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize} # @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize}
def camel_lower(hash) def camel_lower(value)
hash.deep_transform_keys! { |key| key.to_s.camelize(:lower).to_sym } case value
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)
else value
end
end end
# Transforms keys to dashed-case. # Transforms values to dashed-case.
# This is the default case for the JsonApi adapter. # This is the default case for the JsonApi adapter.
# #
# @example: # @example:
# "some_key" => "some-key", # "some_key" => "some-key",
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L185-L187 ActiveSupport::Inflector.dasherize} # @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L185-L187 ActiveSupport::Inflector.dasherize}
def dashed(hash) def dash(value)
hash.deep_transform_keys! { |key| key.to_s.dasherize.to_sym } case value
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
else value
end
end end
# Transforms keys to underscore. # Transforms values to underscore_case.
# This is the default case for deserialization in the JsonApi adapter. # This is the default case for deserialization in the JsonApi adapter.
# #
# @example: # @example:
# "some-key" => "some_key", # "some-key" => "some_key",
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L89-L98 ActiveSupport::Inflector.underscore} # @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L89-L98 ActiveSupport::Inflector.underscore}
def underscore(hash) def underscore(value)
hash.deep_transform_keys! { |key| key.to_s.underscore.to_sym } case value
when Hash then value.deep_transform_keys! { |key| underscore(key) }
when Symbol then underscore(value.to_s).to_sym
when String then value.underscore
else value
end
end end
# Returns the hash unaltered # Returns the value unaltered
def unaltered(hash) def unaltered(value)
hash value
end end
end end
end end

View File

@ -27,7 +27,6 @@ module ActiveModelSerializers
@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
end end
end end

View File

@ -10,7 +10,7 @@ module ActionController
type 'posts' type 'posts'
attributes :title, :body, :publish_at attributes :title, :body, :publish_at
belongs_to :author belongs_to :author
has_many :comments has_many :top_comments
link(:post_authors) { 'https://example.com/post_authors' } link(:post_authors) { 'https://example.com/post_authors' }
@ -28,9 +28,9 @@ module ActionController
attributes :first_name, :last_name attributes :first_name, :last_name
end end
Comment = Class.new(::Model) TopComment = Class.new(::Model)
class CommentSerializer < ActiveModel::Serializer class TopCommentSerializer < ActiveModel::Serializer
type 'comments' type 'top_comments'
attributes :body attributes :body
belongs_to :author belongs_to :author
end end
@ -38,28 +38,28 @@ module ActionController
def setup_post def setup_post
ActionController::Base.cache_store.clear ActionController::Base.cache_store.clear
@author = Author.new(id: 1, first_name: 'Bob', last_name: 'Jones') @author = Author.new(id: 1, first_name: 'Bob', last_name: 'Jones')
@comment1 = Comment.new(id: 7, body: 'cool', author: @author) @comment1 = TopComment.new(id: 7, body: 'cool', author: @author)
@comment2 = Comment.new(id: 12, body: 'awesome', author: @author) @comment2 = TopComment.new(id: 12, body: 'awesome', author: @author)
@post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1', @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1',
author: @author, comments: [@comment1, @comment2], author: @author, top_comments: [@comment1, @comment2],
publish_at: '2020-03-16T03:55:25.291Z') publish_at: '2020-03-16T03:55:25.291Z')
@comment1.post = @post @comment1.post = @post
@comment2.post = @post @comment2.post = @post
end end
def render_resource_with_key_transform def render_resource_with_transform
setup_post setup_post
render json: @post, serializer: PostSerializer, adapter: :json_api, render json: @post, serializer: PostSerializer, adapter: :json_api,
key_transform: :camel key_transform: :camel
end end
def render_resource_with_key_transform_nil def render_resource_with_transform_nil
setup_post setup_post
render json: @post, serializer: PostSerializer, adapter: :json_api, render json: @post, serializer: PostSerializer, adapter: :json_api,
key_transform: nil key_transform: nil
end end
def render_resource_with_key_transform_with_global_config def render_resource_with_transform_with_global_config
setup_post setup_post
old_transform = ActiveModelSerializers.config.key_transform old_transform = ActiveModelSerializers.config.key_transform
ActiveModelSerializers.config.key_transform = :camel_lower ActiveModelSerializers.config.key_transform = :camel_lower
@ -70,13 +70,13 @@ module ActionController
tests KeyTransformTestController tests KeyTransformTestController
def test_render_resource_with_key_transform def test_render_resource_with_transform
get :render_resource_with_key_transform get :render_resource_with_transform
response = JSON.parse(@response.body) response = JSON.parse(@response.body)
expected = { expected = {
'Data' => { 'Data' => {
'Id' => '1337', 'Id' => '1337',
'Type' => 'posts', 'Type' => 'Posts',
'Attributes' => { 'Attributes' => {
'Title' => 'Title 1', 'Title' => 'Title 1',
'Body' => 'Body 1', 'Body' => 'Body 1',
@ -86,13 +86,13 @@ module ActionController
'Author' => { 'Author' => {
'Data' => { 'Data' => {
'Id' => '1', 'Id' => '1',
'Type' => 'authors' 'Type' => 'Authors'
} }
}, },
'Comments' => { 'TopComments' => {
'Data' => [ 'Data' => [
{ 'Id' => '7', 'Type' => 'comments' }, { 'Id' => '7', 'Type' => 'TopComments' },
{ 'Id' => '12', 'Type' => 'comments' } { 'Id' => '12', 'Type' => 'TopComments' }
] ]
} }
}, },
@ -105,8 +105,8 @@ module ActionController
assert_equal expected, response assert_equal expected, response
end end
def test_render_resource_with_key_transform_nil def test_render_resource_with_transform_nil
get :render_resource_with_key_transform_nil get :render_resource_with_transform_nil
response = JSON.parse(@response.body) response = JSON.parse(@response.body)
expected = { expected = {
'data' => { 'data' => {
@ -124,10 +124,10 @@ module ActionController
'type' => 'authors' 'type' => 'authors'
} }
}, },
'comments' => { 'top-comments' => {
'data' => [ 'data' => [
{ 'id' => '7', 'type' => 'comments' }, { 'id' => '7', 'type' => 'top-comments' },
{ 'id' => '12', 'type' => 'comments' } { 'id' => '12', 'type' => 'top-comments' }
] ]
} }
}, },
@ -140,8 +140,8 @@ module ActionController
assert_equal expected, response assert_equal expected, response
end end
def test_render_resource_with_key_transform_with_global_config def test_render_resource_with_transform_with_global_config
get :render_resource_with_key_transform_with_global_config get :render_resource_with_transform_with_global_config
response = JSON.parse(@response.body) response = JSON.parse(@response.body)
expected = { expected = {
'data' => { 'data' => {
@ -159,10 +159,10 @@ module ActionController
'type' => 'authors' 'type' => 'authors'
} }
}, },
'comments' => { 'topComments' => {
'data' => [ 'data' => [
{ 'id' => '7', 'type' => 'comments' }, { 'id' => '7', 'type' => 'topComments' },
{ 'id' => '12', 'type' => 'comments' } { 'id' => '12', 'type' => 'topComments' }
] ]
} }
}, },

View File

@ -0,0 +1,263 @@
require 'test_helper'
class ActiveModelSerializers::KeyTransformTest < ActiveSupport::TestCase
def test_camel
obj = Object.new
scenarios = [
{
value: { :"some-key" => 'value' },
expected: { :SomeKey => 'value' }
},
{
value: { :someKey => 'value' },
expected: { :SomeKey => 'value' }
},
{
value: { :some_key => 'value' },
expected: { :SomeKey => 'value' }
},
{
value: { 'some-key' => 'value' },
expected: { 'SomeKey' => 'value' }
},
{
value: { 'someKey' => 'value' },
expected: { 'SomeKey' => 'value' }
},
{
value: { 'some_key' => 'value' },
expected: { 'SomeKey' => 'value' }
},
{
value: :"some-value",
expected: :SomeValue
},
{
value: :some_value,
expected: :SomeValue
},
{
value: :someValue,
expected: :SomeValue
},
{
value: 'some-value',
expected: 'SomeValue'
},
{
value: 'someValue',
expected: 'SomeValue'
},
{
value: 'some_value',
expected: 'SomeValue'
},
{
value: obj,
expected: obj
},
{
value: nil,
expected: nil
}
]
scenarios.each do |s|
result = ActiveModelSerializers::KeyTransform.camel(s[:value])
assert_equal s[:expected], result
end
end
def test_camel_lower
obj = Object.new
scenarios = [
{
value: { :"some-key" => 'value' },
expected: { :someKey => 'value' }
},
{
value: { :SomeKey => 'value' },
expected: { :someKey => 'value' }
},
{
value: { :some_key => 'value' },
expected: { :someKey => 'value' }
},
{
value: { 'some-key' => 'value' },
expected: { 'someKey' => 'value' }
},
{
value: { 'SomeKey' => 'value' },
expected: { 'someKey' => 'value' }
},
{
value: { 'some_key' => 'value' },
expected: { 'someKey' => 'value' }
},
{
value: :"some-value",
expected: :someValue
},
{
value: :SomeValue,
expected: :someValue
},
{
value: :some_value,
expected: :someValue
},
{
value: 'some-value',
expected: 'someValue'
},
{
value: 'SomeValue',
expected: 'someValue'
},
{
value: 'some_value',
expected: 'someValue'
},
{
value: obj,
expected: obj
},
{
value: nil,
expected: nil
}
]
scenarios.each do |s|
result = ActiveModelSerializers::KeyTransform.camel_lower(s[:value])
assert_equal s[:expected], result
end
end
def test_dash
obj = Object.new
scenarios = [
{
value: { :some_key => 'value' },
expected: { :"some-key" => 'value' }
},
{
value: { 'some_key' => 'value' },
expected: { 'some-key' => 'value' }
},
{
value: { :SomeKey => 'value' },
expected: { :"some-key" => 'value' }
},
{
value: { 'SomeKey' => 'value' },
expected: { 'some-key' => 'value' }
},
{
value: { :someKey => 'value' },
expected: { :"some-key" => 'value' }
},
{
value: { 'someKey' => 'value' },
expected: { 'some-key' => 'value' }
},
{
value: :some_value,
expected: :"some-value"
},
{
value: :SomeValue,
expected: :"some-value"
},
{
value: 'SomeValue',
expected: 'some-value'
},
{
value: :someValue,
expected: :"some-value"
},
{
value: 'someValue',
expected: 'some-value'
},
{
value: obj,
expected: obj
},
{
value: nil,
expected: nil
}
]
scenarios.each do |s|
result = ActiveModelSerializers::KeyTransform.dash(s[:value])
assert_equal s[:expected], result
end
end
def test_underscore
obj = Object.new
scenarios = [
{
value: { :"some-key" => 'value' },
expected: { :some_key => 'value' }
},
{
value: { 'some-key' => 'value' },
expected: { 'some_key' => 'value' }
},
{
value: { :SomeKey => 'value' },
expected: { :some_key => 'value' }
},
{
value: { 'SomeKey' => 'value' },
expected: { 'some_key' => 'value' }
},
{
value: { :someKey => 'value' },
expected: { :some_key => 'value' }
},
{
value: { 'someKey' => 'value' },
expected: { 'some_key' => 'value' }
},
{
value: :"some-value",
expected: :some_value
},
{
value: :SomeValue,
expected: :some_value
},
{
value: :someValue,
expected: :some_value
},
{
value: 'some-value',
expected: 'some_value'
},
{
value: 'SomeValue',
expected: 'some_value'
},
{
value: 'someValue',
expected: 'some_value'
},
{
value: obj,
expected: obj
},
{
value: nil,
expected: nil
}
]
scenarios.each do |s|
result = ActiveModelSerializers::KeyTransform.underscore(s[:value])
assert_equal s[:expected], result
end
end
end

View File

@ -8,8 +8,8 @@ module ActiveModelSerializers
context = Minitest::Mock.new context = Minitest::Mock.new
context.expect(:request_url, URI) context.expect(:request_url, URI)
context.expect(:query_parameters, {}) context.expect(:query_parameters, {})
context.expect(:key_transform, key_transform)
@options = {} @options = {}
@options[:key_transform] = key_transform if key_transform
@options[:serialization_context] = context @options[:serialization_context] = context
end end
@ -25,14 +25,14 @@ module ActiveModelSerializers
@adapter = ActiveModelSerializers::Adapter::Json.new(serializer) @adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
end end
def test_key_transform_default def test_transform_default
mock_request mock_request
assert_equal({ assert_equal({
blog: { id: 1, special_attribute: 'neat', articles: nil } blog: { id: 1, special_attribute: 'neat', articles: nil }
}, @adapter.serializable_hash(@options)) }, @adapter.serializable_hash(@options))
end end
def test_key_transform_global_config def test_transform_global_config
mock_request mock_request
result = with_config(key_transform: :camel_lower) do result = with_config(key_transform: :camel_lower) do
@adapter.serializable_hash(@options) @adapter.serializable_hash(@options)
@ -42,7 +42,7 @@ module ActiveModelSerializers
}, result) }, result)
end end
def test_key_transform_serialization_ctx_overrides_global_config def test_transform_serialization_ctx_overrides_global_config
mock_request(:camel) mock_request(:camel)
result = with_config(key_transform: :camel_lower) do result = with_config(key_transform: :camel_lower) do
@adapter.serializable_hash(@options) @adapter.serializable_hash(@options)
@ -52,7 +52,7 @@ module ActiveModelSerializers
}, result) }, result)
end end
def test_key_transform_undefined def test_transform_undefined
mock_request(:blam) mock_request(:blam)
result = nil result = nil
assert_raises NoMethodError do assert_raises NoMethodError do
@ -60,28 +60,28 @@ module ActiveModelSerializers
end end
end end
def test_key_transform_dashed def test_transform_dash
mock_request(:dashed) mock_request(:dash)
assert_equal({ assert_equal({
blog: { id: 1, :"special-attribute" => 'neat', articles: nil } blog: { id: 1, :"special-attribute" => 'neat', articles: nil }
}, @adapter.serializable_hash(@options)) }, @adapter.serializable_hash(@options))
end end
def test_key_transform_unaltered def test_transform_unaltered
mock_request(:unaltered) mock_request(:unaltered)
assert_equal({ assert_equal({
blog: { id: 1, special_attribute: 'neat', articles: nil } blog: { id: 1, special_attribute: 'neat', articles: nil }
}, @adapter.serializable_hash(@options)) }, @adapter.serializable_hash(@options))
end end
def test_key_transform_camel def test_transform_camel
mock_request(:camel) mock_request(:camel)
assert_equal({ assert_equal({
Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil } Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil }
}, @adapter.serializable_hash(@options)) }, @adapter.serializable_hash(@options))
end end
def test_key_transform_camel_lower def test_transform_camel_lower
mock_request(:camel_lower) mock_request(:camel_lower)
assert_equal({ assert_equal({
blog: { id: 1, specialAttribute: 'neat', articles: nil } blog: { id: 1, specialAttribute: 'neat', articles: nil }

View File

@ -129,7 +129,7 @@ module ActiveModelSerializers
assert_equal({ assert_equal({
data: { data: {
id: '1', id: '1',
type: 'virtual_values', type: 'virtual-values',
relationships: { relationships: {
maker: { data: { id: 1 } }, maker: { data: { id: 1 } },
reviews: { data: [{ id: 1 }, { id: 2 }] } reviews: { data: [{ id: 1 }, { id: 2 }] }

View File

@ -63,7 +63,7 @@ module ActiveModelSerializers
expected = { expected = {
data: { data: {
id: '1', id: '1',
type: 'virtual_values', type: 'virtual-values',
relationships: { relationships: {
maker: { data: { id: 1 } }, maker: { data: { id: 1 } },
reviews: { data: [{ id: 1 }, { id: 2 }] } reviews: { data: [{ id: 1 }, { id: 2 }] }

View File

@ -216,7 +216,7 @@ module ActiveModelSerializers
expected = { expected = {
related: { related: {
data: [{ data: [{
type: 'spam_unrelated_links', type: 'spam-unrelated-links',
id: '456' id: '456'
}] }]
} }
@ -366,12 +366,12 @@ module ActiveModelSerializers
adapter: :json_api, adapter: :json_api,
include: '*').serializable_hash include: '*').serializable_hash
expected = [ expected = [
type: 'nested_posts', id: '2', type: 'nested-posts', id: '2',
relationships: { relationships: {
nested_posts: { :"nested-posts" => {
data: [ data: [
{ type: 'nested_posts', id: '1' }, { type: 'nested-posts', id: '1' },
{ type: 'nested_posts', id: '2' } { type: 'nested-posts', id: '2' }
] ]
} }
} }

View File

@ -80,10 +80,10 @@ module ActiveModelSerializers
} }
}, },
author: 'http://example.com/link_authors/1337', author: 'http://example.com/link_authors/1337',
link_authors: 'http://example.com/link_authors', :"link-authors" => 'http://example.com/link_authors',
resource: 'http://example.com/resource', resource: 'http://example.com/resource',
posts: 'http://example.com/link_authors/1337/posts', posts: 'http://example.com/link_authors/1337/posts',
yet_another: 'http://example.com/resource/1337' :"yet-another" => 'http://example.com/resource/1337'
} }
assert_equal(expected, hash[:data][:links]) assert_equal(expected, hash[:data][:links])
end end

View File

@ -25,7 +25,6 @@ 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

View File

@ -151,11 +151,8 @@ module ActiveModelSerializers
private private
def test_relationship(expected, params = {}) def test_relationship(expected, params = {})
options = params.fetch(:options, {})
links = params.fetch(:links, {})
meta = params[:meta]
parent_serializer = AuthorSerializer.new(@author) parent_serializer = AuthorSerializer.new(@author)
relationship = Relationship.new(parent_serializer, @serializer, options, links, meta) relationship = Relationship.new(parent_serializer, @serializer, nil, params)
assert_equal(expected, relationship.as_json) assert_equal(expected, relationship.as_json)
end end
end end

View File

@ -22,7 +22,7 @@ module ActiveModelSerializers
end end
def test_defined_type def test_defined_type
test_type(WithDefinedTypeSerializer, 'with_defined_type') test_type(WithDefinedTypeSerializer, 'with-defined-type')
end end
def test_singular_type def test_singular_type
@ -58,7 +58,7 @@ module ActiveModelSerializers
def test_type(serializer_class, expected_type) def test_type(serializer_class, expected_type)
serializer = serializer_class.new(@model) serializer = serializer_class.new(@model)
resource_identifier = ResourceIdentifier.new(serializer) resource_identifier = ResourceIdentifier.new(serializer, nil)
expected = { expected = {
id: @model.id.to_s, id: @model.id.to_s,
type: expected_type type: expected_type
@ -69,7 +69,7 @@ module ActiveModelSerializers
def test_id(serializer_class, id) def test_id(serializer_class, id)
serializer = serializer_class.new(@model) serializer = serializer_class.new(@model)
resource_identifier = ResourceIdentifier.new(serializer) resource_identifier = ResourceIdentifier.new(serializer, nil)
inflection = ActiveModelSerializers.config.jsonapi_resource_type inflection = ActiveModelSerializers.config.jsonapi_resource_type
type = @model.class.model_name.send(inflection) type = @model.class.model_name.send(inflection)
expected = { expected = {

View File

@ -54,7 +54,7 @@ module ActiveModel
adapter: :json_api adapter: :json_api
).serializable_hash ).serializable_hash
expected = { expected = {
comments_count: @post.comments.count :"comments-count" => @post.comments.count
} }
assert_equal(expected, hash[:data][:meta]) assert_equal(expected, hash[:data][:meta])
end end
@ -69,8 +69,8 @@ module ActiveModel
).serializable_hash ).serializable_hash
expected = { expected = {
:data => [ :data => [
{ :id => '1337', :type => 'posts', :meta => { :comments_count => 0 } }, { :id => '1337', :type => 'posts', :meta => { :"comments-count" => 0 } },
{ :id => '1339', :type => 'posts', :meta => { :comments_count => 1 } } { :id => '1339', :type => 'posts', :meta => { :"comments-count" => 1 } }
] ]
} }
assert_equal(expected, hash) assert_equal(expected, hash)

View File

@ -36,13 +36,13 @@ module ActiveModelSerializers
belongs_to :author belongs_to :author
end end
def mock_request(key_transform = nil) def mock_request(transform = nil)
context = Minitest::Mock.new context = Minitest::Mock.new
context.expect(:request_url, URI) context.expect(:request_url, URI)
context.expect(:query_parameters, {}) context.expect(:query_parameters, {})
context.expect(:key_transform, key_transform)
context.expect(:url_helpers, Rails.application.routes.url_helpers) context.expect(:url_helpers, Rails.application.routes.url_helpers)
@options = {} @options = {}
@options[:key_transform] = transform if transform
@options[:serialization_context] = context @options[:serialization_context] = context
end end
@ -64,7 +64,7 @@ module ActiveModelSerializers
@comment2.post = @post @comment2.post = @post
end end
def test_success_document_key_transform_default def test_success_document_transform_default
mock_request mock_request
serializer = PostSerializer.new(@post) serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
@ -98,7 +98,7 @@ module ActiveModelSerializers
}, result) }, result)
end end
def test_success_document_key_transform_global_config def test_success_document_transform_global_config
mock_request mock_request
result = with_config(key_transform: :camel_lower) do result = with_config(key_transform: :camel_lower) do
serializer = PostSerializer.new(@post) serializer = PostSerializer.new(@post)
@ -134,7 +134,7 @@ module ActiveModelSerializers
}, result) }, result)
end end
def test_success_doc_key_transform_serialization_ctx_overrides_global def test_success_doc_transform_serialization_ctx_overrides_global
mock_request(:camel) mock_request(:camel)
result = with_config(key_transform: :camel_lower) do result = with_config(key_transform: :camel_lower) do
serializer = PostSerializer.new(@post) serializer = PostSerializer.new(@post)
@ -144,7 +144,7 @@ module ActiveModelSerializers
assert_equal({ assert_equal({
Data: { Data: {
Id: '1337', Id: '1337',
Type: 'posts', Type: 'Posts',
Attributes: { Attributes: {
Title: 'Title 1', Title: 'Title 1',
Body: 'Body 1', Body: 'Body 1',
@ -152,12 +152,12 @@ module ActiveModelSerializers
}, },
Relationships: { Relationships: {
Author: { Author: {
Data: { Id: '1', Type: 'authors' } Data: { Id: '1', Type: 'Authors' }
}, },
Comments: { Comments: {
Data: [ Data: [
{ Id: '7', Type: 'comments' }, { Id: '7', Type: 'Comments' },
{ Id: '12', Type: 'comments' } { Id: '12', Type: 'Comments' }
] } ] }
}, },
Links: { Links: {
@ -170,8 +170,8 @@ module ActiveModelSerializers
}, result) }, result)
end end
def test_success_document_key_transform_dashed def test_success_document_transform_dash
mock_request(:dashed) mock_request(:dash)
serializer = PostSerializer.new(@post) serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
result = adapter.serializable_hash(@options) result = adapter.serializable_hash(@options)
@ -204,7 +204,7 @@ module ActiveModelSerializers
}, result) }, result)
end end
def test_success_document_key_transform_unaltered def test_success_document_transform_unaltered
mock_request(:unaltered) mock_request(:unaltered)
serializer = PostSerializer.new(@post) serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
@ -238,7 +238,7 @@ module ActiveModelSerializers
}, result) }, result)
end end
def test_success_document_key_transform_undefined def test_success_document_transform_undefined
mock_request(:zoot) mock_request(:zoot)
serializer = PostSerializer.new(@post) serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
@ -247,7 +247,7 @@ module ActiveModelSerializers
end end
end end
def test_success_document_key_transform_camel def test_success_document_transform_camel
mock_request(:camel) mock_request(:camel)
serializer = PostSerializer.new(@post) serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
@ -255,7 +255,7 @@ module ActiveModelSerializers
assert_equal({ assert_equal({
Data: { Data: {
Id: '1337', Id: '1337',
Type: 'posts', Type: 'Posts',
Attributes: { Attributes: {
Title: 'Title 1', Title: 'Title 1',
Body: 'Body 1', Body: 'Body 1',
@ -263,12 +263,12 @@ module ActiveModelSerializers
}, },
Relationships: { Relationships: {
Author: { Author: {
Data: { Id: '1', Type: 'authors' } Data: { Id: '1', Type: 'Authors' }
}, },
Comments: { Comments: {
Data: [ Data: [
{ Id: '7', Type: 'comments' }, { Id: '7', Type: 'Comments' },
{ Id: '12', Type: 'comments' } { Id: '12', Type: 'Comments' }
] } ] }
}, },
Links: { Links: {
@ -281,7 +281,7 @@ module ActiveModelSerializers
}, result) }, result)
end end
def test_success_document_key_transform_camel_lower def test_success_document_transform_camel_lower
mock_request(:camel_lower) mock_request(:camel_lower)
serializer = PostSerializer.new(@post) serializer = PostSerializer.new(@post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer) adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
@ -315,7 +315,7 @@ module ActiveModelSerializers
}, result) }, result)
end end
def test_error_document_key_transform_default def test_error_document_transform_default
mock_request mock_request
resource = ModelWithErrors.new resource = ModelWithErrors.new
resource.errors.add(:published_at, 'must be in the future') resource.errors.add(:published_at, 'must be in the future')
@ -327,7 +327,7 @@ module ActiveModelSerializers
{ :errors => { :errors =>
[ [
{ {
:source => { :pointer => '/data/attributes/published_at' }, :source => { :pointer => '/data/attributes/published-at' },
:detail => 'must be in the future' }, :detail => 'must be in the future' },
{ {
:source => { :pointer => '/data/attributes/title' }, :source => { :pointer => '/data/attributes/title' },
@ -338,7 +338,7 @@ module ActiveModelSerializers
assert_equal expected_errors_object, result assert_equal expected_errors_object, result
end end
def test_error_document_key_transform_global_config def test_error_document_transform_global_config
mock_request mock_request
result = with_config(key_transform: :camel) do result = with_config(key_transform: :camel) do
resource = ModelWithErrors.new resource = ModelWithErrors.new
@ -352,11 +352,11 @@ module ActiveModelSerializers
{ :Errors => { :Errors =>
[ [
{ {
:Source => { :Pointer => '/data/attributes/published_at' }, :Source => { :Pointer => '/data/attributes/PublishedAt' },
:Detail => 'must be in the future' :Detail => 'must be in the future'
}, },
{ {
:Source => { :Pointer => '/data/attributes/title' }, :Source => { :Pointer => '/data/attributes/Title' },
:Detail => 'must be longer' :Detail => 'must be longer'
} }
] ]
@ -364,7 +364,7 @@ module ActiveModelSerializers
assert_equal expected_errors_object, result assert_equal expected_errors_object, result
end end
def test_error_document_key_transform_serialization_ctx_overrides_global def test_error_document_transform_serialization_ctx_overrides_global
mock_request(:camel) mock_request(:camel)
result = with_config(key_transform: :camel_lower) do result = with_config(key_transform: :camel_lower) do
resource = ModelWithErrors.new resource = ModelWithErrors.new
@ -378,11 +378,11 @@ module ActiveModelSerializers
{ :Errors => { :Errors =>
[ [
{ {
:Source => { :Pointer => '/data/attributes/published_at' }, :Source => { :Pointer => '/data/attributes/PublishedAt' },
:Detail => 'must be in the future' :Detail => 'must be in the future'
}, },
{ {
:Source => { :Pointer => '/data/attributes/title' }, :Source => { :Pointer => '/data/attributes/Title' },
:Detail => 'must be longer' :Detail => 'must be longer'
} }
] ]
@ -390,8 +390,8 @@ module ActiveModelSerializers
assert_equal expected_errors_object, result assert_equal expected_errors_object, result
end end
def test_error_document_key_transform_dashed def test_error_document_transform_dash
mock_request(:dashed) mock_request(:dash)
resource = ModelWithErrors.new resource = ModelWithErrors.new
resource.errors.add(:published_at, 'must be in the future') resource.errors.add(:published_at, 'must be in the future')
@ -405,7 +405,7 @@ module ActiveModelSerializers
{ :errors => { :errors =>
[ [
{ {
:source => { :pointer => '/data/attributes/published_at' }, :source => { :pointer => '/data/attributes/published-at' },
:detail => 'must be in the future' :detail => 'must be in the future'
}, },
{ {
@ -417,7 +417,7 @@ module ActiveModelSerializers
assert_equal expected_errors_object, result assert_equal expected_errors_object, result
end end
def test_error_document_key_transform_unaltered def test_error_document_transform_unaltered
mock_request(:unaltered) mock_request(:unaltered)
resource = ModelWithErrors.new resource = ModelWithErrors.new
@ -438,7 +438,7 @@ module ActiveModelSerializers
assert_equal expected_errors_object, result assert_equal expected_errors_object, result
end end
def test_error_document_key_transform_undefined def test_error_document_transform_undefined
mock_request(:krazy) mock_request(:krazy)
resource = ModelWithErrors.new resource = ModelWithErrors.new
@ -453,7 +453,7 @@ module ActiveModelSerializers
end end
end end
def test_error_document_key_transform_camel def test_error_document_transform_camel
mock_request(:camel) mock_request(:camel)
resource = ModelWithErrors.new resource = ModelWithErrors.new
@ -467,14 +467,14 @@ module ActiveModelSerializers
expected_errors_object = expected_errors_object =
{ :Errors => { :Errors =>
[ [
{ :Source => { :Pointer => '/data/attributes/published_at' }, :Detail => 'must be in the future' }, { :Source => { :Pointer => '/data/attributes/PublishedAt' }, :Detail => 'must be in the future' },
{ :Source => { :Pointer => '/data/attributes/title' }, :Detail => 'must be longer' } { :Source => { :Pointer => '/data/attributes/Title' }, :Detail => 'must be longer' }
] ]
} }
assert_equal expected_errors_object, result assert_equal expected_errors_object, result
end end
def test_error_document_key_transform_camel_lower def test_error_document_transform_camel_lower
mock_request(:camel_lower) mock_request(:camel_lower)
resource = ModelWithErrors.new resource = ModelWithErrors.new
@ -488,7 +488,7 @@ module ActiveModelSerializers
expected_errors_object = expected_errors_object =
{ :errors => { :errors =>
[ [
{ :source => { :pointer => '/data/attributes/published_at' }, :detail => 'must be in the future' }, { :source => { :pointer => '/data/attributes/publishedAt' }, :detail => 'must be in the future' },
{ :source => { :pointer => '/data/attributes/title' }, :detail => 'must be longer' } { :source => { :pointer => '/data/attributes/title' }, :detail => 'must be longer' }
] ]
} }

View File

@ -70,7 +70,8 @@ class ApiAssertion
}, },
'author' => { 'author' => {
'id' => 42, 'id' => 42,
'name' => 'Joao Moura.' 'first_name' => 'Joao',
'last_name' => 'Moura'
} }
} }
} }

View File

@ -0,0 +1,34 @@
require_relative './benchmarking_support'
require_relative './app'
time = 10
disable_gc = true
ActiveModelSerializers.config.key_transform = :unaltered
comments = (0..50).map do |i|
Comment.new(id: i, body: 'ZOMG A COMMENT')
end
author = Author.new(id: 42, first_name: 'Joao', last_name: 'Moura')
post = Post.new(id: 1337, title: 'New Post', blog: nil, body: 'Body', comments: comments, author: author)
serializer = PostSerializer.new(post)
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
serialization = adapter.as_json
Benchmark.ams('camel', time: time, disable_gc: disable_gc) do
ActiveModelSerializers::KeyTransform.camel(serialization)
end
Benchmark.ams('camel_lower', time: time, disable_gc: disable_gc) do
ActiveModelSerializers::KeyTransform.camel_lower(serialization)
end
Benchmark.ams('dash', time: time, disable_gc: disable_gc) do
ActiveModelSerializers::KeyTransform.dash(serialization)
end
Benchmark.ams('unaltered', time: time, disable_gc: disable_gc) do
ActiveModelSerializers::KeyTransform.unaltered(serialization)
end
Benchmark.ams('underscore', time: time, disable_gc: disable_gc) do
ActiveModelSerializers::KeyTransform.underscore(serialization)
end

View File

@ -8,7 +8,7 @@ class PostController < ActionController::Base
else else
comments = [Comment.new(id: 1, body: 'ZOMG A COMMENT')] comments = [Comment.new(id: 1, body: 'ZOMG A COMMENT')]
end end
author = Author.new(id: 42, name: 'Joao Moura.') author = Author.new(id: 42, first_name: 'Joao', last_name: 'Moura')
Post.new(id: 1337, title: 'New Post', blog: nil, body: 'Body', comments: comments, author: author) Post.new(id: 1337, title: 'New Post', blog: nil, body: 'Body', comments: comments, author: author)
end end

View File

@ -1,6 +1,6 @@
Rails.configuration.serializers = [] Rails.configuration.serializers = []
class AuthorSerializer < ActiveModel::Serializer class AuthorSerializer < ActiveModel::Serializer
attributes :id, :name attributes :id, :first_name, :last_name
has_many :posts, embed: :ids has_many :posts, embed: :ids
has_one :bio has_one :bio
@ -27,6 +27,15 @@ class PostSerializer < ActiveModel::Serializer
belongs_to :blog, serializer: BlogSerializer belongs_to :blog, serializer: BlogSerializer
belongs_to :author, serializer: AuthorSerializer belongs_to :author, serializer: AuthorSerializer
link(:post_authors) { 'https://example.com/post_authors' }
meta do
{
rating: 5,
favorite_count: 10
}
end
def blog def blog
Blog.new(id: 999, name: 'Custom blog') Blog.new(id: 999, name: 'Custom blog')
end end
@ -34,7 +43,7 @@ end
Rails.configuration.serializers << PostSerializer Rails.configuration.serializers << PostSerializer
class CachingAuthorSerializer < AuthorSerializer class CachingAuthorSerializer < AuthorSerializer
cache key: 'writer', only: [:name], skip_digest: true cache key: 'writer', only: [:first_name, :last_name], skip_digest: true
end end
Rails.configuration.serializers << CachingAuthorSerializer Rails.configuration.serializers << CachingAuthorSerializer
@ -63,7 +72,8 @@ if ENV['ENABLE_ACTIVE_RECORD'] == 'true'
t.timestamps null: false t.timestamps null: false
end end
create_table :authors, force: true do |t| create_table :authors, force: true do |t|
t.string :name t.string :first_name
t.string :last_name
t.timestamps null: false t.timestamps null: false
end end
create_table :posts, force: true do |t| create_table :posts, force: true do |t|
@ -144,7 +154,7 @@ else
end end
class Author < BenchmarkModel class Author < BenchmarkModel
attr_accessor :id, :name, :posts attr_accessor :id, :first_name, :last_name, :posts
end end
class Post < BenchmarkModel class Post < BenchmarkModel