bulk_dependency_eraser 1.0.2 → 1.0.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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc8c21921516530d646fff41d72ce0157e1d706373eefea4da7ea924bb1cb9b9
|
4
|
+
data.tar.gz: 9501007142223dfad5cb05e89ab38162499b564eaaf3636074e3cd54a573592f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f461d596eb9160828f0905dde68396ade1409f8f9c17de2e4bb8677cb12695be59c75f6fa33d84d9156a2b10fdab2eed869b7f19a79c84e1526c705ac4cf58b
|
7
|
+
data.tar.gz: 64777768e6eb815d575d11690cb793962ab4fcb24911bc2bb209d5d7a33d3bc660b6d939fe287ca2c41dcac8524efe95032f3b6ee76272956391589193315666
|
@@ -30,7 +30,9 @@ module BulkDependencyEraser
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def report_error msg
|
33
|
-
|
33
|
+
# remove new lines, surrounding white space, replace with semicolon delimiters
|
34
|
+
n = msg.strip.gsub(/\s*\n\s*/, ' ')
|
35
|
+
@errors << n
|
34
36
|
end
|
35
37
|
|
36
38
|
def merge_errors errors, prefix = nil
|
@@ -13,6 +13,7 @@ module BulkDependencyEraser
|
|
13
13
|
# Won't parse any table in this list
|
14
14
|
ignore_tables_and_dependencies: [],
|
15
15
|
ignore_klass_names_and_dependencies: [],
|
16
|
+
batching_size_limit: 500,
|
16
17
|
}.freeze
|
17
18
|
|
18
19
|
DEFAULT_DB_WRAPPER = ->(block) do
|
@@ -106,11 +107,18 @@ module BulkDependencyEraser
|
|
106
107
|
#{e.message}
|
107
108
|
"
|
108
109
|
)
|
110
|
+
raise e
|
109
111
|
|
110
112
|
return false
|
111
113
|
end
|
112
114
|
end
|
113
115
|
|
116
|
+
protected
|
117
|
+
|
118
|
+
attr_reader :ignore_klass_and_dependencies
|
119
|
+
attr_reader :table_names_to_parsed_klass_names
|
120
|
+
attr_reader :ignore_table_name_and_dependencies, :ignore_klass_name_and_dependencies
|
121
|
+
|
114
122
|
def deletion_query_parser query, association_parent = nil
|
115
123
|
# necessary for "ActiveRecord::Reflection::ThroughReflection" use-case
|
116
124
|
# force_through_destroy_chains = options[:force_destroy_chain] || {}
|
@@ -145,7 +153,7 @@ module BulkDependencyEraser
|
|
145
153
|
if association_parent
|
146
154
|
puts "Building #{klass_name}"
|
147
155
|
else
|
148
|
-
puts "Building #{association_parent}
|
156
|
+
puts "Building #{association_parent}, assocation of #{klass_name}"
|
149
157
|
end
|
150
158
|
end
|
151
159
|
|
@@ -184,19 +192,12 @@ module BulkDependencyEraser
|
|
184
192
|
# ignore associations that aren't a dependent destroyable type
|
185
193
|
destroy_associations = query.reflect_on_all_associations.select do |reflection|
|
186
194
|
assoc_dependent_type = reflection.options&.dig(:dependent)&.to_sym
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
"#{klass_name}'s assoc '#{reflection.name}' has a 'dependent: :#{assoc_dependent_type}' set. " \
|
194
|
-
"If you still wish to destroy, use the 'force_destroy_restricted: true' option"
|
195
|
-
)
|
196
|
-
false
|
197
|
-
else
|
198
|
-
DEPENDENCY_DESTROY.include?(assoc_dependent_type)
|
199
|
-
end
|
195
|
+
DEPENDENCY_DESTROY.include?(assoc_dependent_type) && !DEPENDENCY_RESTRICT.include?(assoc_dependent_type)
|
196
|
+
end
|
197
|
+
|
198
|
+
restricted_associations = query.reflect_on_all_associations.select do |reflection|
|
199
|
+
assoc_dependent_type = reflection.options&.dig(:dependent)&.to_sym
|
200
|
+
DEPENDENCY_RESTRICT.include?(assoc_dependent_type)
|
200
201
|
end
|
201
202
|
|
202
203
|
nullify_associations = query.reflect_on_all_associations.select do |reflection|
|
@@ -204,8 +205,15 @@ module BulkDependencyEraser
|
|
204
205
|
DEPENDENCY_NULLIFY.include?(assoc_dependent_type)
|
205
206
|
end
|
206
207
|
|
207
|
-
destroy_association_names
|
208
|
-
nullify_association_names
|
208
|
+
destroy_association_names = destroy_associations.map(&:name)
|
209
|
+
nullify_association_names = nullify_associations.map(&:name)
|
210
|
+
restricted_association_names = restricted_associations.map(&:name)
|
211
|
+
|
212
|
+
if opts_c.verbose
|
213
|
+
puts "Destroyable Associations: #{destroy_association_names}"
|
214
|
+
puts "Nullifiable Associations: #{nullify_association_names}"
|
215
|
+
puts " Restricted Associations: #{restricted_association_names}"
|
216
|
+
end
|
209
217
|
|
210
218
|
# Iterate through the assoc names, if there are any :through assocs, then remap
|
211
219
|
destroy_association_names = destroy_association_names.collect do |assoc_name|
|
@@ -222,12 +230,49 @@ module BulkDependencyEraser
|
|
222
230
|
nullify_association_names.each do |nullify_association_name|
|
223
231
|
association_parser(klass, query, query_ids, nullify_association_name, :nullify)
|
224
232
|
end
|
233
|
+
|
234
|
+
restricted_association_names.each do |restricted_association_name|
|
235
|
+
association_parser(klass, query, query_ids, restricted_association_name, :restricted)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Used to iterate through each destroyable association, and recursively call 'deletion_query_parser'.
|
240
|
+
# @param parent_class [ApplicationRecord]
|
241
|
+
# @param query [ActiveRecord_Relation] - We need the 'query' in case associations are tied to column other than 'id'
|
242
|
+
# @param query_ids [Array<Int | String>] - Array of parent's IDs (or UUIDs)
|
243
|
+
# @param association_name [Symbol] - The association name from the parent_class
|
244
|
+
# @param type [Symbol] - either :delete or :nullify or :restricted
|
245
|
+
def association_parser(parent_class, query, query_ids, association_name, type)
|
246
|
+
reflection = parent_class.reflect_on_association(association_name)
|
247
|
+
reflection_type = reflection.class.name
|
248
|
+
|
249
|
+
if self.class::DEPENDENCY_DESTROY_IGNORE_REFLECTION_TYPES.include?(reflection_type)
|
250
|
+
report_error("Dependency detected on #{parent_class.name}'s '#{association_name}' - association doesn't support dependency")
|
251
|
+
return
|
252
|
+
end
|
253
|
+
|
254
|
+
case reflection_type
|
255
|
+
when 'ActiveRecord::Reflection::HasManyReflection'
|
256
|
+
association_parser_has_many(parent_class, query, query_ids, association_name, type)
|
257
|
+
when 'ActiveRecord::Reflection::HasOneReflection'
|
258
|
+
association_parser_has_many(parent_class, query, query_ids, association_name, type, { limit: 1 })
|
259
|
+
when 'ActiveRecord::Reflection::BelongsToReflection'
|
260
|
+
if type == :nullify
|
261
|
+
report_error("#{parent_class.name}'s association '#{association_name}' - dependent 'nullify' invalid for 'belongs_to'")
|
262
|
+
else
|
263
|
+
association_parser_belongs_to(parent_class, query, query_ids, association_name, type)
|
264
|
+
end
|
265
|
+
else
|
266
|
+
report_message("Unsupported association type for #{parent_class.name}'s association '#{association_name}': #{reflection_type}")
|
267
|
+
end
|
225
268
|
end
|
226
269
|
|
227
|
-
#
|
228
|
-
|
270
|
+
# Handles the :has_many association type
|
271
|
+
# - handles it's polymorphic associations internally (easier on the has_many)
|
272
|
+
def association_parser_has_many(parent_class, query, query_ids, association_name, type, opts = {})
|
229
273
|
reflection = parent_class.reflect_on_association(association_name)
|
230
274
|
reflection_type = reflection.class.name
|
275
|
+
|
231
276
|
assoc_klass = reflection.klass
|
232
277
|
assoc_klass_name = assoc_klass.name
|
233
278
|
|
@@ -241,9 +286,9 @@ module BulkDependencyEraser
|
|
241
286
|
# If there is an association scope present, check to see how many parameters it's using
|
242
287
|
# - if there's any parameter, we have to either skip it or instantiate it to find it's dependencies.
|
243
288
|
if reflection.scope&.arity&.nonzero?
|
244
|
-
# TODO!
|
245
289
|
if opts_c.instantiate_if_assoc_scope_with_arity
|
246
|
-
|
290
|
+
association_parser_has_many_instantiation(parent_class, query, query_ids, association_name, type, opts)
|
291
|
+
return
|
247
292
|
else
|
248
293
|
report_error(
|
249
294
|
"#{parent_class.name} and '#{association_name}' - scope has instance parameters. Use :instantiate_if_assoc_scope_with_arity option?"
|
@@ -252,21 +297,23 @@ module BulkDependencyEraser
|
|
252
297
|
end
|
253
298
|
elsif reflection.scope
|
254
299
|
# I saw this used somewhere, too bad I didn't save the source for it.
|
255
|
-
|
256
|
-
assoc_query = assoc_query.instance_exec(&
|
300
|
+
assoc_scope = parent_class.reflect_on_association(association_name).scope
|
301
|
+
assoc_query = assoc_query.instance_exec(&assoc_scope)
|
257
302
|
end
|
258
303
|
|
304
|
+
# Look for manually specified keys in the assocation first
|
259
305
|
specified_primary_key = reflection.options[:primary_key]&.to_s
|
260
306
|
specified_foreign_key = reflection.options[:foreign_key]&.to_s
|
307
|
+
# For polymorphic_associations
|
308
|
+
specified_foreign_type = nil
|
261
309
|
|
262
310
|
# handle foreign_key edge cases
|
263
311
|
if specified_foreign_key.nil?
|
264
|
-
if reflection.options[:
|
265
|
-
|
266
|
-
specified_foreign_key =
|
267
|
-
|
268
|
-
assoc_query = assoc_query.where({
|
269
|
-
specified_foreign_key = reflection.options[:as].to_s + "_id"
|
312
|
+
if reflection.options[:as]
|
313
|
+
specified_foreign_type = "#{reflection.options[:as]}_type"
|
314
|
+
specified_foreign_key = "#{reflection.options[:as]}_id"
|
315
|
+
# Only filtering by type here, the extra work for a poly assoc. We filter by IDs later
|
316
|
+
assoc_query = assoc_query.where({ specified_foreign_type.to_sym => parent_class.name })
|
270
317
|
else
|
271
318
|
specified_foreign_key = parent_class.table_name.singularize + "_id"
|
272
319
|
end
|
@@ -277,7 +324,7 @@ module BulkDependencyEraser
|
|
277
324
|
report_error(
|
278
325
|
"
|
279
326
|
For #{parent_class.name}'s assoc '#{assoc_klass.name}': Could not determine the assoc's foreign key.
|
280
|
-
|
327
|
+
Foreign key should have been '#{specified_foreign_key}', but did not exist on the #{assoc_klass.table_name} table.
|
281
328
|
"
|
282
329
|
)
|
283
330
|
return
|
@@ -295,9 +342,18 @@ module BulkDependencyEraser
|
|
295
342
|
assoc_query = assoc_query.where(specified_foreign_key.to_sym => query_ids)
|
296
343
|
end
|
297
344
|
|
345
|
+
# Apply limit if has_one assocation (subset of has_many)
|
346
|
+
if opts[:limit]
|
347
|
+
assoc_query = assoc_query.limit(opts[:limit])
|
348
|
+
end
|
349
|
+
|
298
350
|
if type == :delete
|
299
351
|
# Recursively call 'deletion_query_parser' on association query, to delete any if the assoc's dependencies
|
300
352
|
deletion_query_parser(assoc_query, parent_class)
|
353
|
+
elsif type == :restricted
|
354
|
+
if traverse_restricted_dependency?(parent_class, reflection, assoc_query)
|
355
|
+
deletion_query_parser(assoc_query, parent_class)
|
356
|
+
end
|
301
357
|
elsif type == :nullify
|
302
358
|
# No need for recursion here.
|
303
359
|
# - we're not destroying these assocs (just nullifying foreign_key columns) so we don't need to parse their dependencies.
|
@@ -312,16 +368,193 @@ module BulkDependencyEraser
|
|
312
368
|
nullification_list[assoc_klass_name][specified_foreign_key] ||= []
|
313
369
|
nullification_list[assoc_klass_name][specified_foreign_key] += assoc_ids
|
314
370
|
nullification_list[assoc_klass_name][specified_foreign_key].uniq!
|
371
|
+
|
372
|
+
nullification_list[assoc_klass_name][specified_foreign_key].sort! if Rails.env.test?
|
373
|
+
|
374
|
+
# Also nullify the 'type' field, if the association is polymorphic
|
375
|
+
if specified_foreign_type
|
376
|
+
nullification_list[assoc_klass_name][specified_foreign_type] ||= []
|
377
|
+
nullification_list[assoc_klass_name][specified_foreign_type] += assoc_ids
|
378
|
+
nullification_list[assoc_klass_name][specified_foreign_type].uniq!
|
379
|
+
|
380
|
+
nullification_list[assoc_klass_name][specified_foreign_type].sort! if Rails.env.test?
|
381
|
+
end
|
315
382
|
else
|
316
383
|
raise "invalid parsing type: #{type}"
|
317
384
|
end
|
318
385
|
end
|
319
386
|
|
320
|
-
|
387
|
+
# So you've decided to attempt instantiation...
|
388
|
+
# This will be a lot slower than the rest of our logic here, but if needs must.
|
389
|
+
#
|
390
|
+
# This method will replicate association_parser, but instantiate and iterate in batches
|
391
|
+
def association_parser_has_many_instantiation(parent_class, query, query_ids, association_name, type, opts)
|
392
|
+
raise "Invalid State! Not ready to instantiate!"
|
393
|
+
reflection = parent_class.reflect_on_association(association_name)
|
394
|
+
reflection_type = reflection.class.name
|
395
|
+
assoc_klass = reflection.klass
|
396
|
+
assoc_klass_name = assoc_klass.name
|
321
397
|
|
322
|
-
|
323
|
-
|
324
|
-
|
398
|
+
|
399
|
+
# specified_primary_key = reflection.options[:primary_key]&.to_s
|
400
|
+
# specified_foreign_key = reflection.options[:foreign_key]&.to_s
|
401
|
+
|
402
|
+
# assoc_query = assoc_klass.unscoped
|
403
|
+
# query.in_batches
|
404
|
+
|
405
|
+
assoc_klass.in_batches(of: opts_c.batching_size_limit) do |batch|
|
406
|
+
batch.each do |record|
|
407
|
+
record.send(association_name)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
def association_parser_belongs_to(parent_class, query, query_ids, association_name, type)
|
413
|
+
reflection = parent_class.reflect_on_association(association_name)
|
414
|
+
reflection_type = reflection.class.name
|
415
|
+
|
416
|
+
# Can't run certain checks on a polymorphic association, no definitive klass to use.
|
417
|
+
# - Usually, the polymorphic class is the leaf in a dependency tree.
|
418
|
+
# - In this case, i.e.: a `belongs_to :polymorphicable, polymorphic: true, dependent: :destroy` use-case
|
419
|
+
if reflection.options[:polymorphic]
|
420
|
+
# We'd have to pluck our various types, iterate through each, using each type as the assoc_query starting point
|
421
|
+
association_parser_belongs_to_polymorphic(parent_class, query, query_ids, association_name, type)
|
422
|
+
return
|
423
|
+
end
|
424
|
+
|
425
|
+
assoc_klass = reflection.klass
|
426
|
+
assoc_klass_name = assoc_klass.name
|
427
|
+
|
428
|
+
assoc_query = assoc_klass.unscoped
|
429
|
+
|
430
|
+
unless assoc_klass.primary_key == 'id'
|
431
|
+
report_error("#{parent_class.name}'s association '#{association_name}' - assoc class does not use 'id' as a primary_key")
|
432
|
+
return
|
433
|
+
end
|
434
|
+
|
435
|
+
# If there is an association scope present, check to see how many parameters it's using
|
436
|
+
# - if there's any parameter, we have to either skip it or instantiate it to find it's dependencies.
|
437
|
+
if reflection.scope&.arity&.nonzero?
|
438
|
+
# TODO: PENDING:
|
439
|
+
# if opts_c.instantiate_if_assoc_scope_with_arity
|
440
|
+
# association_parser_belongs_to_instantiation(parent_class, query, query_ids, association_name, type)
|
441
|
+
# return
|
442
|
+
# else
|
443
|
+
# report_error(
|
444
|
+
# "#{parent_class.name} and '#{association_name}' - scope has instance parameters. Use :instantiate_if_assoc_scope_with_arity option?"
|
445
|
+
# )
|
446
|
+
# return
|
447
|
+
# end
|
448
|
+
elsif reflection.scope
|
449
|
+
# I saw this used somewhere, too bad I didn't save the source for it.
|
450
|
+
assoc_scope = parent_class.reflect_on_association(association_name).scope
|
451
|
+
assoc_query = assoc_query.instance_exec(&assoc_scope)
|
452
|
+
end
|
453
|
+
|
454
|
+
specified_primary_key = reflection.options[:primary_key] || 'id'
|
455
|
+
specified_foreign_key = reflection.options[:foreign_key] || "#{association_name}_id"
|
456
|
+
|
457
|
+
# Check to see if foreign_key exists in our parent table
|
458
|
+
unless parent_class.column_names.include?(specified_foreign_key)
|
459
|
+
report_error(
|
460
|
+
"
|
461
|
+
For #{parent_class.name}'s association '#{association_name}': Could not determine the assoc's foreign key.
|
462
|
+
Foreign key should have been '#{specified_foreign_key}', but did not exist on the #{parent_class.table_name} table.
|
463
|
+
"
|
464
|
+
)
|
465
|
+
return
|
466
|
+
end
|
467
|
+
|
468
|
+
assoc_query = read_from_db do
|
469
|
+
assoc_query.where(
|
470
|
+
specified_primary_key.to_sym => query.pluck(specified_foreign_key)
|
471
|
+
)
|
472
|
+
end
|
473
|
+
|
474
|
+
if type == :delete
|
475
|
+
# Recursively call 'deletion_query_parser' on association query, to delete any if the assoc's dependencies
|
476
|
+
deletion_query_parser(assoc_query, parent_class)
|
477
|
+
elsif type == :restricted
|
478
|
+
if traverse_restricted_dependency?(parent_class, reflection, assoc_query)
|
479
|
+
deletion_query_parser(assoc_query, parent_class)
|
480
|
+
end
|
481
|
+
else
|
482
|
+
raise "invalid parsing type: #{type}"
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
# So you've decided to attempt instantiation...
|
487
|
+
# This will be a lot slower than the rest of our logic here, but if needs must.
|
488
|
+
#
|
489
|
+
# This method will replicate association_parser, but instantiate and iterate in batches
|
490
|
+
def association_parser_belongs_to_instantiation(parent_class, query, query_ids, association_name, type)
|
491
|
+
# pending
|
492
|
+
end
|
493
|
+
|
494
|
+
# In this case, it's like a `belongs_to :polymorphicable, polymorphic: true, dependent: :destroy` use-case
|
495
|
+
# - it's unusual, but valid use-case
|
496
|
+
def association_parser_belongs_to_polymorphic(parent_class, query, query_ids, association_name, type)
|
497
|
+
reflection = parent_class.reflect_on_association(association_name)
|
498
|
+
reflection_type = reflection.class.name
|
499
|
+
|
500
|
+
# If there is an association scope present, check to see how many parameters it's using
|
501
|
+
# - if there's any parameter, we have to either skip it or instantiate it to find it's dependencies.
|
502
|
+
if reflection.scope&.arity&.nonzero?
|
503
|
+
raise 'PENDING'
|
504
|
+
elsif reflection.scope
|
505
|
+
# I saw this used somewhere, too bad I didn't save the source for it.
|
506
|
+
assoc_scope = parent_class.reflect_on_association(association_name).scope
|
507
|
+
assoc_query = assoc_query.instance_exec(&assoc_scope)
|
508
|
+
end
|
509
|
+
|
510
|
+
specified_primary_key = reflection.options[:primary_key] || 'id'
|
511
|
+
specified_foreign_key = reflection.options[:foreign_key] || "#{association_name}_id"
|
512
|
+
specified_foreign_type = specified_foreign_key.sub(/_id$/, '_type')
|
513
|
+
|
514
|
+
# Check to see if foreign_key exists in our parent table
|
515
|
+
unless parent_class.column_names.include?(specified_foreign_key)
|
516
|
+
report_error(
|
517
|
+
"
|
518
|
+
For #{parent_class.name}'s association '#{association_name}': Could not determine the class's foreign key.
|
519
|
+
Foreign key should have been '#{specified_foreign_key}', but did not exist on the #{parent_class.table_name} table.
|
520
|
+
"
|
521
|
+
)
|
522
|
+
return
|
523
|
+
end
|
524
|
+
unless parent_class.column_names.include?(specified_foreign_type)
|
525
|
+
report_error(
|
526
|
+
"
|
527
|
+
For #{parent_class.name}'s association '#{association_name}': Could not determine the class's polymorphic type.
|
528
|
+
Foreign key should have been '#{specified_foreign_type}', but did not exist on the #{parent_class.table_name} table.
|
529
|
+
"
|
530
|
+
)
|
531
|
+
return
|
532
|
+
end
|
533
|
+
|
534
|
+
foreign_ids_by_type = read_from_db do
|
535
|
+
query.pluck(specified_foreign_key, specified_foreign_type).each_with_object({}) do |(id, type), hash|
|
536
|
+
hash.key?(type) ? hash[type] << id : hash[type] = [id]
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
if type == :delete
|
541
|
+
# Recursively call 'deletion_query_parser' on association query, to delete any if the assoc's dependencies
|
542
|
+
foreign_ids_by_type.each do |type, ids|
|
543
|
+
assoc_klass = type.constantize
|
544
|
+
deletion_query_parser(assoc_klass.where(id: ids), assoc_klass)
|
545
|
+
end
|
546
|
+
elsif type == :restricted
|
547
|
+
if traverse_restricted_dependency_for_belongs_to_poly?(parent_class, reflection, foreign_ids_by_type)
|
548
|
+
# Recursively call 'deletion_query_parser' on association query, to delete any if the assoc's dependencies
|
549
|
+
foreign_ids_by_type.each do |type, ids|
|
550
|
+
assoc_klass = type.constantize
|
551
|
+
deletion_query_parser(assoc_klass.where(id: ids), assoc_klass)
|
552
|
+
end
|
553
|
+
end
|
554
|
+
else
|
555
|
+
raise "invalid parsing type: #{type}"
|
556
|
+
end
|
557
|
+
end
|
325
558
|
|
326
559
|
# A dependent assoc may be through another association. Follow the throughs to find the correct assoc to destroy.
|
327
560
|
def find_root_association_from_through_assocs klass, association_name
|
@@ -334,6 +567,43 @@ module BulkDependencyEraser
|
|
334
567
|
end
|
335
568
|
end
|
336
569
|
|
570
|
+
# return [Boolean]
|
571
|
+
# - true if valid
|
572
|
+
# - false if not valid
|
573
|
+
def traverse_restricted_dependency? parent_class, reflection, association_query
|
574
|
+
# Return true if we're going to destroy all restricted
|
575
|
+
return true if opts_c.force_destroy_restricted
|
576
|
+
|
577
|
+
if association_query.any?
|
578
|
+
report_error(
|
579
|
+
"
|
580
|
+
#{parent_class.name}'s assoc '#{reflection.name}' has a restricted dependency type.
|
581
|
+
If you still wish to destroy, use the 'force_destroy_restricted: true' option
|
582
|
+
"
|
583
|
+
)
|
584
|
+
end
|
585
|
+
|
586
|
+
return false
|
587
|
+
end
|
588
|
+
|
589
|
+
# special use-case to detect restricted dependency for 'belongs_to polymorphic: true' use-case
|
590
|
+
def traverse_restricted_dependency_for_belongs_to_poly? parent_class, reflection, ids_by_type
|
591
|
+
# Return true if we're going to destroy all restricted
|
592
|
+
return true if opts_c.force_destroy_restricted
|
593
|
+
|
594
|
+
ids = ids_by_type.values.flatten
|
595
|
+
if ids.any?
|
596
|
+
report_error(
|
597
|
+
"
|
598
|
+
#{parent_class.name}'s assoc '#{reflection.name}' has a restricted dependency type.
|
599
|
+
If you still wish to destroy, use the 'force_destroy_restricted: true' option
|
600
|
+
"
|
601
|
+
)
|
602
|
+
end
|
603
|
+
|
604
|
+
return false
|
605
|
+
end
|
606
|
+
|
337
607
|
def read_from_db(&block)
|
338
608
|
puts "Reading from DB..." if opts_c.verbose
|
339
609
|
opts_c.db_read_wrapper.call(block)
|
@@ -44,8 +44,8 @@ module BulkDependencyEraser
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
47
|
-
rescue
|
48
|
-
report_error("Issue attempting to delete '#{current_class_name}': #{e.name} - #{e.message}")
|
47
|
+
rescue StandardError => e
|
48
|
+
report_error("Issue attempting to delete '#{current_class_name}': #{e.class.name} - #{e.message}")
|
49
49
|
raise ActiveRecord::Rollback
|
50
50
|
end
|
51
51
|
end
|
@@ -47,8 +47,8 @@ module BulkDependencyEraser
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
50
|
-
rescue
|
51
|
-
report_error("Issue attempting to nullify '#{current_class_name}' column '#{current_column}': #{e.name} - #{e.message}")
|
50
|
+
rescue StandardError => e
|
51
|
+
report_error("Issue attempting to nullify '#{current_class_name}' column '#{current_column}': #{e.class.name} - #{e.message}")
|
52
52
|
raise ActiveRecord::Rollback
|
53
53
|
end
|
54
54
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bulk_dependency_eraser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- benjamin.dana.software.dev@gmail.com
|
@@ -108,6 +108,34 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '1.4'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: factory_bot
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '6.4'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '6.4'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: faker
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '3.4'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '3.4'
|
111
139
|
description:
|
112
140
|
email:
|
113
141
|
executables: []
|