forest_admin_datasource_active_record 1.35.0 → 1.35.1
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: df0d3da32f3e5533efcd50c768ed24a103f7b442e956b48c7a359c07b3d3c721
|
|
4
|
+
data.tar.gz: 1af868e9ed17dc80de89648eb262c9fe960a2e35af5aa2515d7d802bd129557f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 415b589ca9918863682926543313e9c2917baf7736eb4333e876c9aafa9873580ddc6b4c15e9e9c64e7fdefbcea2a4617d9b9212479652d75d1855a277b82615
|
|
7
|
+
data.tar.gz: 7acfbd16b68262aca27a07e52cf2c76d1145f6e0fffc53d923a11a295475c171014d81c615469f6f738f79752475c7475a1fdc5cd36d16595b9beeb92ad58848
|
|
@@ -13,6 +13,7 @@ module ForestAdminDatasourceActiveRecord
|
|
|
13
13
|
# root keeps all its selected columns (attributes + FKs); a related record is restricted to
|
|
14
14
|
# its projected columns, matching the JOINed hydration
|
|
15
15
|
hash = path.empty? || projection.nil? ? base_attributes(object) : projected_columns(object, projection)
|
|
16
|
+
hash = normalize_polymorphic_types(object.class, hash)
|
|
16
17
|
|
|
17
18
|
serialize_associations(object, projection, hash, path) if projection
|
|
18
19
|
|
|
@@ -65,6 +66,7 @@ module ForestAdminDatasourceActiveRecord
|
|
|
65
66
|
|
|
66
67
|
hash = {}
|
|
67
68
|
projection.columns.each { |column| hash[column] = object[meta[:columns][column]] }
|
|
69
|
+
hash = normalize_polymorphic_types(target_model(relation_path), hash)
|
|
68
70
|
projection.relations.each_key do |nested_name|
|
|
69
71
|
hash[nested_name] = hash_joined_relation(projection.relations[nested_name], relation_path + [nested_name])
|
|
70
72
|
end
|
|
@@ -72,8 +74,43 @@ module ForestAdminDatasourceActiveRecord
|
|
|
72
74
|
hash
|
|
73
75
|
end
|
|
74
76
|
|
|
77
|
+
def normalize_polymorphic_types(model_class, hash)
|
|
78
|
+
return hash if model_class.nil?
|
|
79
|
+
|
|
80
|
+
polymorphic_belongs_to(model_class).each do |association|
|
|
81
|
+
stored = hash[association.foreign_type]
|
|
82
|
+
next if stored.nil?
|
|
83
|
+
|
|
84
|
+
hash = hash.merge(association.foreign_type => model_class.polymorphic_class_for(stored).name)
|
|
85
|
+
rescue NameError => e
|
|
86
|
+
warn_unable(association.name, model_class, e)
|
|
87
|
+
end
|
|
88
|
+
hash
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Target model of a JOINed relation path (only belongs_to / has_one :through are ever JOINed).
|
|
92
|
+
def target_model(relation_path)
|
|
93
|
+
relation_path.reduce(object.class) do |model, name|
|
|
94
|
+
model&.reflect_on_association(name.to_sym)&.klass
|
|
95
|
+
end
|
|
96
|
+
rescue NameError
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
99
|
+
|
|
75
100
|
private
|
|
76
101
|
|
|
102
|
+
def polymorphic_belongs_to(model_class)
|
|
103
|
+
(@polymorphic_belongs_to ||= {})[model_class] ||=
|
|
104
|
+
model_class.reflect_on_all_associations(:belongs_to).select(&:polymorphic?)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def warn_unable(name, model_class, error)
|
|
108
|
+
ActiveSupport::Logger.new($stdout).warn(
|
|
109
|
+
"[ForestAdmin] Unable to normalize polymorphic type of '#{name}' " \
|
|
110
|
+
"in model '#{model_class.name}': #{error.message}. Keeping the stored value."
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
|
|
77
114
|
def joined_relation?(relation_path)
|
|
78
115
|
!joined_relations.nil? && joined_relations.key?(relation_path.join('.'))
|
|
79
116
|
end
|
|
@@ -229,12 +229,13 @@ module ForestAdminDatasourceActiveRecord
|
|
|
229
229
|
def split_relations
|
|
230
230
|
join_tree = {}
|
|
231
231
|
preload_tree = {}
|
|
232
|
-
|
|
232
|
+
used_joins = { @collection.model.table_name => :root }
|
|
233
|
+
@filter_joined_tables.each { |table| used_joins[table] ||= :filter } # never reuse a filter/sort join
|
|
233
234
|
|
|
234
235
|
@projection.relations.each do |relation_name, sub_projection|
|
|
235
|
-
|
|
236
|
-
if
|
|
237
|
-
|
|
236
|
+
joins = joinable_joins(@collection, relation_name, sub_projection, used_joins)
|
|
237
|
+
if joins
|
|
238
|
+
used_joins.merge!(joins)
|
|
238
239
|
join_tree[relation_name.to_sym] = format_relation_projection(sub_projection)
|
|
239
240
|
collect_joined_selects(@collection, relation_name, sub_projection, [relation_name])
|
|
240
241
|
else
|
|
@@ -275,22 +276,28 @@ module ForestAdminDatasourceActiveRecord
|
|
|
275
276
|
end
|
|
276
277
|
|
|
277
278
|
# Set of tables the subtree adds via JOIN, or nil if any relation in it can't be safely joined.
|
|
278
|
-
def
|
|
279
|
-
target = joinable_target(collection, relation_name
|
|
279
|
+
def joinable_joins(collection, relation_name, sub_projection, used_joins)
|
|
280
|
+
target = joinable_target(collection, relation_name)
|
|
280
281
|
return nil if target.nil?
|
|
281
282
|
|
|
282
|
-
|
|
283
|
+
joins = join_signatures(collection, relation_name)
|
|
284
|
+
return nil if joins.nil? || conflicting?(joins, used_joins)
|
|
285
|
+
|
|
283
286
|
sub_projection.relations.each do |nested_name, nested_projection|
|
|
284
|
-
nested =
|
|
287
|
+
nested = joinable_joins(target, nested_name, nested_projection, used_joins.merge(joins))
|
|
285
288
|
return nil if nested.nil?
|
|
286
289
|
|
|
287
|
-
|
|
290
|
+
joins = joins.merge(nested)
|
|
288
291
|
end
|
|
289
|
-
|
|
292
|
+
joins
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def conflicting?(new_joins, used_joins)
|
|
296
|
+
new_joins.any? { |table, signature| used_joins.key?(table) && used_joins[table] != signature }
|
|
290
297
|
end
|
|
291
298
|
|
|
292
299
|
# The target collection when this hop is safe to collapse into a JOIN, else nil (-> preload).
|
|
293
|
-
def joinable_target(collection, relation_name
|
|
300
|
+
def joinable_target(collection, relation_name)
|
|
294
301
|
relation_schema = collection.schema[:fields][relation_name]
|
|
295
302
|
return unless relation_schema.respond_to?(:foreign_collection)
|
|
296
303
|
|
|
@@ -308,19 +315,29 @@ module ForestAdminDatasourceActiveRecord
|
|
|
308
315
|
target = local_ar_collection(collection.datasource, relation_schema.foreign_collection)
|
|
309
316
|
return if target.nil? || !target.model.default_scopes.empty? # same risk as a scoped association
|
|
310
317
|
return unless same_database?(collection.model, target.model)
|
|
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)
|
|
313
318
|
|
|
314
319
|
target
|
|
315
320
|
end
|
|
316
321
|
|
|
317
|
-
def
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
+
def join_signatures(collection, relation_name)
|
|
323
|
+
reflection = collection.model.reflect_on_association(relation_name.to_sym)
|
|
324
|
+
if reflection.through_reflection?
|
|
325
|
+
{ reflection.through_reflection.klass.table_name => signature(reflection.through_reflection),
|
|
326
|
+
reflection.klass.table_name => signature(reflection.source_reflection) }
|
|
327
|
+
else
|
|
328
|
+
{ reflection.klass.table_name => signature(reflection) }
|
|
329
|
+
end
|
|
322
330
|
rescue StandardError
|
|
323
|
-
|
|
331
|
+
nil
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def signature(reflection)
|
|
335
|
+
"#{reflection.active_record.table_name}.#{Array(reflection.foreign_key).join(",")}" \
|
|
336
|
+
"->#{reflection.klass.table_name}.#{Array(join_key(reflection)).join(",")}"
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def join_key(reflection)
|
|
340
|
+
reflection.respond_to?(:join_primary_key) ? reflection.join_primary_key : reflection.association_primary_key
|
|
324
341
|
end
|
|
325
342
|
|
|
326
343
|
def belongs_to_chain_through?(reflection)
|