From 9016c766036933326e37526e5bb2431a02265824 Mon Sep 17 00:00:00 2001 From: Andrei Mochalov Date: Thu, 31 Aug 2023 14:54:11 +0300 Subject: [PATCH 1/3] Group all duplicate included resources as arrays --- lib/jsonapi/renderer/resources_processor.rb | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/jsonapi/renderer/resources_processor.rb b/lib/jsonapi/renderer/resources_processor.rb index dfd5264..dbf0121 100644 --- a/lib/jsonapi/renderer/resources_processor.rb +++ b/lib/jsonapi/renderer/resources_processor.rb @@ -50,12 +50,38 @@ def traverse_resource(res, include_keys, primary) if @include_rels.include?(ri) @include_rels[ri].merge!(keys_hash) + + include_duplicate_resource(res) unless primary else @include_rels[ri] = keys_hash (primary ? @primary : @included) << res end end + def include_duplicate_resource(res) + duplicate_index = @included.find_index do |included| + if included.is_a?(Array) + unless included.empty? + included.first.jsonapi_type == res.jsonapi_type && included.first.jsonapi_id == res.jsonapi_id + end + else + included.jsonapi_type == res.jsonapi_type && included.jsonapi_id == res.jsonapi_id + end + end + + if duplicate_index + duplicate = @included.delete_at(duplicate_index) + + if duplicate.is_a?(Array) + duplicate << res + else + duplicate = [duplicate, res] + end + + @included << duplicate + end + end + def enqueue_related_resources(res, prefix, include_dir) res.jsonapi_related(include_dir.keys).each do |key, data| child_prefix = "#{prefix}.#{key}".freeze From 7baeec5609ad2f2a030005ce1adee1f8a82f1628 Mon Sep 17 00:00:00 2001 From: Andrei Mochalov Date: Thu, 31 Aug 2023 14:56:31 +0300 Subject: [PATCH 2/3] Render duplicate included resource groups properly --- .../renderer/simple_resources_processor.rb | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/lib/jsonapi/renderer/simple_resources_processor.rb b/lib/jsonapi/renderer/simple_resources_processor.rb index 1249e6d..13b3b27 100644 --- a/lib/jsonapi/renderer/simple_resources_processor.rb +++ b/lib/jsonapi/renderer/simple_resources_processor.rb @@ -7,13 +7,52 @@ class SimpleResourcesProcessor < ResourcesProcessor def process_resources [@primary, @included].each do |resources| resources.map! do |res| - ri = [res.jsonapi_type, res.jsonapi_id] - include_dir = @include_rels[ri].keys - fields = @fields[res.jsonapi_type.to_sym] - res.as_jsonapi(include: include_dir, fields: fields) + # Duplicates array + if res.is_a?(Array) + process_duplicates(res) + # Regular resource case + else + process_resource(res) + end end end end + + def process_resource(resource) + ri = [resource.jsonapi_type, resource.jsonapi_id] + include_dir = @include_rels[ri].keys + fields = @fields[resource.jsonapi_type.to_sym] + resource.as_jsonapi(include: include_dir, fields: fields) + end + + def process_duplicates(duplicates) + return unless duplicates.is_a?(Array) && duplicates.any? + + duplicates.inject({}) do |result, duplicate| + if result.empty? + result = process_resource(duplicate) if result.empty? + else + duplicate_result = process_resource(duplicate) + result = deep_merge_duplicate_hashes(result, duplicate_result) + end + end + end + + def deep_merge_duplicate_hashes(hash1, hash2) + merger = proc do |_, v1, v2| + if v1.is_a?(Hash) && v2.is_a?(Hash) + v1.merge(v2, &merger) + elsif v1.is_a?(Array) && v2.is_a?(Array) + v1 | v2 + elsif [:undefined, nil, :nil].include?(v2) + v1 + else + v2 + end + end + + hash1.merge(hash2, &merger) + end end end end From 51896163a105bbc0bc0fa7a6800abd376946493d Mon Sep 17 00:00:00 2001 From: Andrei Mochalov Date: Thu, 31 Aug 2023 18:07:47 +0300 Subject: [PATCH 3/3] Reduce code complexity --- lib/jsonapi/renderer/resources_processor.rb | 30 ++++++++++++--------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/jsonapi/renderer/resources_processor.rb b/lib/jsonapi/renderer/resources_processor.rb index dbf0121..e164018 100644 --- a/lib/jsonapi/renderer/resources_processor.rb +++ b/lib/jsonapi/renderer/resources_processor.rb @@ -59,7 +59,23 @@ def traverse_resource(res, include_keys, primary) end def include_duplicate_resource(res) - duplicate_index = @included.find_index do |included| + duplicate_index = find_included_duplicate_resource(res) + + return unless duplicate_index + + duplicate = @included.delete_at(duplicate_index) + + if duplicate.is_a?(Array) + duplicate << res + else + duplicate = [duplicate, res] + end + + @included << duplicate + end + + def find_included_duplicate_resource(res) + @included.find_index do |included| if included.is_a?(Array) unless included.empty? included.first.jsonapi_type == res.jsonapi_type && included.first.jsonapi_id == res.jsonapi_id @@ -68,18 +84,6 @@ def include_duplicate_resource(res) included.jsonapi_type == res.jsonapi_type && included.jsonapi_id == res.jsonapi_id end end - - if duplicate_index - duplicate = @included.delete_at(duplicate_index) - - if duplicate.is_a?(Array) - duplicate << res - else - duplicate = [duplicate, res] - end - - @included << duplicate - end end def enqueue_related_resources(res, prefix, include_dir)