forest_admin_datasource_active_record 1.34.2 → 1.35.0

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: 144849075d84ddc59403e90f55215ff673e7a499ea2a4e9c7bde79b92bedf560
4
- data.tar.gz: 718e037d8de68a33682f00a4c47579f04d63aa8346c2bf2fe33057697adb58d7
3
+ metadata.gz: 04e8352c5b26a43ce466ea857586a38f235d32b38b252c4c37e51bef43e2f4f4
4
+ data.tar.gz: ffcfe8fd892725000f743ac15323015a35d06238680fb80b2c2467ee45b16afc
5
5
  SHA512:
6
- metadata.gz: 3481314773b0f72bd27df1af19a36bda78698f697b427d463a99d2868399890b37cd5ada815a58713d4b09715928c98a2a8895f3c386e6d1fe1f58bbf88e00ca
7
- data.tar.gz: 83bf7e9db1113e836ec18686f718dff66cc13fee0a485b91e09e44b913d04b9da57c539d1a329fcc6eeb93f5a43666bdd38bd77168ce29fb26f9c374f51c1055
6
+ metadata.gz: c8995e225a4300c5a25737a74715c35ddf991c4fdf8bef82722d25b89836cd2e2a1e42c35aabf905aa3bf538413e6e164e18f12f0effcb6a4211bfd2390c0c09
7
+ data.tar.gz: ad3209330ba9e93b06d98fafc26648b06eda6f80309f5b3373d5ff2c0fe28609430c63d9a3f93d75500edde838bae1fc476d43a2cca6009ac2868c9a86b0f1a6
@@ -100,21 +100,32 @@ module ForestAdminDatasourceActiveRecord
100
100
  source_polymorphic = association.source_reflection&.polymorphic? &&
101
101
  association.options[:source_type].present?
102
102
 
