forest_liana 9.14.1 → 9.14.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: e36ceb8802f17dd57f5c777b1d0c47570ee12259c78048735ad9bd75166e099d
4
- data.tar.gz: 4eb56b56fb1392a0750f0e522676b15e34d83ae725b5218010685ca278b7d10b
3
+ metadata.gz: 82aa2dffa179fa0be6398321388077e3ad04f66583969c4fbb68112983dcd5d6
4
+ data.tar.gz: 9e84f6554455e681cf00ca15cee8bb337c4e9ae22289e11959caa889297a0ee1
5
5
  SHA512:
6
- metadata.gz: 8db001396703db0cd5277f501e93fa43efaf771baa8852e561d5dcf706a862e6bceaa20ababeed0702aae66ebea3961b33257308356a175e47ed2a08193364ff
7
- data.tar.gz: 313aed4b4ac152cd4baba032f875016d1c87162a04e957851f4ed9495eebdf8104aaea238db0ea5442a7213661697d5b8acbcbaa2686f4ab4af83cd095ac0879
6
+ metadata.gz: 0cd4367dcab7b965bccf0b49e289509ce0b97b9763491b67273444c06db7a226d4c1fc019da63167567de45afcb752fb76751b4512d0c5ff0754f217679bac0d
7
+ data.tar.gz: 67eb9531fc902db2a9db22cffb6b53ba2f5ff9635f67ef5668db5b107102fb2ddac46b901fa56a7ac245c0fff3215c064b66cede4c37ea819985ce78539346dd
@@ -47,6 +47,8 @@ module ForestLiana
47
47
  end
48
48
 
49
49
  def separate_database?(resource, association)
50
+ return false if SchemaUtils.polymorphic?(association)
51
+
50
52
  target_model_connection = association.klass.connection
51
53
  target_model_database = target_model_connection.current_database if target_model_connection.respond_to? :current_database
52
54
  resource_connection = resource.connection
@@ -62,9 +62,8 @@ module ForestLiana
62
62
  end
63
63
 
64
64
  def field_names_requested
65
- return nil unless @params[:fields] && @params[:fields][@collection_name]
66
- @params[:fields][@collection_name].split(',')
67
- .map { |name| name.to_sym }
65
+ fields = @params.dig(:fields, @collection_name)
66
+ Array(fields&.split(',')).map(&:to_sym)
68
67
  end
69
68
 
70
69
  def model_association
@@ -32,7 +32,7 @@ module ForestLiana
32
32
  def perform
33
33
  polymorphic_association, preload_loads = analyze_associations(@resource)
34
34
  includes = @includes.uniq - polymorphic_association - preload_loads - @optional_includes
35
- has_smart_fields = @params[:fields][@collection_name].split(',').any? do |field|
35
+ has_smart_fields = Array(@params.dig(:fields, @collection_name)&.split(',')).any? do |field|
36
36
  ForestLiana::SchemaHelper.is_smart_field?(@resource, field)
37
37
  end
38
38
 
@@ -58,7 +58,7 @@ module ForestLiana
58
58
  records = @records.offset(offset).limit(limit).to_a
59
59
  polymorphic_association, preload_loads = analyze_associations(@resource)
60
60
 
61
- if polymorphic_association && Rails::VERSION::MAJOR >= 7
61
+ if polymorphic_association.any? && Rails::VERSION::MAJOR >= 7
62
62
  preloader = ActiveRecord::Associations::Preloader.new(records: records, associations: polymorphic_association)
63
63
  preloader.loaders
64
64
  preloader.branches.each do |branch|
@@ -85,7 +85,6 @@ module ForestLiana
85
85
  next unless separate_database?(@resource, association)
86
86
 
87
87
  columns = columns_for_cross_database_association(association_name)
88
-
89
88
  if association.macro == :belongs_to
90
89
  foreign_key = association.foreign_key
91
90
  primary_key = association.klass.primary_key
@@ -125,34 +124,27 @@ module ForestLiana
125
124
  end
126
125
 
127
126
  def columns_for_cross_database_association(association_name)
128
- return [:id] unless @params[:fields].present?
129
-
130
- fields = @params[:fields][association_name.to_s]
131
- return [:id] unless fields
132
-
133
- base_fields = fields.split(',').map(&:strip).map(&:to_sym) | [:id]
134
-
135
127
  association = @resource.reflect_on_association(association_name)
136
- extra_key = association.foreign_key
137
128
 
138
- # Add the foreign key used for the association to ensure it's available in the preloaded records
139
- # This is necessary for has_one associations, without it calling record.public_send(foreign_key) would raise a "missing attribute" error
140
- base_fields << extra_key if association.macro == :has_one
129
+ # Always include all columns of the associated model to avoid missing attribute errors
130
+ columns = association.klass.column_names.map(&:to_sym)
141
131
 
142
- base_fields.uniq
132
+ # Ensure the foreign key is present for manual binding (especially for has_one)
133
+ columns << association.foreign_key.to_sym if association.macro == :has_one
134
+
135
+ columns.uniq
143
136
  end
144
137
 
145
138
  def compute_includes
146
139
  associations_has_one = ForestLiana::QueryHelper.get_one_associations(@resource)
140
+
147
141
  @optional_includes = []
148
- if @field_names_requested
142
+ if @field_names_requested && @params['searchExtended'].to_i != 1
149
143
  includes = associations_has_one.map do |association|
150
144
  association_name = association.name.to_s
151
145
 
