bulk_dependency_eraser 1.0.2 → 1.0.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: 84aa6beacc6783239cb7203cb6c829b041af7829d0692182475d04e00baed0c0
4
- data.tar.gz: c6dc0bb1ebc2807aad0f16db6ad4f585a58b31f64ad04b178851dead99459e8e
3
+ metadata.gz: dc8c21921516530d646fff41d72ce0157e1d706373eefea4da7ea924bb1cb9b9
4
+ data.tar.gz: 9501007142223dfad5cb05e89ab38162499b564eaaf3636074e3cd54a573592f
5
5
  SHA512:
6
- metadata.gz: a557bafd9cc91f7611491607e05f5f4dbccb1d059581f2716a51da9b660656d13a490c958990faea6aa655e41fa3d0ad1ed66a70b5ff19bb6c5763f45bdae376
7
- data.tar.gz: 6d170e68022d2b6fe59b96880f43dafdd52cc837734905de5ca0a65ed2b19d7f01bcce934453dafb0c73a236f0c41a53945f190e386d78f4d1077dc4b6aa6bd4
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
- @errors << msg
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} => #{klass_name}"
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
- if DEPENDENCY_DESTROY_IGNORE_REFLECTION_TYPES.include?(reflection.class.name)
188
- # Ignore those types of associations.
189
- false
190
- elsif DEPENDENCY_RESTRICT.include?(assoc_dependent_type) && opts_c.force_destroy_restricted != true
191
- # If the dependency_type is restricted_with_..., and we're not supposed to destroy those, report errork
192
- report_error(
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 = destroy_associations.map(&:name)
208
- nullify_association_names = nullify_associations.map(&:name)
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
- # Iterate through each destroyable association, and recursively call 'deletion_query_parser'.
228
- def association_parser parent_class, query, query_ids, association_name, type
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
- raise "TODO: instantiate and apply scope!"
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
- s = parent_class.reflect_on_association(association_name).scope
256
- assoc_query = assoc_query.instance_exec(&s)
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[:polymorphic]
265
- assoc_query = assoc_query.where({(association_name.singularize + '_type').to_sym => parent_class.table_name.classify})
266
- specified_foreign_key = association_name.singularize + "_id"
267
- elsif reflection.options[:as]
268
- assoc_query = assoc_query.where({(reflection.options[:as].to_s + '_type').to_sym => parent_class.table_name.classify})
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
- Generated '#{specified_foreign_key}', but did not exist on the association table.
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
- protected
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
- attr_reader :ignore_klass_and_dependencies
323
- attr_reader :table_names_to_parsed_klass_names
324
- attr_reader :ignore_table_name_and_dependencies, :ignore_klass_name_and_dependencies
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 Exception => e
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 Exception => e
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.2
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: []