Refactor Association/Reflection block value reading

This commit is contained in:
Benjamin Fleischer 2015-12-03 10:53:43 -06:00
parent cd736e0adf
commit c4feccfd10
3 changed files with 35 additions and 24 deletions

View File

@ -12,11 +12,11 @@ module ActiveModel
DEFAULT_INCLUDE_TREE = ActiveModel::Serializer::IncludeTree.from_string('*') DEFAULT_INCLUDE_TREE = ActiveModel::Serializer::IncludeTree.from_string('*')
included do |base| included do
base.class_attribute :serialized_associations, instance_writer: false # @api public: maps association name to 'Reflection' instance with_options instance_writer: false, instance_reader: true do |serializer|
base.serialized_associations ||= {} serializer.class_attribute :_reflections
base.class_attribute :_reflections, instance_writer: false self._reflections ||= []
base._reflections ||= [] end
extend ActiveSupport::Autoload extend ActiveSupport::Autoload
autoload :Association autoload :Association
@ -29,7 +29,6 @@ module ActiveModel
end end
module ClassMethods module ClassMethods
# Serializers inherit _reflections.
def inherited(base) def inherited(base)
super super
base._reflections = _reflections.dup base._reflections = _reflections.dup
@ -43,7 +42,7 @@ module ActiveModel
# has_many :comments, serializer: CommentSummarySerializer # has_many :comments, serializer: CommentSummarySerializer
# #
def has_many(name, options = {}, &block) def has_many(name, options = {}, &block)
associate(HasManyReflection.new(name, options), block) associate(HasManyReflection.new(name, options, block))
end end
# @param [Symbol] name of the association # @param [Symbol] name of the association
@ -54,7 +53,7 @@ module ActiveModel
# belongs_to :author, serializer: AuthorSerializer # belongs_to :author, serializer: AuthorSerializer
# #
def belongs_to(name, options = {}, &block) def belongs_to(name, options = {}, &block)
associate(BelongsToReflection.new(name, options), block) associate(BelongsToReflection.new(name, options, block))
end end
# @param [Symbol] name of the association # @param [Symbol] name of the association
@ -65,7 +64,7 @@ module ActiveModel
# has_one :author, serializer: AuthorSerializer # has_one :author, serializer: AuthorSerializer
# #
def has_one(name, options = {}, &block) def has_one(name, options = {}, &block)
associate(HasOneReflection.new(name, options), block) associate(HasOneReflection.new(name, options, block))
end end
private private
@ -76,20 +75,9 @@ module ActiveModel
# #
# @api private # @api private
# #
def associate(reflection, block) def associate(reflection)
self._reflections = _reflections.dup self._reflections = _reflections.dup
reflection_name = reflection.name
if block
serialized_associations[reflection_name] = ->(instance) { instance.instance_eval(&block) }
else
serialized_associations[reflection_name] = ->(instance) { instance.object.send(reflection_name) }
end
define_method reflection_name do
serialized_associations[reflection_name].call(self)
end unless method_defined?(reflection_name)
self._reflections << reflection self._reflections << reflection
end end
end end

View File

@ -3,10 +3,12 @@ module ActiveModel
module Attributes module Attributes
class Attribute class Attribute
delegate :call, to: :reader delegate :call, to: :reader
attr_reader :name, :reader attr_reader :name, :reader
def initialize(name) def initialize(name)
@name = name @name = name
@reader = nil @reader = :no_reader
end end
def self.build(name, block) def self.build(name, block)

View File

@ -17,7 +17,28 @@ module ActiveModel
# #
# So you can inspect reflections in your Adapters. # So you can inspect reflections in your Adapters.
# #
Reflection = Struct.new(:name, :options) do Reflection = Struct.new(:name, :options, :block) do
delegate :call, to: :reader
attr_reader :reader
def initialize(*)
super
@reader = self.class.build_reader(name, block)
end
def value(instance)
call(instance)
end
def self.build_reader(name, block)
if block
->(instance) { instance.instance_eval(&block) }
else
->(instance) { instance.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.
# #
@ -40,7 +61,7 @@ module ActiveModel
# @api private # @api private
# #
def build_association(subject, parent_serializer_options) def build_association(subject, parent_serializer_options)
association_value = subject.send(name) 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)