Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor JsonApi adapter to avoid redundant computations. #1447

Merged
merged 1 commit into from
Feb 2, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 42 additions & 55 deletions lib/active_model/serializer/adapter/json_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,13 @@ def initialize(serializer, options = {})
def serializable_hash(options = nil)
options ||= {}

hash =
if serializer.respond_to?(:each)
serializable_hash_for_collection(options)
else
serializable_hash_for_single_resource
end
is_collection = serializer.respond_to?(:each)
serializers = is_collection ? serializer : [serializer]
primary_data, included = resource_objects_for(serializers)

hash = {}
hash[:data] = is_collection ? primary_data : primary_data[0]
hash[:included] = included if included.any?

ApiObjects::JsonApi.add!(hash)

Expand All @@ -66,6 +67,11 @@ def serializable_hash(options = nil)
hash[:links].update(instance_options[:links])
end

if is_collection && serializer.paginated?
hash[:links] ||= {}
hash[:links].update(pagination_links_for(serializer, options))
end

hash
end

Expand All @@ -80,37 +86,45 @@ def fragment_cache(cached_hash, non_cached_hash)

private

def serializable_hash_for_collection(options)
hash = { data: [] }
included = []
serializer.each do |s|
result = self.class.new(s, instance_options.merge(fieldset: fieldset)).serializable_hash(options)
hash[:data] << result[:data]
next unless result[:included]
def resource_objects_for(serializers)
@primary = []
@included = []
@resource_identifiers = Set.new
serializers.each { |serializer| process_resource(serializer, true) }
serializers.each { |serializer| process_relationships(serializer, @include_tree) }

included |= result[:included]
end
[@primary, @included]
end

included.delete_if { |resource| hash[:data].include?(resource) }
hash[:included] = included if included.any?
def process_resource(serializer, primary)
resource_identifier = resource_identifier_for(serializer)
return false unless @resource_identifiers.add?(resource_identifier)

if serializer.paginated?
hash[:links] ||= {}
hash[:links].update(pagination_links_for(serializer, options))
resource_object = resource_object_for(serializer)
if primary
@primary << resource_object
else
@included << resource_object
end

hash
true
end

def serializable_hash_for_single_resource
primary_data = resource_object_for(serializer)

hash = { data: primary_data }
def process_relationships(serializer, include_tree)
serializer.associations(include_tree).each do |association|
process_relationship(association.serializer, include_tree[association.key])
end
end

included = included_resources(@include_tree, [primary_data])
hash[:included] = included if included.any?
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)

hash
process_relationships(serializer, include_tree)
end

def resource_identifier_type_for(serializer)
Expand Down Expand Up @@ -181,33 +195,6 @@ def relationships_for(serializer)
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)
serializer._links.each_with_object({}) do |(name, value), hash|
hash[name] = Link.new(serializer, value).as_json
Expand Down