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.
- checksums.yaml +4 -4
- data/README.md +83 -19
- data/lib/active_record_doctor/config/default.rb +17 -0
- data/lib/active_record_doctor/detectors/base.rb +216 -56
- data/lib/active_record_doctor/detectors/extraneous_indexes.rb +38 -56
- data/lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb +2 -6
- data/lib/active_record_doctor/detectors/incorrect_dependent_option.rb +88 -15
- data/lib/active_record_doctor/detectors/incorrect_length_validation.rb +60 -0
- data/lib/active_record_doctor/detectors/mismatched_foreign_key_type.rb +16 -9
- data/lib/active_record_doctor/detectors/missing_foreign_keys.rb +2 -4
- data/lib/active_record_doctor/detectors/missing_non_null_constraint.rb +14 -11
- data/lib/active_record_doctor/detectors/missing_presence_validation.rb +16 -10
- data/lib/active_record_doctor/detectors/missing_unique_indexes.rb +61 -17
- data/lib/active_record_doctor/detectors/short_primary_key_type.rb +6 -2
- data/lib/active_record_doctor/detectors/undefined_table_references.rb +2 -4
- data/lib/active_record_doctor/detectors/unindexed_deleted_at.rb +6 -15
- data/lib/active_record_doctor/detectors/unindexed_foreign_keys.rb +2 -4
- data/lib/active_record_doctor/logger/dummy.rb +11 -0
- data/lib/active_record_doctor/logger/hierarchical.rb +22 -0
- data/lib/active_record_doctor/logger.rb +6 -0
- data/lib/active_record_doctor/rake/task.rb +10 -1
- data/lib/active_record_doctor/runner.rb +8 -3
- data/lib/active_record_doctor/version.rb +1 -1
- data/lib/active_record_doctor.rb +4 -0
- data/lib/generators/active_record_doctor/add_indexes/add_indexes_generator.rb +5 -5
- data/test/active_record_doctor/detectors/disable_test.rb +30 -0
- data/test/active_record_doctor/detectors/extraneous_indexes_test.rb +34 -0
- data/test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb +7 -7
- data/test/active_record_doctor/detectors/incorrect_dependent_option_test.rb +220 -43
- data/test/active_record_doctor/detectors/incorrect_length_validation_test.rb +107 -0
- data/test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb +35 -1
- data/test/active_record_doctor/detectors/missing_non_null_constraint_test.rb +78 -21
- data/test/active_record_doctor/detectors/missing_presence_validation_test.rb +89 -25
- data/test/active_record_doctor/detectors/missing_unique_indexes_test.rb +179 -15
- data/test/active_record_doctor/detectors/short_primary_key_type_test.rb +27 -19
- data/test/active_record_doctor/detectors/undefined_table_references_test.rb +11 -13
- data/test/active_record_doctor/detectors/unindexed_deleted_at_test.rb +9 -3
- data/test/active_record_doctor/runner_test.rb +18 -19
- data/test/setup.rb +15 -7
- metadata +25 -5
- data/test/model_factory.rb +0 -128
data/test/model_factory.rb
DELETED
@@ -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
|