para 0.9.3.2 → 0.9.3.3

Sign up to get free protection for your applications and to get access to all the features.
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