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: 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: []