mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-23 06:16:50 +00:00
Merge branch 'beauby-association-blocks'
Has Rubocop failures to be fixed in next commit.
This commit is contained in:
commit
e51597480a
@ -6,6 +6,8 @@ module ActiveModel
|
|||||||
autoload :PaginationLinks
|
autoload :PaginationLinks
|
||||||
autoload :FragmentCache
|
autoload :FragmentCache
|
||||||
autoload :Link
|
autoload :Link
|
||||||
|
autoload :Association
|
||||||
|
autoload :ResourceIdentifier
|
||||||
autoload :Deserialization
|
autoload :Deserialization
|
||||||
|
|
||||||
# TODO: if we like this abstraction and other API objects to it,
|
# TODO: if we like this abstraction and other API objects to it,
|
||||||
@ -97,7 +99,7 @@ module ActiveModel
|
|||||||
end
|
end
|
||||||
|
|
||||||
def process_resource(serializer, primary)
|
def process_resource(serializer, primary)
|
||||||
resource_identifier = resource_identifier_for(serializer)
|
resource_identifier = JsonApi::ResourceIdentifier.new(serializer).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)
|
||||||
@ -127,37 +129,13 @@ module ActiveModel
|
|||||||
process_relationships(serializer, include_tree)
|
process_relationships(serializer, include_tree)
|
||||||
end
|
end
|
||||||
|
|
||||||
def resource_identifier_type_for(serializer)
|
|
||||||
return serializer._type if serializer._type
|
|
||||||
if ActiveModelSerializers.config.jsonapi_resource_type == :singular
|
|
||||||
serializer.object.class.model_name.singular
|
|
||||||
else
|
|
||||||
serializer.object.class.model_name.plural
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_identifier_id_for(serializer)
|
|
||||||
if serializer.respond_to?(:id)
|
|
||||||
serializer.id
|
|
||||||
else
|
|
||||||
serializer.object.id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_identifier_for(serializer)
|
|
||||||
type = resource_identifier_type_for(serializer)
|
|
||||||
id = resource_identifier_id_for(serializer)
|
|
||||||
|
|
||||||
{ id: id.to_s, type: type }
|
|
||||||
end
|
|
||||||
|
|
||||||
def attributes_for(serializer, fields)
|
def attributes_for(serializer, fields)
|
||||||
serializer.attributes(fields).except(:id)
|
serializer.attributes(fields).except(:id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def resource_object_for(serializer)
|
def resource_object_for(serializer)
|
||||||
resource_object = cache_check(serializer) do
|
resource_object = cache_check(serializer) do
|
||||||
resource_object = resource_identifier_for(serializer)
|
resource_object = JsonApi::ResourceIdentifier.new(serializer).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)
|
||||||
@ -165,7 +143,8 @@ module ActiveModel
|
|||||||
resource_object
|
resource_object
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships = relationships_for(serializer)
|
requested_associations = fieldset.fields_for(resource_object[:type]) || '*'
|
||||||
|
relationships = relationships_for(serializer, requested_associations)
|
||||||
resource_object[:relationships] = relationships if relationships.any?
|
resource_object[:relationships] = relationships if relationships.any?
|
||||||
|
|
||||||
links = links_for(serializer)
|
links = links_for(serializer)
|
||||||
@ -174,24 +153,15 @@ module ActiveModel
|
|||||||
resource_object
|
resource_object
|
||||||
end
|
end
|
||||||
|
|
||||||
def relationship_value_for(serializer, options = {})
|
def relationships_for(serializer, requested_associations)
|
||||||
if serializer.respond_to?(:each)
|
|
||||||
serializer.map { |s| resource_identifier_for(s) }
|
|
||||||
else
|
|
||||||
if options[:virtual_value]
|
|
||||||
options[:virtual_value]
|
|
||||||
elsif serializer && serializer.object
|
|
||||||
resource_identifier_for(serializer)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def relationships_for(serializer)
|
|
||||||
resource_type = resource_identifier_type_for(serializer)
|
|
||||||
requested_associations = fieldset.fields_for(resource_type) || '*'
|
|
||||||
include_tree = IncludeTree.from_include_args(requested_associations)
|
include_tree = 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] = { data: relationship_value_for(association.serializer, association.options) }
|
hash[association.key] = JsonApi::Association.new(serializer,
|
||||||
|
association.serializer,
|
||||||
|
association.options,
|
||||||
|
association.links,
|
||||||
|
association.meta)
|
||||||
|
.as_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
48
lib/active_model/serializer/adapter/json_api/association.rb
Normal file
48
lib/active_model/serializer/adapter/json_api/association.rb
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
module ActiveModel
|
||||||
|
class Serializer
|
||||||
|
module Adapter
|
||||||
|
class JsonApi
|
||||||
|
class Association
|
||||||
|
def initialize(parent_serializer, serializer, options, links, meta)
|
||||||
|
@object = parent_serializer.object
|
||||||
|
@scope = parent_serializer.scope
|
||||||
|
|
||||||
|
@options = options
|
||||||
|
@data = data_for(serializer, options)
|
||||||
|
@links = links
|
||||||
|
.map { |key, value| { key => Link.new(parent_serializer, value).as_json } }
|
||||||
|
.reduce({}, :merge)
|
||||||
|
@meta = meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
|
||||||
|
end
|
||||||
|
|
||||||
|
def as_json
|
||||||
|
hash = {}
|
||||||
|
hash[:data] = @data if @options[:include_data]
|
||||||
|
hash[:links] = @links if @links.any?
|
||||||
|
hash[:meta] = @meta if @meta
|
||||||
|
|
||||||
|
hash
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
attr_reader :object, :scope
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def data_for(serializer, options)
|
||||||
|
if serializer.respond_to?(:each)
|
||||||
|
serializer.map { |s| ResourceIdentifier.new(s).as_json }
|
||||||
|
else
|
||||||
|
if options[:virtual_value]
|
||||||
|
options[:virtual_value]
|
||||||
|
elsif serializer && serializer.object
|
||||||
|
ResourceIdentifier.new(serializer).as_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
module ActiveModel
|
||||||
|
class Serializer
|
||||||
|
module Adapter
|
||||||
|
class JsonApi
|
||||||
|
class ResourceIdentifier
|
||||||
|
def initialize(serializer)
|
||||||
|
@id = id_for(serializer)
|
||||||
|
@type = type_for(serializer)
|
||||||
|
end
|
||||||
|
|
||||||
|
def as_json
|
||||||
|
{ id: @id.to_s, type: @type }
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
attr_reader :object, :scope
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def type_for(serializer)
|
||||||
|
return serializer._type if serializer._type
|
||||||
|
if ActiveModelSerializers.config.jsonapi_resource_type == :singular
|
||||||
|
serializer.object.class.model_name.singular
|
||||||
|
else
|
||||||
|
serializer.object.class.model_name.plural
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def id_for(serializer)
|
||||||
|
if serializer.respond_to?(:id)
|
||||||
|
serializer.id
|
||||||
|
else
|
||||||
|
serializer.object.id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -9,7 +9,7 @@ module ActiveModel
|
|||||||
# @example
|
# @example
|
||||||
# Association.new(:comments, CommentSummarySerializer)
|
# Association.new(:comments, CommentSummarySerializer)
|
||||||
#
|
#
|
||||||
Association = Struct.new(:name, :serializer, :options) do
|
Association = Struct.new(:name, :serializer, :options, :links, :meta) do
|
||||||
# @return [Symbol]
|
# @return [Symbol]
|
||||||
#
|
#
|
||||||
def key
|
def key
|
||||||
|
|||||||
@ -34,6 +34,38 @@ module ActiveModel
|
|||||||
# So you can inspect reflections in your Adapters.
|
# So you can inspect reflections in your Adapters.
|
||||||
#
|
#
|
||||||
class Reflection < Field
|
class Reflection < Field
|
||||||
|
def initialize(*)
|
||||||
|
super
|
||||||
|
@_links = {}
|
||||||
|
@_include_data = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def link(name, value = nil, &block)
|
||||||
|
@_links[name] = block || value
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def meta(value = nil, &block)
|
||||||
|
@_meta = block || value
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_data(value = true)
|
||||||
|
@_include_data = value
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def value(serializer)
|
||||||
|
@object = serializer.object
|
||||||
|
@scope = serializer.scope
|
||||||
|
|
||||||
|
if block
|
||||||
|
instance_eval(&block)
|
||||||
|
else
|
||||||
|
serializer.read_attribute_for_serialization(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Build association. This method is used internally to
|
# Build association. This method is used internally to
|
||||||
# build serializer's association by its reflection.
|
# build serializer's association by its reflection.
|
||||||
#
|
#
|
||||||
@ -59,6 +91,7 @@ module ActiveModel
|
|||||||
association_value = value(subject)
|
association_value = value(subject)
|
||||||
reflection_options = options.dup
|
reflection_options = options.dup
|
||||||
serializer_class = subject.class.serializer_for(association_value, reflection_options)
|
serializer_class = subject.class.serializer_for(association_value, reflection_options)
|
||||||
|
reflection_options[:include_data] = @_include_data
|
||||||
|
|
||||||
if serializer_class
|
if serializer_class
|
||||||
begin
|
begin
|
||||||
@ -73,9 +106,13 @@ module ActiveModel
|
|||||||
reflection_options[:virtual_value] = association_value
|
reflection_options[:virtual_value] = association_value
|
||||||
end
|
end
|
||||||
|
|
||||||
Association.new(name, serializer, reflection_options)
|
Association.new(name, serializer, reflection_options, @_links, @_meta)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
attr_accessor :object, :scope
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def serializer_options(subject, parent_serializer_options, reflection_options)
|
def serializer_options(subject, parent_serializer_options, reflection_options)
|
||||||
|
|||||||
@ -17,11 +17,23 @@ module ActiveModel
|
|||||||
link :yet_another do
|
link :yet_another do
|
||||||
"//example.com/resource/#{object.id}"
|
"//example.com/resource/#{object.id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
has_many :posts do
|
||||||
|
link :self do
|
||||||
|
href '//example.com/link_author/relationships/posts'
|
||||||
|
meta stuff: 'value'
|
||||||
|
end
|
||||||
|
link :related do
|
||||||
|
href '//example.com/link_author/posts'
|
||||||
|
meta count: object.posts.count
|
||||||
|
end
|
||||||
|
include_data false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@post = Post.new(id: 1337, comments: [], author: nil)
|
@post = Post.new(id: 1337, comments: [], author: nil)
|
||||||
@author = LinkAuthor.new(id: 1337)
|
@author = LinkAuthor.new(id: 1337, posts: [@post])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_toplevel_links
|
def test_toplevel_links
|
||||||
@ -61,6 +73,23 @@ module ActiveModel
|
|||||||
}
|
}
|
||||||
assert_equal(expected, hash[:data][:links])
|
assert_equal(expected, hash[:data][:links])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_relationship_links
|
||||||
|
hash = serializable(@author, adapter: :json_api).serializable_hash
|
||||||
|
expected = {
|
||||||
|
links: {
|
||||||
|
self: {
|
||||||
|
href: '//example.com/link_author/relationships/posts',
|
||||||
|
meta: { stuff: 'value' }
|
||||||
|
},
|
||||||
|
related: {
|
||||||
|
href: '//example.com/link_author/posts',
|
||||||
|
meta: { count: 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_equal(expected, hash[:data][:relationships][:posts])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -32,13 +32,13 @@ module ActiveModel
|
|||||||
|
|
||||||
case key
|
case key
|
||||||
when :posts
|
when :posts
|
||||||
assert_equal({}, options)
|
assert_equal({ include_data: true }, options)
|
||||||
assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
|
assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
|
||||||
when :bio
|
when :bio
|
||||||
assert_equal({}, options)
|
assert_equal({ include_data: true }, options)
|
||||||
assert_nil serializer
|
assert_nil serializer
|
||||||
when :roles
|
when :roles
|
||||||
assert_equal({}, options)
|
assert_equal({ include_data: true }, options)
|
||||||
assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
|
assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
|
||||||
else
|
else
|
||||||
flunk "Unknown association: #{key}"
|
flunk "Unknown association: #{key}"
|
||||||
@ -80,7 +80,7 @@ module ActiveModel
|
|||||||
flunk "Unknown association: #{key}"
|
flunk "Unknown association: #{key}"
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal({}, association.options)
|
assert_equal({ include_data: true }, association.options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user