Skip to content

Commit e2de359

Browse files
committed
Refactor JsonApi adapter in order to avoid recomputation of hashes for included resources.
1 parent f3403c3 commit e2de359

File tree

1 file changed

+44
-76
lines changed

1 file changed

+44
-76
lines changed

lib/active_model/serializer/adapter/json_api.rb

Lines changed: 44 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,29 @@ def initialize(serializer, options = {})
5656
def serializable_hash(options = nil)
5757
options ||= {}
5858

59-
hash =
60-
if serializer.respond_to?(:each)
61-
serializable_hash_for_collection(options)
59+
is_collection = serializer.respond_to?(:each)
60+
resources = {}
61+
process_resource(serializer, @include_tree, true, resources)
62+
primary_data, included = resources.each_with_object([[], []]) do |(_, resource), (primary, included)|
63+
if resource[:is_primary]
64+
primary.push(resource.except(:is_primary))
6265
else
63-
serializable_hash_for_single_resource
66+
included.push(resource.except(:is_primary))
6467
end
68+
end
69+
70+
hash = {}
71+
hash[:data] = is_collection ? primary_data : primary_data.first
72+
73+
hash[:included] = included if included.any?
6574

6675
ApiObjects::JsonApi.add!(hash)
6776

77+
if is_collection && serializer.paginated?
78+
hash[:links] ||= {}
79+
hash[:links].update(links_for(serializer, options))
80+
end
81+
6882
if instance_options[:links]
6983
hash[:links] ||= {}
7084
hash[:links].update(instance_options[:links])
@@ -84,38 +98,32 @@ def fragment_cache(cached_hash, non_cached_hash)
8498

8599
private
86100

87-
def serializable_hash_for_collection(options)
88-
hash = { data: [] }
89-
included = []
90-
serializer.each do |s|
91-
result = self.class.new(s, instance_options.merge(fieldset: fieldset)).serializable_hash(options)
92-
hash[:data] << result[:data]
93-
next unless result[:included]
94-
95-
included |= result[:included]
101+
def process_resource(serializer, include_tree, is_primary, hashes)
102+
if serializer.respond_to?(:each)
103+
serializer.each { |s| process_resource(s, include_tree, is_primary, hashes) }
104+
return
96105
end
97-
98-
included.delete_if { |resource| hash[:data].include?(resource) }
99-
hash[:included] = included if included.any?
100-
101-
if serializer.paginated?
102-
hash[:links] ||= {}
103-
hash[:links].update(links_for(serializer, options))
106+
return unless serializer && serializer.object
107+
resource_identifier = resource_identifier_for(serializer)
108+
key = [resource_identifier_type_for(serializer), resource_identifier_id_for(serializer)]
109+
if hashes[key]
110+
hashes[key][:is_primary] ||= is_primary
111+
return
104112
end
105113

106-
hash
107-
end
114+
hash = resource_identifier.merge(is_primary: is_primary)
115+
116+
attributes = attributes_for(serializer)
117+
hash[:attributes] = attributes if attributes.any?
108118

109-
def serializable_hash_for_single_resource
110-
primary_data = primary_data_for(serializer)
111119
relationships = relationships_for(serializer)
112-
primary_data[:relationships] = relationships if relationships.any?
113-
hash = { data: primary_data }
120+
hash[:relationships] = relationships if relationships.any?
114121

115-
included = included_resources(@include_tree, [primary_data])
116-
hash[:included] = included if included.any?
122+
hashes[key] = hash
117123

118-
hash
124+
serializer.associations(include_tree).each do |association|
125+
process_resource(association.serializer, include_tree[association.key], false, hashes)
126+
end
119127
end
120128

121129
def resource_identifier_type_for(serializer)
@@ -142,26 +150,15 @@ def resource_identifier_for(serializer)
142150
{ id: id.to_s, type: type }
143151
end
144152

145-
def resource_object_for(serializer)
146-
cache_check(serializer) do
147-
resource_object = resource_identifier_for(serializer)
148-
requested_fields = fieldset && fieldset.fields_for(resource_object[:type])
149-
attributes = serializer.attributes.except(:id)
150-
attributes.slice!(*requested_fields) if requested_fields
151-
resource_object[:attributes] = attributes if attributes.any?
152-
resource_object
153-
end
154-
end
153+
def attributes_for(serializer)
154+
attributes = serializer.attributes.except(:id)
155+
requested_fields = fieldset && fieldset.fields_for(resource_identifier_type_for(serializer))
156+
attributes.slice!(*requested_fields) if requested_fields
155157

156-
def primary_data_for(serializer)
157-
if serializer.respond_to?(:each)
158-
serializer.map { |s| resource_object_for(s) }
159-
else
160-
resource_object_for(serializer)
161-
end
158+
attributes
162159
end
163160

164-
def relationship_value_for(serializer, options = {})
161+
def linkage_for(serializer, options = {})
165162
if serializer.respond_to?(:each)
166163
serializer.map { |s| resource_identifier_for(s) }
167164
else
@@ -175,36 +172,7 @@ def relationship_value_for(serializer, options = {})
175172

176173
def relationships_for(serializer)
177174
serializer.associations.each_with_object({}) do |association, hash|
178-
hash[association.key] = { data: relationship_value_for(association.serializer, association.options) }
179-
end
180-
end
181-
182-
def included_resources(include_tree, primary_data)
183-
included = []
184-
185-
serializer.associations(include_tree).each do |association|
186-
add_included_resources_for(association.serializer, include_tree[association.key], primary_data, included)
187-
end
188-
189-
included
190-
end
191-
192-
def add_included_resources_for(serializer, include_tree, primary_data, included)
193-
if serializer.respond_to?(:each)
194-
serializer.each { |s| add_included_resources_for(s, include_tree, primary_data, included) }
195-
else
196-
return unless serializer && serializer.object
197-
198-
resource_object = primary_data_for(serializer)
199-
relationships = relationships_for(serializer)
200-
resource_object[:relationships] = relationships if relationships.any?
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
175+
hash[association.key] = { data: linkage_for(association.serializer, association.options) }
208176
end
209177
end
210178

0 commit comments

Comments
 (0)