mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-25 07:16:49 +00:00
Apply key transforms to keys referenced in values
This commit is contained in:
@@ -58,25 +58,32 @@ module ActiveModelSerializers
|
||||
json
|
||||
end
|
||||
|
||||
def default_key_transform
|
||||
:unaltered
|
||||
end
|
||||
class << self
|
||||
# Sets the default transform for the adapter.
|
||||
#
|
||||
# @return [Symbol] the default transform for the adapter
|
||||
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
|
||||
# Determines the transform to use in order of precedence:
|
||||
# adapter option, global config, adapter default.
|
||||
#
|
||||
# @param options [Object]
|
||||
# @return [Symbol] the transform to use
|
||||
def transform(options)
|
||||
return options[:key_transform] if options && options[: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)
|
||||
# Transforms the casing of the supplied value.
|
||||
#
|
||||
# @param value [Object] the value to be transformed
|
||||
# @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
|
||||
|
||||
@@ -4,7 +4,7 @@ module ActiveModelSerializers
|
||||
def serializable_hash(options = nil)
|
||||
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
|
||||
|
||||
@@ -37,8 +37,8 @@ module ActiveModelSerializers
|
||||
@fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
|
||||
end
|
||||
|
||||
def default_key_transform
|
||||
:dashed
|
||||
def self.default_key_transform
|
||||
:dash
|
||||
end
|
||||
|
||||
# {http://jsonapi.org/format/#crud Requests are transactional, i.e. success or failure}
|
||||
@@ -48,9 +48,9 @@ module ActiveModelSerializers
|
||||
document = if serializer.success?
|
||||
success_document(options)
|
||||
else
|
||||
failure_document
|
||||
failure_document(options)
|
||||
end
|
||||
transform_key_casing!(document, options[:serialization_context])
|
||||
self.class.transform_key_casing!(document, options)
|
||||
end
|
||||
|
||||
# {http://jsonapi.org/format/#document-top-level Primary data}
|
||||
@@ -71,7 +71,7 @@ module ActiveModelSerializers
|
||||
def success_document(options)
|
||||
is_collection = serializer.respond_to?(:each)
|
||||
serializers = is_collection ? serializer : [serializer]
|
||||
primary_data, included = resource_objects_for(serializers)
|
||||
primary_data, included = resource_objects_for(serializers, options)
|
||||
|
||||
hash = {}
|
||||
# toplevel_data
|
||||
@@ -148,7 +148,7 @@ module ActiveModelSerializers
|
||||
# }.reject! {|_,v| v.nil? }
|
||||
# prs:
|
||||
# https://github.com/rails-api/active_model_serializers/pull/1004
|
||||
def failure_document
|
||||
def failure_document(options)
|
||||
hash = {}
|
||||
# PR Please :)
|
||||
# Jsonapi.add!(hash)
|
||||
@@ -163,10 +163,10 @@ module ActiveModelSerializers
|
||||
# ]
|
||||
if serializer.respond_to?(:each)
|
||||
hash[:errors] = serializer.flat_map do |error_serializer|
|
||||
Error.resource_errors(error_serializer)
|
||||
Error.resource_errors(error_serializer, options)
|
||||
end
|
||||
else
|
||||
hash[:errors] = Error.resource_errors(serializer)
|
||||
hash[:errors] = Error.resource_errors(serializer, options)
|
||||
end
|
||||
hash
|
||||
end
|
||||
@@ -224,21 +224,21 @@ module ActiveModelSerializers
|
||||
# [x] url helpers https://github.com/rails-api/active_model_serializers/issues/1269
|
||||
# meta
|
||||
# [x] https://github.com/rails-api/active_model_serializers/pull/1340
|
||||
def resource_objects_for(serializers)
|
||||
def resource_objects_for(serializers, options)
|
||||
@primary = []
|
||||
@included = []
|
||||
@resource_identifiers = Set.new
|
||||
serializers.each { |serializer| process_resource(serializer, true) }
|
||||
serializers.each { |serializer| process_relationships(serializer, @include_tree) }
|
||||
serializers.each { |serializer| process_resource(serializer, true, options) }
|
||||
serializers.each { |serializer| process_relationships(serializer, @include_tree, options) }
|
||||
|
||||
[@primary, @included]
|
||||
end
|
||||
|
||||
def process_resource(serializer, primary)
|
||||
resource_identifier = ResourceIdentifier.new(serializer).as_json
|
||||
def process_resource(serializer, primary, options)
|
||||
resource_identifier = ResourceIdentifier.new(serializer, options).as_json
|
||||
return false unless @resource_identifiers.add?(resource_identifier)
|
||||
|
||||
resource_object = resource_object_for(serializer)
|
||||
resource_object = resource_object_for(serializer, options)
|
||||
if primary
|
||||
@primary << resource_object
|
||||
else
|
||||
@@ -248,21 +248,21 @@ module ActiveModelSerializers
|
||||
true
|
||||
end
|
||||
|
||||
def process_relationships(serializer, include_tree)
|
||||
def process_relationships(serializer, include_tree, options)
|
||||
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
|
||||
|
||||
def process_relationship(serializer, include_tree)
|
||||
def process_relationship(serializer, include_tree, options)
|
||||
if serializer.respond_to?(:each)
|
||||
serializer.each { |s| process_relationship(s, include_tree) }
|
||||
serializer.each { |s| process_relationship(s, include_tree, options) }
|
||||
return
|
||||
end
|
||||
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
|
||||
|
||||
# {http://jsonapi.org/format/#document-resource-object-attributes Document Resource Object Attributes}
|
||||
@@ -286,9 +286,9 @@ module ActiveModelSerializers
|
||||
end
|
||||
|
||||
# {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 = ResourceIdentifier.new(serializer).as_json
|
||||
resource_object = ResourceIdentifier.new(serializer, options).as_json
|
||||
|
||||
requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
|
||||
attributes = attributes_for(serializer, requested_fields)
|
||||
@@ -297,7 +297,7 @@ module ActiveModelSerializers
|
||||
end
|
||||
|
||||
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?
|
||||
|
||||
links = links_for(serializer)
|
||||
@@ -425,15 +425,16 @@ module ActiveModelSerializers
|
||||
# id: 'required-id',
|
||||
# meta: meta
|
||||
# }.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)
|
||||
serializer.associations(include_tree).each_with_object({}) do |association, hash|
|
||||
hash[association.key] = Relationship.new(
|
||||
serializer,
|
||||
association.serializer,
|
||||
association.options,
|
||||
association.links,
|
||||
association.meta
|
||||
options,
|
||||
options: association.options,
|
||||
links: association.links,
|
||||
meta: association.meta
|
||||
).as_json
|
||||
end
|
||||
end
|
||||
|
||||
@@ -182,14 +182,14 @@ module ActiveModelSerializers
|
||||
prefix_key = field_key(assoc_name, options).to_s.singularize
|
||||
hash =
|
||||
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
|
||||
{ "#{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
|
||||
|
||||
polymorphic = (options[:polymorphic] || []).include?(assoc_name.to_sym)
|
||||
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
|
||||
|
||||
hash
|
||||
@@ -198,7 +198,7 @@ module ActiveModelSerializers
|
||||
# @api private
|
||||
def parse_relationships(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)
|
||||
end
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@ module ActiveModelSerializers
|
||||
#
|
||||
# @param [ActiveModel::Serializer::ErrorSerializer] error_serializer
|
||||
# @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|
|
||||
attribute_name = JsonApi.send(:transform_key_casing!, attribute_name,
|
||||
options)
|
||||
attribute_error_objects(attribute_name, attribute_errors)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,21 +6,22 @@ module ActiveModelSerializers
|
||||
# {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-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
|
||||
@scope = parent_serializer.scope
|
||||
|
||||
@options = options
|
||||
@data = data_for(serializer, options)
|
||||
@links = links.each_with_object({}) do |(key, value), hash|
|
||||
@association_options = args.fetch(:options, {})
|
||||
@serializable_resource_options = serializable_resource_options
|
||||
@data = data_for(serializer)
|
||||
@links = args.fetch(:links, {}).each_with_object({}) do |(key, value), hash|
|
||||
hash[key] = ActiveModelSerializers::Adapter::JsonApi::Link.new(parent_serializer, value).as_json
|
||||
end
|
||||
meta = args.fetch(:meta, nil)
|
||||
@meta = meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
|
||||
end
|
||||
|
||||
def as_json
|
||||
hash = {}
|
||||
hash[:data] = data if options[:include_data]
|
||||
hash[:data] = data if association_options[:include_data]
|
||||
links = self.links
|
||||
hash[:links] = links if links.any?
|
||||
meta = self.meta
|
||||
@@ -31,17 +32,18 @@ module ActiveModelSerializers
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :object, :scope, :data, :options, :links, :meta
|
||||
attr_reader :object, :scope, :data, :serializable_resource_options,
|
||||
:association_options, :links, :meta
|
||||
|
||||
private
|
||||
|
||||
def data_for(serializer, options)
|
||||
def data_for(serializer)
|
||||
if serializer.respond_to?(:each)
|
||||
serializer.map { |s| ResourceIdentifier.new(s).as_json }
|
||||
elsif options[:virtual_value]
|
||||
options[:virtual_value]
|
||||
serializer.map { |s| ResourceIdentifier.new(s, serializable_resource_options).as_json }
|
||||
elsif association_options[:virtual_value]
|
||||
association_options[:virtual_value]
|
||||
elsif serializer && serializer.object
|
||||
ResourceIdentifier.new(serializer).as_json
|
||||
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,9 +3,10 @@ module ActiveModelSerializers
|
||||
class JsonApi
|
||||
class ResourceIdentifier
|
||||
# {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects}
|
||||
def initialize(serializer)
|
||||
def initialize(serializer, options)
|
||||
@id = id_for(serializer)
|
||||
@type = type_for(serializer)
|
||||
@type = JsonApi.send(:transform_key_casing!, type_for(serializer),
|
||||
options)
|
||||
end
|
||||
|
||||
def as_json
|
||||
|
||||
@@ -4,47 +4,67 @@ module ActiveModelSerializers
|
||||
module KeyTransform
|
||||
module_function
|
||||
|
||||
# Transforms keys to UpperCamelCase or PascalCase.
|
||||
# Transforms values 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 }
|
||||
def camel(value)
|
||||
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
|
||||
|
||||
# Transforms keys to camelCase.
|
||||
# Transforms values 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 }
|
||||
def camel_lower(value)
|
||||
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
|
||||
|
||||
# Transforms keys to dashed-case.
|
||||
# Transforms values 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 }
|
||||
def dash(value)
|
||||
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
|
||||
|
||||
# Transforms keys to underscore.
|
||||
# Transforms values to underscore_case.
|
||||
# This is the default case for deserialization in the JsonApi adapter.
|
||||
#
|
||||
# @example:
|
||||
# "some-key" => "some_key",
|
||||
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L89-L98 ActiveSupport::Inflector.underscore}
|
||||
def underscore(hash)
|
||||
hash.deep_transform_keys! { |key| key.to_s.underscore.to_sym }
|
||||
def underscore(value)
|
||||
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
|
||||
|
||||
# Returns the hash unaltered
|
||||
def unaltered(hash)
|
||||
hash
|
||||
# Returns the value unaltered
|
||||
def unaltered(value)
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -27,7 +27,6 @@ module ActiveModelSerializers
|
||||
@query_parameters = request.query_parameters
|
||||
@url_helpers = options.delete(:url_helpers) || self.class.url_helpers
|
||||
@default_url_options = options.delete(:default_url_options) || self.class.default_url_options
|
||||
@key_transform = options.delete(:key_transform)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user