active_record_doctor 1.8.0 → 1.9.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +246 -48
  3. data/lib/active_record_doctor/config/default.rb +59 -0
  4. data/lib/active_record_doctor/config/loader.rb +137 -0
  5. data/lib/active_record_doctor/config.rb +14 -0
  6. data/lib/active_record_doctor/detectors/base.rb +110 -19
  7. data/lib/active_record_doctor/detectors/extraneous_indexes.rb +63 -37
  8. data/lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb +32 -23
  9. data/lib/active_record_doctor/detectors/incorrect_dependent_option.rb +70 -34
  10. data/lib/active_record_doctor/detectors/mismatched_foreign_key_type.rb +45 -0
  11. data/lib/active_record_doctor/detectors/missing_foreign_keys.rb +32 -23
  12. data/lib/active_record_doctor/detectors/missing_non_null_constraint.rb +40 -28
  13. data/lib/active_record_doctor/detectors/missing_presence_validation.rb +28 -21
  14. data/lib/active_record_doctor/detectors/missing_unique_indexes.rb +40 -30
  15. data/lib/active_record_doctor/detectors/short_primary_key_type.rb +41 -0
  16. data/lib/active_record_doctor/detectors/undefined_table_references.rb +19 -20
  17. data/lib/active_record_doctor/detectors/unindexed_deleted_at.rb +44 -18
  18. data/lib/active_record_doctor/detectors/unindexed_foreign_keys.rb +31 -20
  19. data/lib/active_record_doctor/detectors.rb +12 -4
  20. data/lib/active_record_doctor/errors.rb +226 -0
  21. data/lib/active_record_doctor/help.rb +39 -0
  22. data/lib/active_record_doctor/rake/task.rb +78 -0
  23. data/lib/active_record_doctor/runner.rb +41 -0
  24. data/lib/active_record_doctor/version.rb +1 -1
  25. data/lib/active_record_doctor.rb +7 -3
  26. data/lib/generators/active_record_doctor/add_indexes/add_indexes_generator.rb +34 -21
  27. data/lib/tasks/active_record_doctor.rake +9 -18
  28. data/test/active_record_doctor/config/loader_test.rb +120 -0
  29. data/test/active_record_doctor/config_test.rb +116 -0
  30. data/test/active_record_doctor/detectors/extraneous_indexes_test.rb +131 -8
  31. data/test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb +48 -5
  32. data/test/active_record_doctor/detectors/incorrect_dependent_option_test.rb +190 -12
  33. data/test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb +82 -0
  34. data/test/active_record_doctor/detectors/missing_foreign_keys_test.rb +50 -4
  35. data/test/active_record_doctor/detectors/missing_non_null_constraint_test.rb +138 -24
  36. data/test/active_record_doctor/detectors/missing_presence_validation_test.rb +74 -13
  37. data/test/active_record_doctor/detectors/missing_unique_indexes_test.rb +57 -8
  38. data/test/active_record_doctor/detectors/short_primary_key_type_test.rb +64 -0
  39. data/test/active_record_doctor/detectors/undefined_table_references_test.rb +34 -21
  40. data/test/active_record_doctor/detectors/unindexed_deleted_at_test.rb +112 -8
  41. data/test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb +56 -4
  42. data/test/active_record_doctor/runner_test.rb +42 -0
  43. data/test/generators/active_record_doctor/add_indexes/add_indexes_generator_test.rb +131 -0
  44. data/test/model_factory.rb +73 -23
  45. data/test/setup.rb +62 -72
  46. metadata +40 -9
  47. data/lib/active_record_doctor/printers/io_printer.rb +0 -133
  48. data/lib/active_record_doctor/task.rb +0 -28
  49. data/test/active_record_doctor/printers/io_printer_test.rb +0 -33
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_doctor
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.9.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Navis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-20 00:00:00.000000000 Z
11
+ date: 2021-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 1.1.4
69
+ - !ruby/object:Gem::Dependency
70
+ name: railties
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 4.2.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 4.2.0
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rake
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -104,37 +118,49 @@ files:
104
118
  - MIT-LICENSE.txt
105
119
  - README.md
106
120
  - lib/active_record_doctor.rb
121
+ - lib/active_record_doctor/config.rb
122
+ - lib/active_record_doctor/config/default.rb
123
+ - lib/active_record_doctor/config/loader.rb
107
124
  - lib/active_record_doctor/detectors.rb
108
125
  - lib/active_record_doctor/detectors/base.rb
109
126
  - lib/active_record_doctor/detectors/extraneous_indexes.rb
110
127
  - lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb
111
128
  - lib/active_record_doctor/detectors/incorrect_dependent_option.rb
129
+ - lib/active_record_doctor/detectors/mismatched_foreign_key_type.rb
112
130
  - lib/active_record_doctor/detectors/missing_foreign_keys.rb
113
131
  - lib/active_record_doctor/detectors/missing_non_null_constraint.rb
114
132
  - lib/active_record_doctor/detectors/missing_presence_validation.rb
115
133
  - lib/active_record_doctor/detectors/missing_unique_indexes.rb
134
+ - lib/active_record_doctor/detectors/short_primary_key_type.rb
116
135
  - lib/active_record_doctor/detectors/undefined_table_references.rb
117
136
  - lib/active_record_doctor/detectors/unindexed_deleted_at.rb
118
137
  - lib/active_record_doctor/detectors/unindexed_foreign_keys.rb
138
+ - lib/active_record_doctor/errors.rb
139
+ - lib/active_record_doctor/help.rb
119
140
  - lib/active_record_doctor/printers.rb
120
- - lib/active_record_doctor/printers/io_printer.rb
121
141
  - lib/active_record_doctor/railtie.rb
122
- - lib/active_record_doctor/task.rb
142
+ - lib/active_record_doctor/rake/task.rb
143
+ - lib/active_record_doctor/runner.rb
123
144
  - lib/active_record_doctor/version.rb
124
145
  - lib/generators/active_record_doctor/add_indexes/USAGE
125
146
  - lib/generators/active_record_doctor/add_indexes/add_indexes_generator.rb
126
147
  - lib/tasks/active_record_doctor.rake
148
+ - test/active_record_doctor/config/loader_test.rb
149
+ - test/active_record_doctor/config_test.rb
127
150
  - test/active_record_doctor/detectors/extraneous_indexes_test.rb
128
151
  - test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb
129
152
  - test/active_record_doctor/detectors/incorrect_dependent_option_test.rb
153
+ - test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb
130
154
  - test/active_record_doctor/detectors/missing_foreign_keys_test.rb
131
155
  - test/active_record_doctor/detectors/missing_non_null_constraint_test.rb
132
156
  - test/active_record_doctor/detectors/missing_presence_validation_test.rb
133
157
  - test/active_record_doctor/detectors/missing_unique_indexes_test.rb
158
+ - test/active_record_doctor/detectors/short_primary_key_type_test.rb
134
159
  - test/active_record_doctor/detectors/undefined_table_references_test.rb
135
160
  - test/active_record_doctor/detectors/unindexed_deleted_at_test.rb
136
161
  - test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb
137
- - test/active_record_doctor/printers/io_printer_test.rb
162
+ - test/active_record_doctor/runner_test.rb
163
+ - test/generators/active_record_doctor/add_indexes/add_indexes_generator_test.rb
138
164
  - test/model_factory.rb
139
165
  - test/setup.rb
140
166
  homepage: https://github.com/gregnavis/active_record_doctor
@@ -152,25 +178,30 @@ required_ruby_version: !ruby/object:Gem::Requirement
152
178
  version: 2.1.0
153
179
  required_rubygems_version: !ruby/object:Gem::Requirement
154
180
  requirements:
155
- - - ">="
181
+ - - ">"
156
182
  - !ruby/object:Gem::Version
157
- version: '0'
183
+ version: 1.3.1
158
184
  requirements: []
159
- rubygems_version: 3.2.15
185
+ rubygems_version: 3.2.22
160
186
  signing_key:
161
187
  specification_version: 4
162
188
  summary: Identify database issues before they hit production.
163
189
  test_files:
