mirror of
https://github.com/ditkrg/active_model_serializers.git
synced 2026-01-22 13:56:53 +00:00
Refactor: introduce lazy association
This commit is contained in:
parent
34d55e4729
commit
7697d9f5ec
@ -1,3 +1,5 @@
|
||||
require 'active_model/serializer/lazy_association'
|
||||
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
# This class holds all information about serializer's association.
|
||||
@ -10,14 +12,22 @@ module ActiveModel
|
||||
# Association.new(:comments, { serializer: CommentSummarySerializer })
|
||||
#
|
||||
class Association < Field
|
||||
attr_reader :lazy_association
|
||||
delegate :include_data?, :virtual_value, to: :lazy_association
|
||||
|
||||
def initialize(*)
|
||||
super
|
||||
@lazy_association = LazyAssociation.new(name, options, block)
|
||||
end
|
||||
|
||||
# @return [Symbol]
|
||||
def key
|
||||
options.fetch(:key, name)
|
||||
end
|
||||
|
||||
# @return [ActiveModel::Serializer, nil]
|
||||
def serializer
|
||||
options[:serializer]
|
||||
# @return [True,False]
|
||||
def key?
|
||||
options.key?(:key)
|
||||
end
|
||||
|
||||
# @return [Hash]
|
||||
@ -30,21 +40,30 @@ module ActiveModel
|
||||
options[:meta]
|
||||
end
|
||||
|
||||
def polymorphic?
|
||||
true == options[:polymorphic]
|
||||
end
|
||||
|
||||
# @api private
|
||||
def serializable_hash(adapter_options, adapter_instance)
|
||||
return options[:virtual_value] if options[:virtual_value]
|
||||
object = serializer && serializer.object
|
||||
return unless object
|
||||
association_serializer = lazy_association.serializer
|
||||
return virtual_value if virtual_value
|
||||
association_object = association_serializer && association_serializer.object
|
||||
return unless association_object
|
||||
|
||||
serialization = serializer.serializable_hash(adapter_options, {}, adapter_instance)
|
||||
serialization = association_serializer.serializable_hash(adapter_options, {}, adapter_instance)
|
||||
|
||||
if options[:polymorphic] && serialization
|
||||
polymorphic_type = object.class.name.underscore
|
||||
if polymorphic? && serialization
|
||||
polymorphic_type = association_object.class.name.underscore
|
||||
serialization = { type: polymorphic_type, polymorphic_type.to_sym => serialization }
|
||||
end
|
||||
|
||||
serialization
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
delegate :reflection, to: :lazy_association
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -193,12 +193,13 @@ module ActiveModel
|
||||
cache_keys << object_cache_key(serializer, adapter_instance)
|
||||
|
||||
serializer.associations(include_directive).each do |association|
|
||||
if association.serializer.respond_to?(:each)
|
||||
association.serializer.each do |sub_serializer|
|
||||
association_serializer = association.lazy_association.serializer
|
||||
if association_serializer.respond_to?(:each)
|
||||
association_serializer.each do |sub_serializer|
|
||||
cache_keys << object_cache_key(sub_serializer, adapter_instance)
|
||||
end
|
||||
else
|
||||
cache_keys << object_cache_key(association.serializer, adapter_instance)
|
||||
cache_keys << object_cache_key(association_serializer, adapter_instance)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
22
lib/active_model/serializer/lazy_association.rb
Normal file
22
lib/active_model/serializer/lazy_association.rb
Normal file
@ -0,0 +1,22 @@
|
||||
module ActiveModel
|
||||
class Serializer
|
||||
class LazyAssociation < Field
|
||||
|
||||
def serializer
|
||||
options[:serializer]
|
||||
end
|
||||
|
||||
def include_data?
|
||||
options[:include_data]
|
||||
end
|
||||
|
||||
def virtual_value
|
||||
options[:virtual_value]
|
||||
end
|
||||
|
||||
def reflection
|
||||
options[:reflection]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -170,7 +170,11 @@ module ActiveModel
|
||||
end
|
||||
|
||||
association_block = nil
|
||||
Association.new(name, reflection_options, association_block)
|
||||
reflection_options[:reflection] = self
|
||||
reflection_options[:parent_serializer] = parent_serializer
|
||||
reflection_options[:parent_serializer_options] = parent_serializer_options
|
||||
reflection_options[:include_slice] = include_slice
|
||||
Association.new(name, reflection_options, block)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
@ -257,7 +257,7 @@ module ActiveModelSerializers
|
||||
|
||||
def process_relationships(serializer, include_slice)
|
||||
serializer.associations(include_slice).each do |association|
|
||||
process_relationship(association.serializer, include_slice[association.key])
|
||||
process_relationship(association.lazy_association.serializer, include_slice[association.key])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -15,9 +15,7 @@ module ActiveModelSerializers
|
||||
def as_json
|
||||
hash = {}
|
||||
|
||||
if association.options[:include_data]
|
||||
hash[:data] = data_for(association)
|
||||
end
|
||||
hash[:data] = data_for(association) if association.include_data?
|
||||
|
||||
links = links_for(association)
|
||||
hash[:links] = links if links.any?
|
||||
@ -36,10 +34,10 @@ module ActiveModelSerializers
|
||||
private
|
||||
|
||||
def data_for(association)
|
||||
serializer = association.serializer
|
||||
serializer = association.lazy_association.serializer
|
||||
if serializer.respond_to?(:each)
|
||||
serializer.map { |s| ResourceIdentifier.new(s, serializable_resource_options).as_json }
|
||||
elsif (virtual_value = association.options[:virtual_value])
|
||||
elsif (virtual_value = association.virtual_value)
|
||||
virtual_value
|
||||
elsif serializer && serializer.object
|
||||
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
|
||||
|
||||
@ -30,18 +30,17 @@ module ActiveModel
|
||||
def test_has_many_and_has_one
|
||||
@author_serializer.associations.each do |association|
|
||||
key = association.key
|
||||
serializer = association.serializer
|
||||
options = association.options
|
||||
serializer = association.lazy_association.serializer
|
||||
|
||||
case key
|
||||
when :posts
|
||||
assert_equal true, options.fetch(:include_data)
|
||||
assert_equal true, association.include_data?
|
||||
assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
|
||||
when :bio
|
||||
assert_equal true, options.fetch(:include_data)
|
||||
assert_equal true, association.include_data?
|
||||
assert_nil serializer
|
||||
when :roles
|
||||
assert_equal true, options.fetch(:include_data)
|
||||
assert_equal true, association.include_data?
|
||||
assert_kind_of(ActiveModelSerializers.config.collection_serializer, serializer)
|
||||
else
|
||||
flunk "Unknown association: #{key}"
|
||||
@ -56,12 +55,11 @@ module ActiveModel
|
||||
end
|
||||
post_serializer_class.new(@post).associations.each do |association|
|
||||
key = association.key
|
||||
serializer = association.serializer
|
||||
options = association.options
|
||||
serializer = association.lazy_association.serializer
|
||||
|
||||
assert_equal :tags, key
|
||||
assert_nil serializer
|
||||
assert_equal [{ id: 'tagid', name: '#hashtagged' }].to_json, options[:virtual_value].to_json
|
||||
assert_equal [{ id: 'tagid', name: '#hashtagged' }].to_json, association.virtual_value.to_json
|
||||
end
|
||||
end
|
||||
|
||||
@ -70,7 +68,7 @@ module ActiveModel
|
||||
.associations
|
||||
.detect { |assoc| assoc.key == :comments }
|
||||
|
||||
comment_serializer = association.serializer.first
|
||||
comment_serializer = association.lazy_association.serializer.first
|
||||
class << comment_serializer
|
||||
def custom_options
|
||||
instance_options
|
||||
@ -82,7 +80,7 @@ module ActiveModel
|
||||
def test_belongs_to
|
||||
@comment_serializer.associations.each do |association|
|
||||
key = association.key
|
||||
serializer = association.serializer
|
||||
serializer = association.lazy_association.serializer
|
||||
|
||||
case key
|
||||
when :post
|
||||
@ -93,7 +91,7 @@ module ActiveModel
|
||||
flunk "Unknown association: #{key}"
|
||||
end
|
||||
|
||||
assert_equal true, association.options.fetch(:include_data)
|
||||
assert_equal true, association.include_data?
|
||||
end
|
||||
end
|
||||
|
||||
@ -203,11 +201,11 @@ module ActiveModel
|
||||
@post_serializer.associations.each do |association|
|
||||
case association.key
|
||||
when :comments
|
||||
assert_instance_of(ResourceNamespace::CommentSerializer, association.serializer.first)
|
||||
assert_instance_of(ResourceNamespace::CommentSerializer, association.lazy_association.serializer.first)
|
||||
when :author
|
||||
assert_instance_of(ResourceNamespace::AuthorSerializer, association.serializer)
|
||||
assert_instance_of(ResourceNamespace::AuthorSerializer, association.lazy_association.serializer)
|
||||
when :description
|
||||
assert_instance_of(ResourceNamespace::DescriptionSerializer, association.serializer)
|
||||
assert_instance_of(ResourceNamespace::DescriptionSerializer, association.lazy_association.serializer)
|
||||
else
|
||||
flunk "Unknown association: #{key}"
|
||||
end
|
||||
@ -245,11 +243,11 @@ module ActiveModel
|
||||
@post_serializer.associations.each do |association|
|
||||
case association.key
|
||||
when :comments
|
||||
assert_instance_of(PostSerializer::CommentSerializer, association.serializer.first)
|
||||
assert_instance_of(PostSerializer::CommentSerializer, association.lazy_association.serializer.first)
|
||||
when :author
|
||||
assert_instance_of(PostSerializer::AuthorSerializer, association.serializer)
|
||||
assert_instance_of(PostSerializer::AuthorSerializer, association.lazy_association.serializer)
|
||||
when :description
|
||||
assert_instance_of(PostSerializer::DescriptionSerializer, association.serializer)
|
||||
assert_instance_of(PostSerializer::DescriptionSerializer, association.lazy_association.serializer)
|
||||
else
|
||||
flunk "Unknown association: #{key}"
|
||||
end
|
||||
@ -260,7 +258,7 @@ module ActiveModel
|
||||
def test_conditional_associations
|
||||
model = Class.new(::Model) do
|
||||
attributes :true, :false
|
||||
associations :association
|
||||
associations :something
|
||||
end.new(true: true, false: false)
|
||||
|
||||
scenarios = [
|
||||
@ -284,7 +282,7 @@ module ActiveModel
|
||||
|
||||
scenarios.each do |s|
|
||||
serializer = Class.new(ActiveModel::Serializer) do
|
||||
belongs_to :association, s[:options]
|
||||
belongs_to :something, s[:options]
|
||||
|
||||
def true
|
||||
true
|
||||
@ -296,7 +294,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
hash = serializable(model, serializer: serializer).serializable_hash
|
||||
assert_equal(s[:included], hash.key?(:association), "Error with #{s[:options]}")
|
||||
assert_equal(s[:included], hash.key?(:something), "Error with #{s[:options]}")
|
||||
end
|
||||
end
|
||||
|
||||
@ -341,8 +339,8 @@ module ActiveModel
|
||||
@author_serializer = AuthorSerializer.new(@author)
|
||||
@inherited_post_serializer = InheritedPostSerializer.new(@post)
|
||||
@inherited_author_serializer = InheritedAuthorSerializer.new(@author)
|
||||
@author_associations = @author_serializer.associations.to_a
|
||||
@inherited_author_associations = @inherited_author_serializer.associations.to_a
|
||||
@author_associations = @author_serializer.associations.to_a.sort_by(&:name)
|
||||
@inherited_author_associations = @inherited_author_serializer.associations.to_a.sort_by(&:name)
|
||||
@post_associations = @post_serializer.associations.to_a
|
||||
@inherited_post_associations = @inherited_post_serializer.associations.to_a
|
||||
end
|
||||
@ -361,28 +359,35 @@ module ActiveModel
|
||||
|
||||
test 'a serializer inheriting from another serializer can redefine has_many and has_one associations' do
|
||||
expected = [:roles, :bio].sort
|
||||
result = (@inherited_author_associations - @author_associations).map(&:name).sort
|
||||
result = (@inherited_author_associations.map(&:reflection) - @author_associations.map(&:reflection)).map(&:name)
|
||||
assert_equal(result, expected)
|
||||
assert_equal [true, false, true], @inherited_author_associations.map(&:polymorphic?)
|
||||
assert_equal [false, false, false], @author_associations.map(&:polymorphic?)
|
||||
end
|
||||
|
||||
test 'a serializer inheriting from another serializer can redefine belongs_to associations' do
|
||||
assert_equal [:author, :comments, :blog], @post_associations.map(&:name)
|
||||
assert_equal [:author, :comments, :blog, :comments], @inherited_post_associations.map(&:name)
|
||||
|
||||
refute @post_associations.detect { |assoc| assoc.name == :author }.options.key?(:polymorphic)
|
||||
assert_equal true, @inherited_post_associations.detect { |assoc| assoc.name == :author }.options.fetch(:polymorphic)
|
||||
refute @post_associations.detect { |assoc| assoc.name == :author }.polymorphic?
|
||||
assert @inherited_post_associations.detect { |assoc| assoc.name == :author }.polymorphic?
|
||||
|
||||
refute @post_associations.detect { |assoc| assoc.name == :comments }.options.key?(:key)
|
||||
refute @post_associations.detect { |assoc| assoc.name == :comments }.key?
|
||||
original_comment_assoc, new_comments_assoc = @inherited_post_associations.select { |assoc| assoc.name == :comments }
|
||||
refute original_comment_assoc.options.key?(:key)
|
||||
assert_equal :reviews, new_comments_assoc.options.fetch(:key)
|
||||
refute original_comment_assoc.key?
|
||||
assert_equal :reviews, new_comments_assoc.key
|
||||
|
||||
assert_equal @post_associations.detect { |assoc| assoc.name == :blog }, @inherited_post_associations.detect { |assoc| assoc.name == :blog }
|
||||
original_blog = @post_associations.detect { |assoc| assoc.name == :blog }
|
||||
inherited_blog = @inherited_post_associations.detect { |assoc| assoc.name == :blog }
|
||||
original_parent_serializer = original_blog.lazy_association.options.delete(:parent_serializer)
|
||||
inherited_parent_serializer = inherited_blog.lazy_association.options.delete(:parent_serializer)
|
||||
assert_equal PostSerializer, original_parent_serializer.class
|
||||
assert_equal InheritedPostSerializer, inherited_parent_serializer.class
|
||||
end
|
||||
|
||||
test 'a serializer inheriting from another serializer can have an additional association with the same name but with different key' do
|
||||
expected = [:author, :comments, :blog, :reviews].sort
|
||||
result = @inherited_post_serializer.associations.map { |a| a.options.fetch(:key, a.name) }.sort
|
||||
result = @inherited_post_serializer.associations.map(&:key).sort
|
||||
assert_equal(result, expected)
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
Reference in New Issue
Block a user