152
- if @params[:fields].key?(association_name) &&
153
- @params[:fields][association_name].split(',').size == 1 &&
154
- @params[:fields][association_name].split(',').include?(association.klass.primary_key)
155
-
146
+ fields = @params[:fields]&.[](association_name)&.split(',')
147
+ if fields&.size == 1 && fields.include?(association.klass.primary_key)
156
148
  @field_names_requested << association.foreign_key
157
149
  @optional_includes << association.name
158
150
  end
@@ -175,7 +167,10 @@ module ForestLiana
175
167
 
176
168
  @includes = (includes & @field_names_requested).concat(includes_for_smart_search)
177
169
  else
178
- @includes = associations_has_one.map(&:name)
170
+ @includes = associations_has_one
171
+ # Avoid eager loading has_one associations pointing to a different database as ORM can't join cross databases
172
+ .reject { |association| separate_database?(@resource, association) }
173
+ .map(&:name)
179
174
  end
180
175
  end
181
176
 
@@ -194,7 +189,7 @@ module ForestLiana
194
189
  end
195
190
 
196
191
  def field_names_requested
197
- return nil unless @params[:fields] && @params[:fields][@collection_name]
192
+ return [] unless @params.dig(:fields, @collection_name)
198
193
 
199
194
  associations_for_query = extract_associations_from_filter
200
195
  associations_for_query << @params[:sort].split('.').first.to_sym if @params[:sort]&.include?('.')
@@ -335,11 +330,12 @@ module ForestLiana
335
330
  select << "#{@resource.table_name}.#{association.foreign_key}"
336
331
  end
337
332
 
338
- if @params[:fields].key?(path)
333
+ fields = @params[:fields]&.[](path)&.split(',')
334
+ if fields
339
335
  association = get_one_association(path)
340
336
  table_name = association.table_name
341
337
 
342
- @params[:fields][path].split(',').each do |association_path|
338
+ fields.each do |association_path|
343
339
  if ForestLiana::SchemaHelper.is_smart_field?(association.klass, association_path)
344
340
  association.klass.attribute_names.each { |attribute| select << "#{table_name}.#{attribute}" }
345
341
  else
@@ -278,7 +278,13 @@ module ForestLiana
278
278
  field[:field] = association.name
279
279
  field[:inverse_of] = inverse_of(association)
280
280
  field[:relationship] = get_relationship_type(association)
281
- # NOTICE: Create the fields of hasOne, HasMany, … relationships.
281
+
282
+ ForestLiana::SchemaUtils.disable_filter_and_sort_if_cross_db!(
283
+ field,
284
+ association.name.to_s,
285
+ ForestLiana.name_for(@model)
286
+ )
287
+ # NOTICE: Create the fields of hasOne, HasMany, … relationships.
282
288
  else
283
289
  collection.fields << get_schema_for_association(association)
284
290
  end
@@ -346,7 +352,7 @@ module ForestLiana
346
352
  end
347
353
 
348
354
  def get_schema_for_association(association)
349
- {
355
+ opts ={
350
356
  field: association.name.to_s,
351
357
  type: get_type_for_association(association),
352
358
  relationship: get_relationship_type(association),
@@ -363,6 +369,14 @@ module ForestLiana
363
369
  widget: nil,
364
370
  validations: []
365
371
  }
372
+
373
+ ForestLiana::SchemaUtils.disable_filter_and_sort_if_cross_db!(
374
+ opts,
375
+ association.name.to_s,
376
+ ForestLiana.name_for(@model)
377
+ )
378
+
379
+ opts
366
380
  end
367
381
 
368
382
  def get_relationship_type(association)
@@ -126,5 +126,27 @@ module ForestLiana
126
126
  def self.is_active_type? model
127
127
  Object.const_defined?('ActiveType::Object') && model < ActiveType::Object
128
128
  end
129
+
130
+ def self.disable_filter_and_sort_if_cross_db!(opts, name, collection_name)
131
+ return unless opts[:reference]
132
+
133
+ assoc_name = opts[:reference].split('.').first&.underscore&.to_sym || name
134
+ model = find_model_from_collection_name(collection_name)
135
+ return unless model
136
+
137
+ association = model.reflect_on_association(assoc_name)
138
+ return unless association
139
+ return if polymorphic?(association)
140
+
141
+ model_db = model.connection_db_config.database
142
+ assoc_db = association.klass.connection_db_config.database
143
+
144
+ if model_db != assoc_db
145
+ opts[:is_filterable] = false
146
+ opts[:is_sortable] = false
147
+ end
148
+ rescue => e
149
+ FOREST_LOGGER.warn("Could not evaluate cross-db association for #{name}: #{e.message}")
150
+ end
129
151
  end
130
152
  end
@@ -1,3 +1,3 @@
1
1
  module ForestLiana
2
- VERSION = "9.14.1"
2
+ VERSION = "9.14.3"
3
3
  end
@@ -572,6 +572,26 @@ module ForestLiana
572
572
  expect(records.second.name).to eq 'Treasure'
573
573
  end
574
574
  end
575
+
576
+ describe '#perform' do
577
+ let(:resource) { User }
578
+ let(:fields) { nil }
579
+ let(:sort) { nil }
580
+ let(:filters) { nil }
581
+
582
+ it 'does not raise an error when params[:fields] is nil' do
583
+ expect {
584
+ getter.perform
585
+ }.not_to raise_error
586
+
587
+ records = getter.records
588
+ count = getter.count
589
+
590
+ expect(records).to all(be_a(User))
591
+ expect(count).to eq User.count
592
+ end
593
+ end
594
+
575
595
  end
576
596
  end
577
597
  end
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.14.1
4
+ version: 9.14.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-05-28 00:00:00.000000000 Z
11
+ date: 2025-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails