forest_liana 9.15.1 → 9.15.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: 1c700fa60cbdb6da1e131e2e041d7bec408052bf6ce50304e51f2e6f6525e8d8
4
- data.tar.gz: fdc53e76cf29afabdd6b18ec6a3279bd0e9bec3b467e8f8b16cb6cbb7dda87aa
3
+ metadata.gz: 6c293cc19d5273766a98fcc556bb07bcdd9dea63cd604aeee9352055556b9994
4
+ data.tar.gz: fe841959936a030dc0989da2645ce9db9a74237bf86d7028c2cc4c7de27fece8
5
5
  SHA512:
6
- metadata.gz: fe65c60f75a320f96e826dc7b93e25106d0958c62f1157212496ba57eef33e9be08776ab104200b2eb9d7e772fc1f2cae58f811f2033d57e2969e877cf3dbc64
7
- data.tar.gz: bf3c7eb151213fb126ef896d040369c2a3c65500e8e2ecbd292c06cbb016939fc607e295db9fc719b09e428c52ad7f4462bee211f7f532794be0d4895ef2d50d
6
+ metadata.gz: b4ab70690f0da9891d2e5484ad33548e443bf5d72cf0edbc24caae74fe6214a2e3d9f10489ebdb331b907809a6d19cd2cfdd1631c80ed5c0aff4156a5e666326
7
+ data.tar.gz: bb3272e7f6ae11f3538f15ac0cb280df67015ac940d1734d8cc5d04f9ad1168b1e09ad9073e3b85cfef0c936d7393db70a90973e14a34d0e367584e46f3a4311
@@ -156,10 +156,12 @@ module ForestLiana
156
156
  params_fields_hash.inject({}) do |fields, param_field|
157
157
  relation_name = param_field[0]
158
158
  relation_fields = param_field[1]
159
+ forest_collection = ForestLiana.apimap.find { |collection| collection.name.to_s == model.to_s.gsub('::', '__') }
160
+ smart_relations = forest_collection.fields_smart_belongs_to
159
161
 
160
162
  if relation_name == ForestLiana.name_for(model)
161
163
  fields[relation_name] = relation_fields
162
- else
164
+ elsif model.reflect_on_association(relation_name.to_sym)
163
165
  model_association = model.reflect_on_association(relation_name.to_sym)
164
166
  if model_association
165
167
  model_name = ForestLiana.name_for(model_association.klass)
@@ -173,7 +175,14 @@ module ForestLiana
173
175
  fields[model_name] = relation_fields
174
176
  end
175
177
  end
178
+ else
179
+ smart_relations.each do |smart_relation|
180
+ if smart_relation[:field].to_s == relation_name
181
+ fields[smart_relation[:reference].split('.').first] = relation_fields
182
+ end
183
+ end
176
184
  end
185
+
177
186
  fields
178
187
  end
179
188
  else
@@ -47,6 +47,81 @@ module ForestLiana
47
47
  end
48
48
  end
49
49
 
