Skip to content

Commit 6ea87f5

Browse files
authored
Render in two passes. (#3)
1 parent 6048c0f commit 6ea87f5

File tree

1 file changed

+51
-31
lines changed

1 file changed

+51
-31
lines changed

lib/jsonapi/renderer/resources_processor.rb

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,58 +7,78 @@ def initialize(resources, include, fields)
77
@resources = resources
88
@include = include
99
@fields = fields
10-
@primary = []
11-
@included = []
12-
@hashes = {}
13-
@queue = []
14-
@processed = Set.new # NOTE(beauby): Set of [type, id, prefix].
1510
end
1611

1712
def process
18-
@resources.each do |res|
19-
process_resource(res, '', @include, true)
20-
@processed.add([res.jsonapi_type, res.jsonapi_id, ''])
21-
end
22-
until @queue.empty?
23-
res, prefix, include_dir = @queue.pop
24-
process_resource(res, prefix, include_dir, false)
25-
end
13+
traverse_resources
14+
process_resources
2615

2716
[@primary, @included]
2817
end
2918

3019
private
3120

32-
def merge_resources!(a, b)
33-
b[:relationships].each do |name, rel|
34-
a[:relationships][name][:data] ||= rel[:data] if rel.key?(:data)
35-
if rel.key?(:links)
36-
(a[:relationships][name][:links] ||= {}).merge!(rel[:links])
37-
end
21+
def traverse_resources
22+
@traversed = Set.new # [type, id, prefix]
23+
@include_rels = {} # [type, id => Set]
24+
@queue = []
25+
@primary = []
26+
@included = []
27+
28+
initialize_queue
29+
traverse_queue
30+
end
31+
32+
def initialize_queue
33+
@resources.each do |res|
34+
@traversed.add([res.jsonapi_type, res.jsonapi_id, ''])
35+
traverse_resource(res, @include.keys, true)
36+
enqueue_related_resources(res, '', @include)
3837
end
3938
end
4039

41-
def process_resource(res, prefix, include_dir, is_primary)
40+
def traverse_queue
41+
until @queue.empty?
42+
res, prefix, include_dir = @queue.pop
43+
traverse_resource(res, include_dir.keys, false)
44+
enqueue_related_resources(res, prefix, include_dir)
45+
end
46+
end
47+
48+
def traverse_resource(res, include_keys, primary)
4249
ri = [res.jsonapi_type, res.jsonapi_id]
43-
hash = res.as_jsonapi(fields: @fields[res.jsonapi_type.to_sym],
44-
include: include_dir.keys)
45-
if @hashes.key?(ri)
46-
merge_resources!(@hashes[ri], hash)
50+
if @include_rels.include?(ri)
51+
@include_rels[ri].merge!(include_keys)
4752
else
48-
(is_primary ? @primary : @included) << (@hashes[ri] = hash)
53+
@include_rels[ri] = Set.new(include_keys)
54+
(primary ? @primary : @included) << res
4955
end
50-
process_relationships(res, prefix, include_dir)
5156
end
5257

53-
def process_relationships(res, prefix, include_dir)
58+
def enqueue_related_resources(res, prefix, include_dir)
5459
res.jsonapi_related(include_dir.keys).each do |key, data|
5560
data.each do |child_res|
5661
next if child_res.nil?
5762
child_prefix = "#{prefix}.#{key}"
58-
next unless @processed.add?([child_res.jsonapi_type,
59-
child_res.jsonapi_id,
60-
child_prefix])
61-
@queue << [child_res, child_prefix, include_dir[key]]
63+
enqueue_resource(child_res, child_prefix, include_dir[key])
64+
end
65+
end
66+
end
67+
68+
def enqueue_resource(res, prefix, include_dir)
69+
return unless @traversed.add?([res.jsonapi_type,
70+
res.jsonapi_id,
71+
prefix])
72+
@queue << [res, prefix, include_dir]
73+
end
74+
75+
def process_resources
76+
[@primary, @included].each do |resources|
77+
resources.map! do |res|
78+
ri = [res.jsonapi_type, res.jsonapi_id]
79+
include_dir = @include_rels[ri]
80+
fields = @fields[res.jsonapi_type.to_sym]
81+
res.as_jsonapi(include: include_dir, fields: fields)
6282
end
6383
end
6484
end

0 commit comments

Comments
 (0)