103
- add_field(
104
- association.name.to_s,
105
- ForestAdminDatasourceToolkit::Schema::Relations::ManyToManySchema.new(
106
- foreign_collection: format_model_name(association.klass.name),
107
- origin_key: through_reflection.foreign_key,
108
- origin_key_target: through_reflection.join_foreign_key,
109
- foreign_key: association.join_foreign_key,
110
- foreign_key_target: association.association_primary_key,
111
- through_collection: format_model_name(through_reflection.klass.name),
112
- origin_type_field: is_polymorphic ? through_reflection.type : nil,
113
- origin_type_value: is_polymorphic ? @model.name : nil,
114
- foreign_type_field: source_polymorphic ? association.source_reflection.foreign_type : nil,
115
- foreign_type_value: source_polymorphic ? association.options[:source_type] : nil
103
+ if is_polymorphic || source_polymorphic
104
+ add_field(
105
+ association.name.to_s,
106
+ ForestAdminDatasourceToolkit::Schema::Relations::ManyToManySchema.new(
107
+ foreign_collection: format_model_name(association.klass.name),
108
+ origin_key: through_reflection.foreign_key,
109
+ origin_key_target: through_reflection.join_foreign_key,
110
+ foreign_key: association.join_foreign_key,
111
+ foreign_key_target: association.association_primary_key,
112
+ through_collection: format_model_name(through_reflection.klass.name),
113
+ origin_type_field: is_polymorphic ? through_reflection.type : nil,
114
+ origin_type_value: is_polymorphic ? @model.name : nil,
115
+ foreign_type_field: source_polymorphic ? association.source_reflection.foreign_type : nil,
116
+ foreign_type_value: source_polymorphic ? association.options[:source_type] : nil
117
+ )
116
118
  )
117
- )
119
+ else
120
+ add_field(
121
+ association.name.to_s,
122
+ ForestAdminDatasourceToolkit::Schema::Relations::OneToOneSchema.new(
123
+ foreign_collection: format_model_name(association.klass.name),
124
+ origin_key: association.klass.primary_key,
125
+ origin_key_target: @model.primary_key
126
+ )
127
+ )
128
+ end
118
129
  elsif association.inverse_of&.polymorphic?
119
130
  add_field(
120
131
  association.name.to_s,
@@ -196,6 +196,10 @@ module ForestAdminDatasourceActiveRecord
196
196
  if collection.model.table_name == @collection.model.table_name
197
197
  if one_to_one_relations.include?(relation_schema.type)
198
198
  @select << "#{collection.model.table_name}.#{relation_schema.origin_key_target}"
199
+ # foreign_key is an array for a composite-key belongs_to through hop
200
+ Array(root_through_foreign_key(collection, relation_name)).each do |through_fk|
201
+ @select << "#{collection.model.table_name}.#{through_fk}"
202
+ end
199
203
  elsif many_to_one_relations.include?(relation_schema.type)
200
204
  @select << "#{collection.model.table_name}.#{relation_schema.foreign_key}"
201
205
  end
@@ -275,7 +279,7 @@ module ForestAdminDatasourceActiveRecord
275
279
  target = joinable_target(collection, relation_name, used_tables)
276
280
  return nil if target.nil?
277
281
 
278
- tables = Set[target.model.table_name]
282
+ tables = Set[target.model.table_name] | through_tables(collection, relation_name)
279
283
  sub_projection.relations.each do |nested_name, nested_projection|
280
284
  nested = joinable_tables(target, nested_name, nested_projection, used_tables | tables)
281
285
  return nil if nested.nil?
@@ -288,23 +292,50 @@ module ForestAdminDatasourceActiveRecord
288
292
  # The target collection when this hop is safe to collapse into a JOIN, else nil (-> preload).
289
293
  def joinable_target(collection, relation_name, used_tables)
290
294
  relation_schema = collection.schema[:fields][relation_name]
291
- # belongs_to only: it joins on the target's primary key, so it can't duplicate the parent
292
- # (a has_one child may not be unique)
293
- return unless relation_schema.type == 'ManyToOne' && relation_schema.respond_to?(:foreign_collection)
295
+ return unless relation_schema.respond_to?(:foreign_collection)
294
296
 
295
297
  # a scoped association applies its scope to the JOIN and may inject raw/unqualified SQL or
296
298
  # extra joins (e.g. `belongs_to :x, -> { where('id > ?', 1) }`)
297
299
  reflection = collection.model.reflect_on_association(relation_name.to_sym)
298
300
  return if reflection.nil? || reflection.scope
299
301
 
302
+ case relation_schema.type
303
+ when 'ManyToOne' then nil
304
+ when 'OneToOne' then return unless belongs_to_chain_through?(reflection)
305
+ else return
306
+ end
307
+
300
308
  target = local_ar_collection(collection.datasource, relation_schema.foreign_collection)
301
309
  return if target.nil? || !target.model.default_scopes.empty? # same risk as a scoped association
302
310
  return unless same_database?(collection.model, target.model)
303
311
  return if used_tables.include?(target.model.table_name) # a table joined twice would be aliased by AR
312
+ return if through_tables(collection, relation_name).intersect?(used_tables)
304
313
 
305
314
  target
306
315
  end
307
316
 
317
+ def through_tables(collection, relation_name)
318
+ through = collection.model.reflect_on_association(relation_name.to_sym)&.through_reflection
319
+ return Set[] unless through
320
+
321
+ Set[through.table_name]
322
+ rescue StandardError
323
+ Set[]
324
+ end
325
+
326
+ def belongs_to_chain_through?(reflection)
327
+ return false unless reflection.through_reflection?
328
+
329
+ through = reflection.through_reflection
330
+ source = reflection.source_reflection
331
+
332
+ through && source && through.belongs_to? && source.belongs_to? &&
333
+ through.scope.nil? && source.scope.nil? && through.klass.default_scopes.empty? &&
334
+ same_database?(reflection.active_record, through.klass)
335
+ rescue StandardError
336
+ false
337
+ end
338
+
308
339
  def same_database?(model_a, model_b)
309
340
  # compare the pools, not connection_specification_name (only an owner class name, shared across shards)
310
341
  model_a.connection_pool == model_b.connection_pool
@@ -330,6 +361,15 @@ module ForestAdminDatasourceActiveRecord
330
361
  @query
331
362
  end
332
363
 
364
+ def root_through_foreign_key(collection, relation_name)
365
+ through = collection.model.reflect_on_association(relation_name.to_sym)&.through_reflection
366
+ return unless through&.belongs_to?
367
+
368
+ through.foreign_key
369
+ rescue StandardError
370
+ nil
371
+ end
372
+
333
373
  def resolve_field(original_field)
334
374
  if original_field.include?(':')
335
375
  relation_name, column_name = original_field.split(':')
@@ -1,3 +1,3 @@
1
1
  module ForestAdminDatasourceActiveRecord
2
- VERSION = "1.34.2"
2
+ VERSION = "1.35.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forest_admin_datasource_active_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.34.2
4
+ version: 1.35.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthieu