Merge pull request #1447 from beauby/jsonapi-refactor-2016

[PERF] Refactor JsonApi adapter to avoid redundant computations.
This commit is contained in:
Benjamin Fleischer 2016-02-02 10:10:47 -06:00
commit 72c2c9f0d7

View File

@ -52,12 +52,13 @@ module ActiveModel
def serializable_hash(options = nil) def serializable_hash(options = nil)
options ||= {} options ||= {}
hash = is_collection = serializer.respond_to?(:each)
if serializer.respond_to?(:each) serializers = is_collection ? serializer : [serializer]
serializable_hash_for_collection(options) primary_data, included = resource_objects_for(serializers)
else
serializable_hash_for_single_resource hash = {}
end hash[:data] = is_collection ? primary_data : primary_data[0]
hash[:included] = included if included.any?
ApiObjects::JsonApi.add!(hash) ApiObjects::JsonApi.add!(hash)
@ -66,6 +67,11 @@ module ActiveModel
hash[:links].update(instance_options[:links]) hash[:links].update(instance_options[:links])
end end
if is_collection && serializer.paginated?
hash[:links] ||= {}
hash[:links].update(pagination_links_for(serializer, options))
end
hash hash
end end
@ -80,37 +86,45 @@ module ActiveModel
private private
def serializable_hash_for_collection(options) def resource_objects_for(serializers)
hash = { data: [] } @primary = []
included = [] @included = []
serializer.each do |s| @resource_identifiers = Set.new
result = self.class.new(s, instance_options.merge(fieldset: fieldset)).serializable_hash(options) serializers.each { |serializer| process_resource(serializer, true) }
hash[:data] << result[:data] serializers.each { |serializer| process_relationships(serializer, @include_tree) }
next unless result[:included]
included |= result[:included] [@primary, @included]
end end
included.delete_if { |resource| hash[:data].include?(resource) } def process_resource(serializer, primary)
hash[:included] = included if included.any? resource_identifier = resource_identifier_for(serializer)
return false unless @resource_identifiers.add?(resource_identifier)
if serializer.paginated? resource_object = resource_object_for(serializer)
hash[:links] ||= {} if primary
hash[:links].update(pagination_links_for(serializer, options)) @primary << resource_object
else
@included << resource_object
end end
hash true
end end
def serializable_hash_for_single_resource def process_relationships(serializer, include_tree)
primary_data = resource_object_for(serializer) serializer.associations(include_tree).each do |association|
process_relationship(association.serializer, include_tree[association.key])
end
end
hash = { data: primary_data } def process_relationship(serializer, include_tree)
if serializer.respond_to?(:each)
serializer.each { |s| process_relationship(s, include_tree) }
return
end
return unless serializer && serializer.object
return unless process_resource(serializer, false)
included = included_resources(@include_tree, [primary_data]) process_relationships(serializer, include_tree)
hash[:included] = included if included.any?
hash
end end
def resource_identifier_type_for(serializer) def resource_identifier_type_for(serializer)
@ -181,33 +195,6 @@ module ActiveModel
end end
end end
def included_resources(include_tree, primary_data)
included = []
serializer.associations(include_tree).each do |association|
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
end
included
end
def add_included_resources_for(serializer, include_tree, primary_data, included)
if serializer.respond_to?(:each)
serializer.each { |s| add_included_resources_for(s, include_tree, primary_data, included) }
else
return unless serializer && serializer.object
resource_object = resource_object_for(serializer)
return if included.include?(resource_object) || primary_data.include?(resource_object)
included.push(resource_object)
serializer.associations(include_tree).each do |association|
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
end
end
end
def links_for(serializer) def links_for(serializer)
serializer._links.each_with_object({}) do |(name, value), hash| serializer._links.each_with_object({}) do |(name, value), hash|
hash[name] = Link.new(serializer, value).as_json hash[name] = Link.new(serializer, value).as_json