active_record_doctor 1.9.0 → 1.11.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +83 -19
  3. data/lib/active_record_doctor/config/default.rb +17 -0
  4. data/lib/active_record_doctor/detectors/base.rb +216 -56
  5. data/lib/active_record_doctor/detectors/extraneous_indexes.rb +38 -56
  6. data/lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb +2 -6
  7. data/lib/active_record_doctor/detectors/incorrect_dependent_option.rb +88 -15
  8. data/lib/active_record_doctor/detectors/incorrect_length_validation.rb +60 -0
  9. data/lib/active_record_doctor/detectors/mismatched_foreign_key_type.rb +16 -9
  10. data/lib/active_record_doctor/detectors/missing_foreign_keys.rb +2 -4
  11. data/lib/active_record_doctor/detectors/missing_non_null_constraint.rb +14 -11
  12. data/lib/active_record_doctor/detectors/missing_presence_validation.rb +16 -10
  13. data/lib/active_record_doctor/detectors/missing_unique_indexes.rb +61 -17
  14. data/lib/active_record_doctor/detectors/short_primary_key_type.rb +6 -2
  15. data/lib/active_record_doctor/detectors/undefined_table_references.rb +2 -4
  16. data/lib/active_record_doctor/detectors/unindexed_deleted_at.rb +6 -15
  17. data/lib/active_record_doctor/detectors/unindexed_foreign_keys.rb +2 -4
  18. data/lib/active_record_doctor/logger/dummy.rb +11 -0
  19. data/lib/active_record_doctor/logger/hierarchical.rb +22 -0
  20. data/lib/active_record_doctor/logger.rb +6 -0
  21. data/lib/active_record_doctor/rake/task.rb +10 -1
  22. data/lib/active_record_doctor/runner.rb +8 -3
  23. data/lib/active_record_doctor/version.rb +1 -1
  24. data/lib/active_record_doctor.rb +4 -0
  25. data/lib/generators/active_record_doctor/add_indexes/add_indexes_generator.rb +5 -5
  26. data/test/active_record_doctor/detectors/disable_test.rb +30 -0
  27. data/test/active_record_doctor/detectors/extraneous_indexes_test.rb +34 -0
  28. data/test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb +7 -7
  29. data/test/active_record_doctor/detectors/incorrect_dependent_option_test.rb +220 -43
  30. data/test/active_record_doctor/detectors/incorrect_length_validation_test.rb +107 -0
  31. data/test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb +35 -1
  32. data/test/active_record_doctor/detectors/missing_non_null_constraint_test.rb +78 -21
  33. data/test/active_record_doctor/detectors/missing_presence_validation_test.rb +89 -25
  34. data/test/active_record_doctor/detectors/missing_unique_indexes_test.rb +179 -15
  35. data/test/active_record_doctor/detectors/short_primary_key_type_test.rb +27 -19
  36. data/test/active_record_doctor/detectors/undefined_table_references_test.rb +11 -13
  37. data/test/active_record_doctor/detectors/unindexed_deleted_at_test.rb +9 -3
  38. data/test/active_record_doctor/runner_test.rb +18 -19
  39. data/test/setup.rb +15 -7
  40. metadata +25 -5
  41. data/test/model_factory.rb +0 -128
@@ -1,128 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ModelFactory
4
- def create_table(*args, &block)
5
- ModelFactory.create_table(*args, &block)
6
- end
7
-
8
- def create_model(*args, &block)
9
- ModelFactory.create_model(*args, &block)
10
- end
11
-
12
- def cleanup_models
13
- ModelFactory.cleanup_models
14
- end
15
-
16
- def self.cleanup_models
17
- delete_models
18
- drop_all_tables
19
- GC.start
20
- end
21
-
22
- def self.drop_all_tables
23
- connection = ActiveRecord::Base.connection
24
- loop do
25
- before = connection.data_sources.size
26
- break if before.zero?
27
-
28
- try_drop_all_tables_and_views(connection)
29
- remaining_data_sources = connection.data_sources
30
- after = remaining_data_sources.size
31
-
32
- # rubocop:disable Style/Next
33
- if before == after
34
- raise(<<~ERROR)
35
- Cannot delete temporary tables - most likely due to failing constraints. Remaining tables and views:
36
-
37
- #{remaining_data_sources.join("\n")}
38
- ERROR
39
- end
40
- # rubocop:enable Style/Next
41
- end
42
- end
43
-
44
- def self.try_drop_all_tables_and_views(connection)
45
- connection.data_sources.each do |table_name|
46
- try_drop_table(connection, table_name) || try_drop_view(connection, table_name)
47
- end
48
- end
49
-
50
- def self.try_drop_table(connection, table_name)
51
- ActiveRecord::Migration.suppress_messages do
52
- begin
53
- connection.drop_table(table_name, force: :cascade)
54
- true
55
- rescue ActiveRecord::InvalidForeignKey, ActiveRecord::StatementInvalid
56
- # The table cannot be dropped due to foreign key constraints so
57
- # we'll try to drop it on another attempt.
58
- false
59
- end
60
- end
61
- end
62
-
63
- def self.try_drop_view(connection, view_name)
64
- ActiveRecord::Migration.suppress_messages do
65
- begin
66
- connection.execute("DROP VIEW #{view_name}")
67
- true
68
- rescue ActiveRecord::InvalidForeignKey, ActiveRecord::StatementInvalid
69
- # The table cannot be dropped due to foreign key constraints so
70
- # we'll try to drop it on another attempt.
71
- false
72
- end
73
- end
74
- end
75
-
76
- def self.delete_models
77
- Models.empty
78
- end
79
-
80
- def self.create_table(table_name, options = {}, &block)
81
- table_name = table_name.to_sym
82
- ActiveRecord::Migration.suppress_messages do
83
- ActiveRecord::Migration.create_table(table_name, **options, &block)
84
- end
85
- # Return a proxy object allowing the caller to chain #create_model
86
- # right after creating a table so that it can be followed by the model
87
- # definition.
88
- ModelDefinitionProxy.new(table_name)
89
- end
90
-
91
- def self.create_model(model_name, base_class = ActiveRecord::Base, &block)
92
- model_name = model_name.to_sym
93
-
94
- # Normally, when a class is defined via `class MyClass < MySuperclass` the
95
- # .name class method returns the name of the class when called from within
96
- # the class body. However, anonymous classes defined via Class.new DO NOT
97
- # HAVE NAMES. They're assigned names when they're assigned to a constant.
98
- # If we evaluated the class body, passed via block here, in the class
99
- # definition below then some macros would break
100
- # (e.g. has_and_belongs_to_many) due to nil name.
101
- #
102
- # We solve the problem by defining an empty model class first, assigning to
103
- # a constant to ensure a name is assigned, and then reopening the class to
104
- # give it a non-trivial body.
105
- klass = Class.new(base_class)
106
- Models.const_set(model_name, klass)
107
-
108
- klass.class_eval(&block) if block_given?
109
- end
110
-
111
- class ModelDefinitionProxy
112
- def initialize(table_name)
113
- @table_name = table_name
114
- end
115
-
116
- def create_model(&block)
117
- ModelFactory.create_model(@table_name.to_s.classify, &block)
118
- end
119
- end
120
-
121
- module Models
122
- def self.empty
123
- constants.each do |name|
124
- remove_const(name)
125
- end
126
- end
127
- end
128
- end