190
+ - test/active_record_doctor/config/loader_test.rb
191
+ - test/active_record_doctor/config_test.rb
164
192
  - test/active_record_doctor/detectors/extraneous_indexes_test.rb
165
193
  - test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb
166
194
  - test/active_record_doctor/detectors/incorrect_dependent_option_test.rb
195
+ - test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb
167
196
  - test/active_record_doctor/detectors/missing_foreign_keys_test.rb
168
197
  - test/active_record_doctor/detectors/missing_non_null_constraint_test.rb
169
198
  - test/active_record_doctor/detectors/missing_presence_validation_test.rb
170
199
  - test/active_record_doctor/detectors/missing_unique_indexes_test.rb
200
+ - test/active_record_doctor/detectors/short_primary_key_type_test.rb
171
201
  - test/active_record_doctor/detectors/undefined_table_references_test.rb
172
202
  - test/active_record_doctor/detectors/unindexed_deleted_at_test.rb
173
203
  - test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb
174
- - test/active_record_doctor/printers/io_printer_test.rb
204
+ - test/active_record_doctor/runner_test.rb
205
+ - test/generators/active_record_doctor/add_indexes/add_indexes_generator_test.rb
175
206
  - test/model_factory.rb
176
207
  - test/setup.rb
@@ -1,133 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveRecordDoctor
4
- module Printers
5
- # Default printer for displaying messages produced by active_record_doctor.
6
- class IOPrinter
7
- def initialize(io = $stdout)
8
- @io = io
9
- end
10
-
11
- def unindexed_foreign_keys(unindexed_foreign_keys, _options)
12
- return if unindexed_foreign_keys.empty?
13
-
14
- @io.puts("The following foreign keys should be indexed for performance reasons:")
15
- @io.puts(unindexed_foreign_keys.sort.map do |table, columns|
16
- " #{table} #{columns.sort.join(' ')}"
17
- end.join("\n"))
18
- end
19
-
20
- def extraneous_indexes(extraneous_indexes, _options)
21
- return if extraneous_indexes.empty?
22
-
23
- @io.puts("The following indexes are extraneous and can be removed:")
24
- extraneous_indexes.each do |index, details|
25
- reason, *params = details
26
- case reason
27
- when :multi_column
28
- @io.puts(" #{index} (can be handled by #{params.join(', ')})")
29
- when :primary_key
30
- @io.puts(" #{index} (is a primary key of #{params[0]})")
31
- else
32
- raise("unknown reason #{reason.inspect}")
33
- end
34
- end
35
- end
36
-
37
- def missing_foreign_keys(missing_foreign_keys, _options)
38
- return if missing_foreign_keys.empty?
39
-
40
- @io.puts("The following columns lack a foreign key constraint:")
41
- @io.puts(missing_foreign_keys.sort.map do |table, columns|
42
- " #{table} #{columns.sort.join(' ')}"
43
- end.join("\n"))
44
- end
45
-
46
- def undefined_table_references(models, options)
47
- return if models.empty?
48
-
49
- unless options.fetch(:views_checked)
50
- @io.puts(<<WARNING)
51
- WARNING: Models backed by database views are supported only in Rails 5+ OR
52
- Rails 4.2 + PostgreSQL. It seems this is NOT your setup. Therefore, such models
53
- will be erroneously reported below as not having their underlying tables/views.
54
- Consider upgrading Rails or disabling this task temporarily.
55
- WARNING
56
- end
57
-
58
- @io.puts("The following models reference undefined tables:")
59
- models.each do |model_name, table_name|
60
- @io.puts(" #{model_name} (the table #{table_name} is undefined)")
61
- end
62
- end
63
-
64
- def unindexed_deleted_at(indexes, _options)
65
- return if indexes.empty?
66
-
67
- @io.puts("The following indexes should include `deleted_at IS NULL`:")
68
- indexes.each do |index|
69
- @io.puts(" #{index}")
70
- end
71
- end
72
-
73
- def missing_unique_indexes(indexes, _options)
74
- return if indexes.empty?
75
-
76
- @io.puts("The following indexes should be created to back model-level uniqueness validations:")
77
- indexes.each do |table, arrays_of_columns|
78
- arrays_of_columns.each do |columns|
79
- @io.puts(" #{table}: #{columns.join(', ')}")
80
- end
81
- end
82
- end
83
-
84
- def missing_presence_validation(missing_presence_validators, _options)
85
- return if missing_presence_validators.empty?
86
-
87
- @io.puts("The following models and columns should have presence validations:")
88
- missing_presence_validators.each do |model_name, array_of_columns|
89
- @io.puts(" #{model_name}: #{array_of_columns.join(', ')}")
90
- end
91
- end
92
-
93
- def missing_non_null_constraint(missing_non_null_constraints, _options)
94
- return if missing_non_null_constraints.empty?
95
-
96
- @io.puts("The following columns should be marked as `null: false`:")
97
- missing_non_null_constraints.each do |table, columns|
98
- @io.puts(" #{table}: #{columns.join(', ')}")
99
- end
100
- end
101
-
102
- def incorrect_boolean_presence_validation(incorrect_boolean_presence_validations, _options)
103
- return if incorrect_boolean_presence_validations.empty?
104
-
105
- @io.puts("The presence of the following boolean columns is validated incorrectly:")
106
- incorrect_boolean_presence_validations.each do |table, columns|
107
- @io.puts(" #{table}: #{columns.join(', ')}")
108
- end
109
- end
110
-
111
- def incorrect_dependent_option(problems, _options)
112
- return if problems.empty?
113
-
114
- @io.puts("The following associations might be using invalid dependent settings:")
115
- problems.each do |model, associations|
116
- associations.each do |(name, problem)|
117
- # rubocop:disable Layout/LineLength
118
- message =
119
- case problem
120
- when :suggest_destroy then "skips callbacks that are defined on the associated model - consider changing to `dependent: :destroy` or similar"
121
- when :suggest_delete then "loads the associated model before deleting it - consider using `dependent: :delete`"
122
- when :suggest_delete_all then "loads models one-by-one to invoke callbacks even though the related model defines none - consider using `dependent: :delete_all`"
123
- else next
124
- end
125
- # rubocop:enable Layout/LineLength
126
-
127
- @io.puts(" #{model}: #{name} #{message}")
128
- end
129
- end
130
- end
131
- end
132
- end
133
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveRecordDoctor
4
- # Rake task for running a detector and reporting its results.
5
- class Task
6
- DEFAULT_PRINTER = ActiveRecordDoctor::Printers::IOPrinter.new
7
-
8
- def initialize(detector_class, printer = DEFAULT_PRINTER)
9
- @detector_class = detector_class
10
- @printer = printer
11
- end
12
-
13
- def name
14
- @detector_class.name.demodulize.underscore.to_sym
15
- end
16
-
17
- def description
18
- @detector_class.description
19
- end
20
-
21
- def run
22
- problems, options = @detector_class.run
23
- @printer.public_send(name, problems, options)
24
-
25
- problems.empty?
26
- end
27
- end
28
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Load all detectors
4
- class ActiveRecordDoctor::Printers::IOPrinterTest < Minitest::Test
5
- def test_all_detectors_have_printers
6
- ActiveRecordDoctor::Detectors::Base.subclasses.each do |detector_class|
7
- name = detector_class.name.demodulize.underscore.to_sym
8
-
9
- assert(
10
- ActiveRecordDoctor::Printers::IOPrinter.method_defined?(name),
11
- "IOPrinter should define #{name}"
12
- )
13
- end
14
- end
15
-
16
- def test_unindexed_foreign_keys
17
- # rubocop:disable Layout/LineLength
18
- assert_equal(<<OUTPUT, unindexed_foreign_keys({ "users" => ["profile_id", "account_id"], "account" => ["group_id"] }))
19
- The following foreign keys should be indexed for performance reasons:
20
- account group_id
21
- users account_id profile_id
22
- OUTPUT
23
- # rubocop:enable Layout/LineLength
24
- end
25
-
26
- private
27
-
28
- def unindexed_foreign_keys(argument)
29
- io = StringIO.new
30
- ActiveRecordDoctor::Printers::IOPrinter.new(io).unindexed_foreign_keys(argument, {})
31
- io.string
32
- end
33
- end