Skip to content

Commit

Permalink
Refactor JsonApi adapter to avoid redundant computations.
Browse files Browse the repository at this point in the history
  • Loading branch information
beauby committed Jan 19, 2016
1 parent 8981683 commit 6c6c9d8
Showing 1 changed file with 41 additions and 56 deletions.
97 changes: 41 additions & 56 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,43 @@ 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
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 +193,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

0 comments on commit 6c6c9d8

Please sign in to comment.