50
+ # duplicate method from Serializer
51
+ ForestAdmin::JSONAPI::Serializer.singleton_class.send(:define_method, :find_recursive_relationships) do |root_object, root_inclusion_tree, results, options|
52
+ ActiveSupport::Notifications.instrument(
53
+ 'render.jsonapi_serializers.find_recursive_relationships',
54
+ {class_name: root_object.class.name},
55
+ ) do
56
+ root_inclusion_tree.each do |attribute_name, child_inclusion_tree|
57
+ next if attribute_name == :_include
58
+
59
+ serializer = ForestAdmin::JSONAPI::Serializer.find_serializer(root_object, options)
60
+ unformatted_attr_name = serializer.unformat_name(attribute_name).to_sym
61
+ object = nil
62
+ is_collection = false
63
+ is_valid_attr = false
64
+ if serializer.has_one_relationships.has_key?(unformatted_attr_name)
65
+ # only added this condition
66
+ if root_object.class.reflect_on_association(unformatted_attr_name)&.polymorphic?
67
+ options[:context][:unoptimized] = true
68
+ end
69
+
70
+ is_valid_attr = true
71
+ attr_data = serializer.has_one_relationships[unformatted_attr_name]
72
+ object = serializer.has_one_relationship(unformatted_attr_name, attr_data)
73
+ elsif serializer.has_many_relationships.has_key?(unformatted_attr_name)
74
+ is_valid_attr = true
75
+ is_collection = true
76
+ attr_data = serializer.has_many_relationships[unformatted_attr_name]
77
+ object = serializer.has_many_relationship(unformatted_attr_name, attr_data)
78
+ end
79
+
80
+ if !is_valid_attr
81
+ raise ForestAdmin::JSONAPI::Serializer::InvalidIncludeError.new(
82
+ "'#{attribute_name}' is not a valid include.")
83
+ end
84
+
85
+ if attribute_name != serializer.format_name(attribute_name)
86
+ expected_name = serializer.format_name(attribute_name)
87
+
88
+ raise ForestAdmin::JSONAPI::Serializer::InvalidIncludeError.new(
89
+ "'#{attribute_name}' is not a valid include. Did you mean '#{expected_name}' ?"
90
+ )
91
+ end
92
+
93
+ next if object.nil?
94
+
95
+ objects = is_collection ? object : [object]
96
+ if child_inclusion_tree[:_include] == true
97
+ objects.each do |obj|
98
+ obj_serializer = ForestAdmin::JSONAPI::Serializer.find_serializer(obj, options)
99
+ key = [obj_serializer.type, obj_serializer.id]
100
+
101
+ current_child_includes = []
102
+ inclusion_names = child_inclusion_tree.keys.reject { |k| k == :_include }
103
+ inclusion_names.each do |inclusion_name|
104
+ if child_inclusion_tree[inclusion_name][:_include]
105
+ current_child_includes << inclusion_name
106
+ end
107
+ end
108
+
109
+ current_child_includes += results[key] && results[key][:include_linkages] || []
110
+ current_child_includes.uniq!
111
+ results[key] = {object: obj, include_linkages: current_child_includes}
112
+ end
113
+ end
114
+
115
+ if !child_inclusion_tree.empty?
116
+ objects.each do |obj|
117
+ find_recursive_relationships(obj, child_inclusion_tree, results, options)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ nil
123
+ end
124
+
50
125
  def initialize(is_smart_collection = false)
51
126
  @is_smart_collection = is_smart_collection
52
127
  end
@@ -136,28 +211,46 @@ module ForestLiana
136
211
  data = {}
137
212
  self.class.to_one_associations.each do |attribute_name, attr_data|
138
213
  relation = object.class.reflect_on_all_associations.find { |a| a.name == attribute_name }
139
-
140
214
  next if !should_include_attr?(attribute_name, attr_data)
141
215
 
142
- unless relation.nil? || (relation.respond_to?(:polymorphic?) && relation.polymorphic?)
143
- relation_class_name = ForestLiana.name_for(relation.klass).demodulize
144
-
145
- if object.respond_to?(relation.foreign_key.to_sym) &&
146
- @options[:fields][relation_class_name]&.size == 1 &&
147
- @options[:fields][relation_class_name]&.include?(relation.klass.primary_key.to_sym)
148
-
149
- attr_data[:attr_or_block] = proc {
150
- foreign_key_value = object.send(relation.foreign_key.to_sym)
151
- foreign_key_value.nil? ? nil : relation.klass.new(relation.klass.primary_key => foreign_key_value)
152
- }
216
+ if relation && relation.belongs_to? && relation.polymorphic?.nil?
217
+ reflection_primary_key = relation.options[:primary_key]&.to_sym || :id
218
+ klass_primary_key = relation.klass.primary_key.to_sym
219
+
220
+ if reflection_primary_key != klass_primary_key
221
+ data[attribute_name] = attr_data.merge({
222
+ attr_or_block: proc {
223
+ relation.klass.find_by(reflection_primary_key => object.send(relation.foreign_key))
224
+ }
225
+ })
226
+ next
153
227
  end
154
228
  end
155
229
 
156
230
  data[attribute_name] = attr_data
157
231
  end
232
+
158
233
  data
159
234
  end
160
235
 
236
+ def should_include_attr?(attribute_name, attr_data)
237
+ collection = self.type
238
+
239
+ unless @options.dig(:context, :unoptimized)
240
+ return false unless @options[:fields][collection]&.include?(attribute_name.to_sym)
241
+ end
242
+
243
+ # Allow "if: :show_title?" and "unless: :hide_title?" attribute options.
244
+ if_method_name = attr_data[:options][:if]
245
+ unless_method_name = attr_data[:options][:unless]
246
+ formatted_attribute_name = format_name(attribute_name).to_sym
247
+ show_attr = true
248
+ show_attr &&= send(if_method_name) if if_method_name
249
+ show_attr &&= !send(unless_method_name) if unless_method_name
250
+ show_attr &&= @_fields[type.to_s].include?(formatted_attribute_name) if @_fields[type.to_s]
251
+ show_attr
252
+ end
253
+
161
254
  private
