Skip to content

Commit 20ddc5e

Browse files
committed
Refactor JsonApi adapter to avoid redundant computations.
1 parent 8981683 commit 20ddc5e

File tree

1 file changed

+42
-55
lines changed

1 file changed

+42
-55
lines changed

lib/active_model/serializer/adapter/json_api.rb

Lines changed: 42 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,13 @@ def initialize(serializer, options = {})
5252
def serializable_hash(options = nil)
5353
options ||= {}
5454

55-
hash =
56-
if serializer.respond_to?(:each)
57-
serializable_hash_for_collection(options)
58-
else
59-
serializable_hash_for_single_resource
60-
end
55+
is_collection = serializer.respond_to?(:each)
56+
serializers = is_collection ? serializer : [serializer]
57+
primary_data, included = resource_objects_for(serializers)
58+
59+
hash = {}
60+
hash[:data] = is_collection ? primary_data : primary_data[0]
61+
hash[:included] = included if included.any?
6162

6263
ApiObjects::JsonApi.add!(hash)
6364

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

70+
if is_collection && serializer.paginated?
71+
hash[:links] ||= {}
72+
hash[:links].update(pagination_links_for(serializer, options))
73+
end
74+
6975
hash
7076
end
7177

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

8187
private
8288

83-
def serializable_hash_for_collection(options)
84-
hash = { data: [] }
85-
included = []
86-
serializer.each do |s|
87-
result = self.class.new(s, instance_options.merge(fieldset: fieldset)).serializable_hash(options)
88-
hash[:data] << result[:data]
89-
next unless result[:included]
89+
def resource_objects_for(serializers)
90+
@primary = []
91+
@included = []
92+
@resource_identifiers = Set.new
93+
serializers.each { |serializer| process_resource(serializer, true) }
94+
serializers.each { |serializer| process_relationships(serializer, @include_tree) }
9095

91-
included |= result[:included]
92-
end
96+
[@primary, @included]
97+
end
9398

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

97-
if serializer.paginated?
98-
hash[:links] ||= {}
99-
hash[:links].update(pagination_links_for(serializer, options))
103+
resource_object = resource_object_for(serializer)
104+
if primary
105+
@primary << resource_object
106+
else
107+
@included << resource_object
100108
end
101109

102-
hash
110+
true
103111
end
104112

105-
def serializable_hash_for_single_resource
106-
primary_data = resource_object_for(serializer)
107-
108-
hash = { data: primary_data }
113+
def process_relationships(serializer, include_tree)
114+
serializer.associations(include_tree).each do |association|
115+
process_relationship(association.serializer, include_tree[association.key])
116+
end
117+
end
109118

110-
included = included_resources(@include_tree, [primary_data])
111-
hash[:included] = included if included.any?
119+
def process_relationship(serializer, include_tree)
120+
if serializer.respond_to?(:each)
121+
serializer.each { |s| process_relationship(s, include_tree) }
122+
return
123+
end
124+
return unless serializer && serializer.object
125+
return unless process_resource(serializer, false)
112126

113-
hash
127+
process_relationships(serializer, include_tree)
114128
end
115129

116130
def resource_identifier_type_for(serializer)
@@ -181,33 +195,6 @@ def relationships_for(serializer)
181195
end
182196
end
183197

184-
def included_resources(include_tree, primary_data)
185-
included = []
186-
187-
serializer.associations(include_tree).each do |association|
188-
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
189-
end
190-
191-
included
192-
end
193-
194-
def add_included_resources_for(serializer, include_tree, primary_data, included)
195-
if serializer.respond_to?(:each)
196-
serializer.each { |s| add_included_resources_for(s, include_tree, primary_data, included) }
197-
else
198-
return unless serializer && serializer.object
199-
200-
resource_object = resource_object_for(serializer)
201-
202-
return if included.include?(resource_object) || primary_data.include?(resource_object)
203-
included.push(resource_object)
204-
205-
serializer.associations(include_tree).each do |association|
206-
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
207-
end
208-
end
209-
end
210-
211198
def links_for(serializer)
212199
serializer._links.each_with_object({}) do |(name, value), hash|
213200
hash[name] = Link.new(serializer, value).as_json

0 commit comments

Comments
 (0)