para 0.9.3.2 → 0.9.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6deb45940599a543b4257463561e645256df1db47b9cef62f1d287b97e841abd
4
- data.tar.gz: a37ab4aca7ab0c23baf41a65083c84bdbdb6d6fe12437621b08f9a4f49baf22d
3
+ metadata.gz: 8f742ef6484e0ccd595c1a79e219f4cb2a03c72a7fbf9f584990c638412f19e0
4
+ data.tar.gz: 8c231925d6c2ecc4d0eb7208c5c4b530410d1a30d8a1276d849f31d3e52f5f76
5
5
  SHA512:
6
- metadata.gz: 229be291a706963be79ef902fb99fdedad27092a62f8c855d912b57c21d10f94ed8e9ffd254a9bb1f29ae465ab6ec1cbe3753af53096778ce6131bdca83ef3b3
7
- data.tar.gz: 10e5675bb29448fd949112632bda77b2dc897d7ca3b2d2dd8dfbac4ef47a19d8ddf629aa6ef40cea98dbfed05bfc716afe8fe06b1c57b950e2af8b541f859672
6
+ metadata.gz: e42df31fcb4d4d490f5b5127939494fbc9e07855ee1123fd720f089c2e36737b4e1f706b347d36699270ed00eeeb4a228ca81050bdc8ae35b84bbde8a7dfd7b6
7
+ data.tar.gz: 707696ad2b1f6a2c43f6944f6543ca5dd8dd5a17c3c91942d0d3c38b43e5d2c652d1b76a35409c2de0019c11658f81a6922931d3e4126a6490a77ed189bd8e8a
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Para
2
4
  module Cloneable
3
5
  # This object acts as a service to compile a nested cloneable options hash to be
@@ -49,7 +51,7 @@ module Para
49
51
  # }
50
52
  #
51
53
  class IncludeTreeBuilder
52
- attr_reader :resource, :cloneable_options
54
+ attr_reader :resource, :cloneable_options
53
55
 
54
56
  def initialize(resource)
55
57
  @resource = resource
@@ -73,26 +75,56 @@ module Para
73
75
  # if it exist, which include the attributes that shouldn't be duplicated when
74
76
  # the resource is cloned.
75
77
  #
76
- def build_cloneable_options_tree(resource)
78
+ def build_cloneable_options_tree(resource, path = [])
77
79
  cloneable_options = resource.cloneable_options
78
- options = {}
79
80
 
80
81
  # Iterate over the resource's cloneable options' :include array and recursively
81
82
  # add nested included resources to its own included resources.
82
83
  options = cloneable_options[:include].each_with_object({}) do |reflection_name, hash|
84
+ # This avoids cyclic dependencies issues by stopping nested association
85
+ # inclusions before the cycle starts.
86
+ #
87
+ # For example, if a post includes its author, and the author includes its posts,
88
+ # this would make the system fail with a stack level too deep error. Here this
89
+ # guard allows the inclusion to stop at :
90
+ #
91
+ # { posts: { author: { posts: { author: {}}}}}
92
+ #
93
+ # Which ensures that, using the dictionary strategy of deep_cloneable, all
94
+ # posts' authors' posts will have their author mapped to an already cloned
95
+ # author when it comes to cloning the "author" 4th level of the include tree.
96
+ #
97
+ # This is not the most optimized solution, but works well enough as if the
98
+ # author's posts match previously cloned posts, they won't be cloned as they'll
99
+ # exist in the cloned resources dictionary.
100
+ next if path.length >= 4 &&
101
+ path[-4] == path[-2] &&
102
+ path[-2] == reflection_name &&
103
+ path[-3] == path[-1]
104
+
83
105
  hash[reflection_name] = {}
84
106
 
85
- if (reflection = resource.class.reflections[reflection_name.to_s])
86
- reflection_options = hash[reflection_name]
87
- association_target = resource.send(reflection_name)
107
+ unless (reflection = resource.class.reflections[reflection_name.to_s])
108
+ next
109
+ end
88
110
 
89
- if reflection.collection?
90
- association_target.each do |nested_resource|
91
- add_reflection_options(reflection_options, nested_resource)
92
- end
93
- else
94
- add_reflection_options(reflection_options, association_target)
111
+ reflection_options = hash[reflection_name]
112
+ association_target = resource.send(reflection_name)
113
+
114
+ if reflection.collection?
115
+ association_target.each do |nested_resource|
116
+ add_reflection_options(
117
+ reflection_options,
118
+ nested_resource,
119
+ [*path, reflection_name]
120
+ )
95
121
  end
122
+ else
123
+ add_reflection_options(
124
+ reflection_options,
125
+ association_target,
126
+ [*path, reflection_name]
127
+ )
96
128
  end
97
129
  end
98
130
 
@@ -104,11 +136,11 @@ module Para
104
136
  options
105
137
  end
106
138
 
107
- def add_reflection_options(reflection_options, nested_resource)
139
+ def add_reflection_options(reflection_options, nested_resource, path)
108
140
  options = nested_resource.class.try(:cloneable_options)
109
141
  return reflection_options unless options
110
142
 
111
- target_options = build_cloneable_options_tree(nested_resource)
143
+ target_options = build_cloneable_options_tree(nested_resource, path)
112
144
  reflection_options.deep_merge!(target_options)
113
145
  end
114
146
 
data/lib/para/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Para
4
- VERSION = '0.9.3.2'
4
+ VERSION = '0.9.3.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: para
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3.2
4
+ version: 0.9.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valentin Ballestrino