162
255
 
163
256
  def intercom_integration?
@@ -74,55 +74,9 @@ module ForestLiana
74
74
  end
75
75
  end
76
76
 
77
- preload_cross_database_associations(records, preload_loads)
78
-
79
77
  records
80
78
  end
81
79
 
82
- def preload_cross_database_associations(records, preload_loads)
83
- preload_loads.each do |association_name|
84
- association = @resource.reflect_on_association(association_name)
85
- next unless separate_database?(@resource, association)
86
-
87
- columns = columns_for_cross_database_association(association_name)
88
- if association.macro == :belongs_to
89
- foreign_key = association.foreign_key
90
- primary_key = association.klass.primary_key
91
-
92
- ids = records.map { |r| r.public_send(foreign_key) }.compact.uniq
93
- next if ids.empty?
94
-
95
- associated = association.klass.where(primary_key => ids)
96
- .select(columns)
97
- .index_by { |record| record.public_send(primary_key) }
98
-
99
- records.each do |record|
100
- record.define_singleton_method(association_name) do
101
- associated[record.send(foreign_key.to_sym)] || nil
102
- end
103
- end
104
- end
105
-
106
- if association.macro == :has_one
107
- foreign_key = association.foreign_key
108
- primary_key = association.active_record_primary_key
109
-
110
- ids = records.map { |r| r.public_send(primary_key) }.compact.uniq
111
- next if ids.empty?
112
-
113
- associated = association.klass.where(foreign_key => ids)
114
- .select(columns)
115
- .index_by { |record| record.public_send(foreign_key.to_sym) }
116
-
117
- records.each do |record|
118
- record.define_singleton_method(association_name) do
119
- associated[record.send(primary_key.to_sym)] || nil
120
- end
121
- end
122
- end
123
- end
124
- end
125
-
126
80
  def columns_for_cross_database_association(association_name)
127
81
  association = @resource.reflect_on_association(association_name)
128
82
 
@@ -1,3 +1,3 @@
1
1
  module ForestLiana
2
- VERSION = "9.15.1"
2
+ VERSION = "9.15.3"
3
3
  end
@@ -124,18 +124,7 @@ describe 'Requesting Tree resources', :type => :request do
124
124
  "included" => [{
125
125
  "type" => "Location",
126
126
  "id" => "1",
127
- "attributes" => include(
128
- "id" => 1,
129
- "created_at" => nil,
130
- "updated_at" => nil,
131
- "coordinates" => nil
132
- ),
133
- "links" => { "self" => "/forest/location/1" },
134
- "relationships" => {
135
- "island" => {
136
- "links" => { "related" => {} }
137
- }
138
- }
127
+ "links" => { "self" => "/forest/location/1" }
139
128
  }]
140
129
  })
141
130
  end
@@ -5,27 +5,6 @@ module ForestLiana
5
5
  let(:island) { Island.create!(name: 'TestIsland') }
6
6
  let(:tree) { Tree.create!(name: 'TestTree', island: island, owner: user) }
7
7
 
8
- it 'simulates has_one relation with only primary key' do
9
- factory = described_class.new
10
- serializer_class = factory.serializer_for(Tree)
11
-
12
- serializer_class.send(:has_one, :island) { }
13
-
14
- instance = serializer_class.new(tree, fields: {
15
- 'Island' => [:id],
16
- 'Tree' => [:island]
17
- })
18
-
19
- relationships = instance.send(:has_one_relationships)
20
- expect(relationships).to have_key(:island)
21
- relation_data = relationships[:island]
22
- expect(relation_data[:attr_or_block]).to be_a(Proc)
23
- model = relation_data[:attr_or_block].call
24
-
25
- expect(model).to be_a(Island)
26
- expect(model.id).to eq(island.id)
27
- end
28
-
29
8
  it 'returns nil if foreign key is nil' do
30
9
  tree_without_island = Tree.create!(name: 'NoIslandTree', island_id: nil, owner: user)
31
10
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forest_liana
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.15.1
4
+ version: 9.15.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sandro Munda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-23 00:00:00.000000000 Z
11
+ date: 2025-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails