active_record_doctor 1.8.0 → 1.10.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 +316 -54
- data/lib/active_record_doctor/config/default.rb +76 -0
- data/lib/active_record_doctor/config/loader.rb +137 -0
- data/lib/active_record_doctor/config.rb +14 -0
- data/lib/active_record_doctor/detectors/base.rb +142 -21
- data/lib/active_record_doctor/detectors/extraneous_indexes.rb +59 -48
- data/lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb +31 -23
- data/lib/active_record_doctor/detectors/incorrect_dependent_option.rb +102 -35
- data/lib/active_record_doctor/detectors/incorrect_length_validation.rb +63 -0
- data/lib/active_record_doctor/detectors/mismatched_foreign_key_type.rb +45 -0
- data/lib/active_record_doctor/detectors/missing_foreign_keys.rb +32 -23
- data/lib/active_record_doctor/detectors/missing_non_null_constraint.rb +41 -28
- data/lib/active_record_doctor/detectors/missing_presence_validation.rb +29 -23
- data/lib/active_record_doctor/detectors/missing_unique_indexes.rb +92 -32
- data/lib/active_record_doctor/detectors/short_primary_key_type.rb +45 -0
- data/lib/active_record_doctor/detectors/undefined_table_references.rb +17 -20
- data/lib/active_record_doctor/detectors/unindexed_deleted_at.rb +43 -18
- data/lib/active_record_doctor/detectors/unindexed_foreign_keys.rb +31 -20
- data/lib/active_record_doctor/detectors.rb +12 -4
- data/lib/active_record_doctor/errors.rb +226 -0
- data/lib/active_record_doctor/help.rb +39 -0
- data/lib/active_record_doctor/rake/task.rb +78 -0
- data/lib/active_record_doctor/runner.rb +41 -0
- data/lib/active_record_doctor/version.rb +1 -1
- data/lib/active_record_doctor.rb +8 -3
- data/lib/generators/active_record_doctor/add_indexes/add_indexes_generator.rb +34 -21
- data/lib/tasks/active_record_doctor.rake +9 -18
- data/test/active_record_doctor/config/loader_test.rb +120 -0
- data/test/active_record_doctor/config_test.rb +116 -0
- data/test/active_record_doctor/detectors/disable_test.rb +30 -0
- data/test/active_record_doctor/detectors/extraneous_indexes_test.rb +165 -8
- data/test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb +48 -5
- data/test/active_record_doctor/detectors/incorrect_dependent_option_test.rb +288 -12
- data/test/active_record_doctor/detectors/incorrect_length_validation_test.rb +105 -0
- data/test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb +82 -0
- data/test/active_record_doctor/detectors/missing_foreign_keys_test.rb +50 -4
- data/test/active_record_doctor/detectors/missing_non_null_constraint_test.rb +172 -24
- data/test/active_record_doctor/detectors/missing_presence_validation_test.rb +111 -14
- data/test/active_record_doctor/detectors/missing_unique_indexes_test.rb +223 -10
- data/test/active_record_doctor/detectors/short_primary_key_type_test.rb +72 -0
- data/test/active_record_doctor/detectors/undefined_table_references_test.rb +34 -21
- data/test/active_record_doctor/detectors/unindexed_deleted_at_test.rb +118 -8
- data/test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb +56 -4
- data/test/active_record_doctor/runner_test.rb +42 -0
- data/test/generators/active_record_doctor/add_indexes/add_indexes_generator_test.rb +131 -0
- data/test/model_factory.rb +73 -23
- data/test/setup.rb +65 -71
- metadata +43 -7
- data/lib/active_record_doctor/printers/io_printer.rb +0 -133
- data/lib/active_record_doctor/task.rb +0 -28
- data/test/active_record_doctor/printers/io_printer_test.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1e1bf7642b1c7b471fbf78956825067ff070d261d4c47a182d1aaf00f10b98b7
|
4
|
+
data.tar.gz: 6e6fc746327d6db30c282d7964ae778b254107281d9626a251f8593d6ce85db4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0a2d71947728b9d6a130901d94d1ed8c2ede9dde209e91897a8beb8b757da2fea854b89a3a3f6223d658c203e75e687edbee2af37b2203ddb24b28e831439e8
|
7
|
+
data.tar.gz: f0fe87dd6acf379ef946bc513504342c3977bf5c4d7f71eca79290c693b318f0a838c31b95111f68635579cdfa26249d5a95fd456817b6a3c32023c1b4fe0fd1
|
data/README.md
CHANGED
@@ -1,42 +1,149 @@
|
|
1
1
|
# Active Record Doctor
|
2
2
|
|
3
3
|
Active Record Doctor helps to keep the database in a good shape. Currently, it
|
4
|
-
can:
|
4
|
+
can detect:
|
5
|
+
|
6
|
+
* extraneous indexes - [`active_record_doctor:extraneous_indexes`](#removing-extraneous-indexes)
|
7
|
+
* unindexed `deleted_at` columns - [`active_record_doctor:unindexed_deleted_at`](#detecting-unindexed-deleted_at-columns)
|
8
|
+
* missing foreign key constraints - [`active_record_doctor:missing_foreign_keys`](#detecting-missing-foreign-key-constraints)
|
9
|
+
* models referencing undefined tables - [`active_record_doctor:undefined_table_references`](#detecting-models-referencing-undefined-tables)
|
10
|
+
* uniqueness validations not backed by an unique index - [`active_record_doctor:missing_unique_indexes`](#detecting-uniqueness-validations-not-backed-by-an-index)
|
11
|
+
* missing non-`NULL` constraints - [`active_record_doctor:missing_non_null_constraint`](#detecting-missing-non-null-constraints)
|
12
|
+
* missing presence validations - [`active_record_doctor:missing_presence_validation`](#detecting-missing-presence-validations)
|
13
|
+
* incorrect presence validations on boolean columns - [`active_record_doctor:incorrect_boolean_presence_validation`](#detecting-incorrect-presence-validations-on-boolean-columns)
|
14
|
+
* mismatches between model length validations and database validation constraints - [`active_record_doctor:incorrect_length_validation`](#detecting-incorrect-length-validation)
|
15
|
+
* incorrect values of `dependent` on associations - [`active_record_doctor:incorrect_dependent_option`](#detecting-incorrect-dependent-option-on-associations)
|
16
|
+
* primary keys having short integer types - [`active_record_doctor:short_primary_key_type`](#detecting-primary-keys-having-short-integer-types)
|
17
|
+
* mismatched foreign key types - [`active_record_doctor:mismatched_foreign_key_type`](#detecting-mismatched-foreign-key-types)
|
18
|
+
|
19
|
+
It can also:
|
5
20
|
|
6
21
|
* index unindexed foreign keys - [`active_record_doctor:unindexed_foreign_keys`](#indexing-unindexed-foreign-keys)
|
7
|
-
* detect extraneous indexes - [`active_record_doctor:extraneous_indexes`](#removing-extraneous-indexes)
|
8
|
-
* detect unindexed `deleted_at` columns - [`active_record_doctor:unindexed_deleted_at`](#detecting-unindexed-deleted_at-columns)
|
9
|
-
* detect missing foreign key constraints - [`active_record_doctor:missing_foreign_keys`](#detecting-missing-foreign-key-constraints)
|
10
|
-
* detect models referencing undefined tables - [`active_record_doctor:undefined_table_references`](#detecting-models-referencing-undefined-tables)
|
11
|
-
* detect uniqueness validations not backed by an unique index - [`active_record_doctor:missing_unique_indexes`](#detecting-uniqueness-validations-not-backed-by-an-index)
|
12
|
-
* detect missing non-`NULL` constraints - [`active_record_doctor:missing_non_null_constraint`](#detecting-missing-non-null-constraints)
|
13
|
-
* detect missing presence validations - [`active_record_doctor:missing_presence_validation`](#detecting-missing-presence-validations)
|
14
|
-
* detect incorrect presence validations on boolean columns - [`active_record_doctor:incorrect_boolean_presence_validation`](#detecting-incorrect-presence-validations-on-boolean-columns)
|
15
|
-
* detect incorrect values of `dependent` on associations - [`active_record_doctor:incorrect_dependent_option`](#detecting-incorrect-dependent-option-on-associations)
|
16
22
|
|
17
|
-
|
18
|
-
|
19
|
-
Want to suggest a feature? Just shoot me [an email](mailto:contact@gregnavis.com).
|
20
|
-
|
21
|
-
[<img src="https://travis-ci.org/gregnavis/active_record_doctor.svg?branch=master" alt="Build Status" />](https://travis-ci.org/gregnavis/active_record_doctor)
|
23
|
+
[](https://github.com/gregnavis/active_record_doctor/actions/workflows/test.yml)
|
22
24
|
|
23
25
|
## Installation
|
24
26
|
|
25
|
-
|
26
|
-
`Gemfile`:
|
27
|
+
In order to use the latest production release, please add the following to
|
28
|
+
your `Gemfile`:
|
27
29
|
|
28
30
|
```ruby
|
29
31
|
gem 'active_record_doctor', group: :development
|
30
32
|
```
|
31
33
|
|
32
|
-
|
34
|
+
and run `bundle install`. If you'd like to use the most recent development
|
35
|
+
version then use this instead:
|
33
36
|
|
34
|
-
```
|
35
|
-
|
37
|
+
```ruby
|
38
|
+
gem 'active_record_doctor', github: 'gregnavis/active_record_doctor'
|
36
39
|
```
|
37
40
|
|
41
|
+
That's it when it comes to Rails projects. If your project doesn't use Rails
|
42
|
+
then you can use `active_record_doctor` via `Rakefile`.
|
43
|
+
|
44
|
+
### Additional Installation Steps for non-Rails Projects
|
45
|
+
|
46
|
+
If your project uses Rake then you can add the following to `Rakefile` in order
|
47
|
+
to use `active_record_doctor`:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
require "active_record_doctor"
|
51
|
+
|
52
|
+
ActiveRecordDoctor::Rake::Task.new do |task|
|
53
|
+
# Add project-specific Rake dependencies that should be run before running
|
54
|
+
# active_record_doctor.
|
55
|
+
task.deps = []
|
56
|
+
|
57
|
+
# A path to your active_record_doctor configuration file.
|
58
|
+
task.config_path = ::Rails.root.join(".active_record_doctor")
|
59
|
+
|
60
|
+
# A Proc called right before running detectors that should ensure your Active
|
61
|
+
# Record models are preloaded and a database connection is ready.
|
62
|
+
task.setup = -> { ::Rails.application.eager_load! }
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
**IMPORTANT**. `active_record_doctor` expects that after running `deps` and
|
67
|
+
calling `setup` your Active Record models are loaded and a database connection
|
68
|
+
is established.
|
69
|
+
|
38
70
|
## Usage
|
39
71
|
|
72
|
+
`active_record_doctor` can be used via `rake` or `rails`.
|
73
|
+
|
74
|
+
You can run all available detectors via:
|
75
|
+
|
76
|
+
```
|
77
|
+
bundle exec rake active_record_doctor
|
78
|
+
```
|
79
|
+
|
80
|
+
You can run a specific detector via:
|
81
|
+
|
82
|
+
```
|
83
|
+
bundle exec rake active_record_doctor:extraneous_indexes
|
84
|
+
```
|
85
|
+
|
86
|
+
### Continuous Integration
|
87
|
+
|
88
|
+
If you want to use `active_record_doctor` in a Continuous Integration setting
|
89
|
+
then ensure the configuration file is committed and run the tool as one of your
|
90
|
+
build steps -- it returns a non-zero exit status if any errors were reported.
|
91
|
+
|
92
|
+
### Obtaining Help
|
93
|
+
|
94
|
+
If you'd like to obtain help on a specific detector then use the `help`
|
95
|
+
sub-task:
|
96
|
+
|
97
|
+
```
|
98
|
+
bundle exec rake active_record_doctor:extraneous_indexes:help
|
99
|
+
```
|
100
|
+
|
101
|
+
This will show the detector help text in the terminal, along with supported
|
102
|
+
configuration options, their meaning, and whether they're global or local.
|
103
|
+
|
104
|
+
### Configuration
|
105
|
+
|
106
|
+
`active_record_doctor` can be configured to better suit your project's needs.
|
107
|
+
For example, if it complains about a model that you want ignored then you can
|
108
|
+
add that model to the configuration file.
|
109
|
+
|
110
|
+
If you want to use the default configuration then you don't have to do anything.
|
111
|
+
Just run `active_record_doctor` in your project directory.
|
112
|
+
|
113
|
+
If you want to customize the tool you should create a file named
|
114
|
+
`.active_record_doctor` in your project root directory with content like:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
ActiveRecordDoctor.configure do
|
118
|
+
# Global settings affect all detectors.
|
119
|
+
global :ignore_tables, [
|
120
|
+
# Ignore internal Rails-related tables.
|
121
|
+
"ar_internal_metadata",
|
122
|
+
"schema_migrations",
|
123
|
+
"active_storage_blobs",
|
124
|
+
"active_storage_attachments",
|
125
|
+
"action_text_rich_texts",
|
126
|
+
|
127
|
+
# Add project-specific tables here.
|
128
|
+
"legacy_users"
|
129
|
+
]
|
130
|
+
|
131
|
+
# Detector-specific settings affect only one specific detector.
|
132
|
+
detector :extraneous_indexes,
|
133
|
+
ignore_tables: ["users"],
|
134
|
+
ignore_indexes: ["accounts_on_email_organization_id"]
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
138
|
+
The configuration file above will make `active_record_doctor` ignore internal
|
139
|
+
Rails tables (which are ignored by default) and also the `legacy_users` table.
|
140
|
+
It'll also make the `extraneous_indexes` detector skip the `users` table
|
141
|
+
entirely and will not report the index named `accounts_on_email_organization_id`
|
142
|
+
as extraneous.
|
143
|
+
|
144
|
+
Configuration options for each detector are listed below. They can also be
|
145
|
+
obtained via the help mechanism described in the previous section.
|
146
|
+
|
40
147
|
### Indexing Unindexed Foreign Keys
|
41
148
|
|
42
149
|
Foreign keys should be indexed unless it's proven ineffective. However, Rails
|
@@ -66,6 +173,13 @@ three-step process:
|
|
66
173
|
bundle exec rake db:migrate
|
67
174
|
```
|
68
175
|
|
176
|
+
Supported configuration options:
|
177
|
+
|
178
|
+
- `enabled` - set to `false` to disable the detector altogether
|
179
|
+
- `ignore_tables` - tables whose foreign keys should not be checked
|
180
|
+
- `ignore_columns` - columns, written as table.column, that should not be
|
181
|
+
checked.
|
182
|
+
|
69
183
|
### Removing Extraneous Indexes
|
70
184
|
|
71
185
|
Let me illustrate with an example. Consider a `users` table with columns
|
@@ -105,14 +219,25 @@ reported.
|
|
105
219
|
Note that a unique index can _never be replaced by a non-unique one_. For
|
106
220
|
example, if there's a unique index on `users.login` and a non-unique index on
|
107
221
|
`users.login, users.domain` then the tool will _not_ suggest dropping
|
108
|
-
`users.login` as it could violate the uniqueness assumption.
|
222
|
+
`users.login` as it could violate the uniqueness assumption. However, a unique
|
223
|
+
index on `users.login, user.domain` might be replaceable with `users.login` as
|
224
|
+
the uniqueness of the latter implies the uniqueness of the former (if a given
|
225
|
+
`login` can appear only once then it can be present in only one `login, domain`
|
226
|
+
pair).
|
227
|
+
|
228
|
+
Supported configuration options:
|
229
|
+
|
230
|
+
- `enabled` - set to `false` to disable the detector altogether
|
231
|
+
- `ignore_tables` - tables whose indexes should never be reported as extraneous.
|
232
|
+
- `ignore_columns` - indexes that should never be reported as extraneous.
|
109
233
|
|
110
234
|
### Detecting Unindexed `deleted_at` Columns
|
111
235
|
|
112
236
|
If you soft-delete some models (e.g. with `paranoia`) then you need to modify
|
113
237
|
your indexes to include only non-deleted rows. Otherwise they will include
|
114
238
|
logically non-existent rows. This will make them larger and slower to use. Most
|
115
|
-
of the time they should only cover columns satisfying `deleted_at IS NULL
|
239
|
+
of the time they should only cover columns satisfying `deleted_at IS NULL` (to
|
240
|
+
cover existing records) or `deleted_at IS NOT NULL` (to cover deleted records).
|
116
241
|
|
117
242
|
`active_record_doctor` can automatically detect indexes on tables with a
|
118
243
|
`deleted_at` column. Just run:
|
@@ -125,6 +250,16 @@ This will print a list of indexes that don't have the `deleted_at IS NULL`
|
|
125
250
|
clause. Currently, `active_record_doctor` cannot automatically generate
|
126
251
|
appropriate migrations. You need to do that manually.
|
127
252
|
|
253
|
+
Supported configuration options:
|
254
|
+
|
255
|
+
- `enabled` - set to `false` to disable the detector altogether
|
256
|
+
- `ignore_tables` - tables whose indexes should not be checked.
|
257
|
+
- `ignore_columns` - specific columns, written as table.column, that should not
|
258
|
+
be reported as unindexed.
|
259
|
+
- `ignore_indexes` - specific indexes that should not be reported as excluding a
|
260
|
+
timestamp column.
|
261
|
+
- `column_names` - deletion timestamp column names.
|
262
|
+
|
128
263
|
### Detecting Missing Foreign Key Constraints
|
129
264
|
|
130
265
|
If `users.profile_id` references a row in `profiles` then this can be expressed
|
@@ -142,20 +277,8 @@ keys with the following command:
|
|
142
277
|
bundle exec rake active_record_doctor:missing_foreign_keys
|
143
278
|
```
|
144
279
|
|
145
|
-
|
146
|
-
|
147
|
-
```
|
148
|
-
users profile_id
|
149
|
-
comments user_id article_id
|
150
|
-
```
|
151
|
-
|
152
|
-
Tables are listed one per line. Each line starts with a table name followed by
|
153
|
-
column names that should have a foreign key constraint. In the example above,
|
154
|
-
`users.profile_id`, `comments.user_id`, and `comments.article_id` lack a foreign
|
155
|
-
key constraint.
|
156
|
-
|
157
|
-
In order to add a foreign key constraint to `users.profile_id` use the following
|
158
|
-
migration:
|
280
|
+
In order to add a foreign key constraint to `users.profile_id` use a migration
|
281
|
+
like:
|
159
282
|
|
160
283
|
```ruby
|
161
284
|
class AddForeignKeyConstraintToUsersProfileId < ActiveRecord::Migration
|
@@ -165,6 +288,13 @@ class AddForeignKeyConstraintToUsersProfileId < ActiveRecord::Migration
|
|
165
288
|
end
|
166
289
|
```
|
167
290
|
|
291
|
+
Supported configuration options:
|
292
|
+
|
293
|
+
- `enabled` - set to `false` to disable the detector altogether
|
294
|
+
- `ignore_tables` - tables whose columns should not be checked.
|
295
|
+
- `ignore_columns` - columns, written as table.column, that should not be
|
296
|
+
checked.
|
297
|
+
|
168
298
|
### Detecting Models Referencing Undefined Tables
|
169
299
|
|
170
300
|
Active Record guesses the table name based on the class name. There are a few
|
@@ -187,18 +317,23 @@ If there a model references an undefined table then you'll see a message like
|
|
187
317
|
this:
|
188
318
|
|
189
319
|
```
|
190
|
-
|
191
|
-
Contract (the table contract_records is undefined)
|
320
|
+
Contract references a non-existent table or view named contract_records
|
192
321
|
```
|
193
322
|
|
194
323
|
On top of that `rake` will exit with status code of 1. This allows you to use
|
195
324
|
this check as part of your Continuous Integration pipeline.
|
196
325
|
|
326
|
+
Supported configuration options:
|
327
|
+
|
328
|
+
- `enabled` - set to `false` to disable the detector altogether
|
329
|
+
- `ignore_models` - models whose underlying tables should not be checked for
|
330
|
+
existence.
|
331
|
+
|
197
332
|
### Detecting Uniqueness Validations not Backed by an Index
|
198
333
|
|
199
|
-
|
200
|
-
order to be robust. Otherwise you risk inserting
|
201
|
-
load.
|
334
|
+
Model-level uniqueness validations and `has_one` associations should be backed
|
335
|
+
by a database index in order to be robust. Otherwise you risk inserting
|
336
|
+
duplicate values under heavy load.
|
202
337
|
|
203
338
|
In order to detect such validations run:
|
204
339
|
|
@@ -209,16 +344,23 @@ bundle exec rake active_record_doctor:missing_unique_indexes
|
|
209
344
|
If there are such indexes then the command will print:
|
210
345
|
|
211
346
|
```
|
212
|
-
|
213
|
-
users: email
|
347
|
+
add a unique index on users(email) - validating uniqueness in the model without an index can lead to duplicates
|
214
348
|
```
|
215
349
|
|
216
350
|
This means that you should create a unique index on `users.email`.
|
217
351
|
|
352
|
+
Supported configuration options:
|
353
|
+
|
354
|
+
- `enabled` - set to `false` to disable the detector altogether
|
355
|
+
- `ignore_models` - models whose uniqueness validators should not be checked.
|
356
|
+
- `ignore_columns` - specific validators, written as Model(column1, ...), that
|
357
|
+
should not be checked.
|
358
|
+
|
218
359
|
### Detecting Missing Non-`NULL` Constraints
|
219
360
|
|
220
361
|
If there's an unconditional presence validation on a column then it should be
|
221
|
-
marked as non-`NULL`-able at the database level
|
362
|
+
marked as non-`NULL`-able at the database level or should have a `IS NOT NULL`
|
363
|
+
constraint.
|
222
364
|
|
223
365
|
In order to detect columns whose presence is required but that are marked
|
224
366
|
`null: true` in the database run the following command:
|
@@ -230,9 +372,7 @@ bundle exec rake active_record_doctor:missing_non_null_constraint
|
|
230
372
|
The output of the command is similar to:
|
231
373
|
|
232
374
|
```
|
233
|
-
|
234
|
-
users: name
|
235
|
-
|
375
|
+
add `NOT NULL` to users.name - models validates its presence but it's not non-NULL in the database
|
236
376
|
```
|
237
377
|
|
238
378
|
You can mark the columns mentioned in the output as `null: false` by creating a
|
@@ -240,6 +380,13 @@ migration and calling `change_column_null`.
|
|
240
380
|
|
241
381
|
This validator skips models whose corresponding database tables don't exist.
|
242
382
|
|
383
|
+
Supported configuration options:
|
384
|
+
|
385
|
+
- `enabled` - set to `false` to disable the detector altogether
|
386
|
+
- `ignore_tables` - tables whose columns should not be checked.
|
387
|
+
- `ignore_columns` - columns, written as table.column, that should not be
|
388
|
+
checked.
|
389
|
+
|
243
390
|
### Detecting Missing Presence Validations
|
244
391
|
|
245
392
|
If a column is marked as `null: false` then it's likely it should have the
|
@@ -254,14 +401,21 @@ bundle exec rake active_record_doctor:missing_presence_validation
|
|
254
401
|
The output of the command looks like this:
|
255
402
|
|
256
403
|
```
|
257
|
-
|
258
|
-
|
404
|
+
add a `presence` validator to User.email - it's NOT NULL but lacks a validator
|
405
|
+
add a `presence` validator to User.name - it's NOT NULL but lacks a validator
|
259
406
|
```
|
260
407
|
|
261
408
|
This means `User` should have a presence validator on `email` and `name`.
|
262
409
|
|
263
410
|
This validator skips models whose corresponding database tables don't exist.
|
264
411
|
|
412
|
+
Supported configuration options:
|
413
|
+
|
414
|
+
- `enabled` - set to `false` to disable the detector altogether
|
415
|
+
- `ignore_models` - models whose underlying tables' columns should not be checked.
|
416
|
+
- `ignore_attributes` - specific attributes, written as Model.attribute, that
|
417
|
+
should not be checked.
|
418
|
+
|
265
419
|
### Detecting Incorrect Presence Validations on Boolean Columns
|
266
420
|
|
267
421
|
A boolean column's presence should be validated using inclusion or exclusion
|
@@ -276,8 +430,7 @@ bundle exec rake active_record_doctor:incorrect_boolean_presence_validation
|
|
276
430
|
The output of the command looks like this:
|
277
431
|
|
278
432
|
```
|
279
|
-
|
280
|
-
User: active
|
433
|
+
replace the `presence` validator on User.active with `inclusion` - `presence` can't be used on booleans
|
281
434
|
```
|
282
435
|
|
283
436
|
This means `active` is validated with `presence: true` instead of
|
@@ -285,6 +438,46 @@ This means `active` is validated with `presence: true` instead of
|
|
285
438
|
|
286
439
|
This validator skips models whose corresponding database tables don't exist.
|
287
440
|
|
441
|
+
Supported configuration options:
|
442
|
+
|
443
|
+
- `enabled` - set to `false` to disable the detector altogether
|
444
|
+
- `ignore_models` - models whose validators should not be checked.
|
445
|
+
- `ignore_columns` - attributes, written as Model.attribute, whose validators
|
446
|
+
should not be checked.
|
447
|
+
|
448
|
+
### Detecting Incorrect Length Validations
|
449
|
+
|
450
|
+
String length can be enforced by both the database and the application. If
|
451
|
+
there's a database limit then it's a good idea to add a model validation to
|
452
|
+
ensure user-friendly error messages. Similarly, if there's a model validator
|
453
|
+
without the corresponding database constraint then it's a good idea to add one
|
454
|
+
to avoid saving invalid models.
|
455
|
+
|
456
|
+
In order to detect columns whose length isn't validated properly run:
|
457
|
+
|
458
|
+
```
|
459
|
+
bundle exec rake active_record_doctor:incorrect_length_validation
|
460
|
+
```
|
461
|
+
|
462
|
+
The output of the command looks like this:
|
463
|
+
|
464
|
+
```
|
465
|
+
set the maximum length in the validator of User.email (currently 32) and the database limit on users.email (currently 64) to the same value
|
466
|
+
add a length validator on User.address to enforce a maximum length of 64 defined on users.address
|
467
|
+
```
|
468
|
+
|
469
|
+
The first message means the validator on `User.email` is checking for a
|
470
|
+
different maximum than the database limit on `users.email`. The second message
|
471
|
+
means there's a database limit on `users.address` without the corresponding
|
472
|
+
model validation.
|
473
|
+
|
474
|
+
Supported configuration options:
|
475
|
+
|
476
|
+
- `enabled` - set to `false` to disable the detector altogether
|
477
|
+
- `ignore_models` - models whose validators should not be checked.
|
478
|
+
- `ignore_columns` - attributes, written as Model.attribute, whose validators
|
479
|
+
should not be checked.
|
480
|
+
|
288
481
|
### Detecting Incorrect `dependent` Option on Associations
|
289
482
|
|
290
483
|
Cascading model deletions can be sped up with `dependent: :delete_all` (to
|
@@ -308,11 +501,80 @@ bundle exec rake active_record_doctor:incorrect_dependent_option
|
|
308
501
|
The output of the command looks like this:
|
309
502
|
|
310
503
|
```
|
311
|
-
|
312
|
-
|
313
|
-
|
504
|
+
use `dependent: :delete_all` or similar on Company.users - associated models have no validations and can be deleted in bulk
|
505
|
+
use `dependent: :destroy` or similar on Post.comments - the associated model has callbacks that are currently skipped
|
506
|
+
```
|
507
|
+
|
508
|
+
Supported configuration options:
|
509
|
+
|
510
|
+
- `enabled` - set to `false` to disable the detector altogether
|
511
|
+
- `ignore_models` - models whose associations should not be checked.
|
512
|
+
- `ignore_columns` - associations, written as Model.association, that should not
|
513
|
+
be checked.
|
514
|
+
|
515
|
+
### Detecting Primary Keys Having Short Integer Types
|
516
|
+
|
517
|
+
Active Record 5.1 changed the default primary and foreign key type from INTEGER
|
518
|
+
to BIGINT. The reason is to reduce the risk of running out of IDs on inserts.
|
519
|
+
|
520
|
+
In order to detect primary keys using shorter integer types, for example created
|
521
|
+
before migrating to 5.1, you can run the following command:
|
522
|
+
|
523
|
+
```
|
524
|
+
bundle exec rake active_record_doctor:short_primary_key_type
|
525
|
+
```
|
526
|
+
|
527
|
+
The output of the command looks like this:
|
528
|
+
|
529
|
+
```
|
530
|
+
change the type of companies.id to bigint
|
314
531
|
```
|
315
532
|
|
533
|
+
The above means `comanies.id` should be migrated to a wider integer type. An
|
534
|
+
example migration to accomplish this looks likes this:
|
535
|
+
|
536
|
+
```ruby
|
537
|
+
class ChangeCompaniesPrimaryKeyType < ActiveRecord::Migration[5.1]
|
538
|
+
def change
|
539
|
+
change_column :companies, :id, :bigint
|
540
|
+
end
|
541
|
+
end
|
542
|
+
```
|
543
|
+
|
544
|
+
**IMPORTANT**. Running the above migration on a large table can cause downtime
|
545
|
+
as all rows need to be rewritten.
|
546
|
+
|
547
|
+
Supported configuration options:
|
548
|
+
|
549
|
+
- `enabled` - set to `false` to disable the detector altogether
|
550
|
+
- `ignore_tables` - tables whose primary keys should not be checked.
|
551
|
+
|
552
|
+
### Detecting Mismatched Foreign Key Types
|
553
|
+
|
554
|
+
Foreign keys should be of the same type as the referenced primary key.
|
555
|
+
Otherwise, there's a risk of bugs caused by IDs representable by one type but
|
556
|
+
not the other.
|
557
|
+
|
558
|
+
Running the command below will list all foreign keys whose type is different
|
559
|
+
from the referenced primary key:
|
560
|
+
|
561
|
+
```
|
562
|
+
bundle exec rake active_record_doctor:mismatched_foreign_key_type
|
563
|
+
```
|
564
|
+
|
565
|
+
The output of the command looks like this:
|
566
|
+
|
567
|
+
```
|
568
|
+
companies.user_id references a column of different type - foreign keys should be of the same type as the referenced column
|
569
|
+
```
|
570
|
+
|
571
|
+
Supported configuration options:
|
572
|
+
|
573
|
+
- `enabled` - set to `false` to disable the detector altogether
|
574
|
+
- `ignore_tables` - tables whose foreign keys should not be checked.
|
575
|
+
- `ignore_columns` - foreign keys, written as table.column, that should not be
|
576
|
+
checked.
|
577
|
+
|
316
578
|
## Ruby and Rails Compatibility Policy
|
317
579
|
|
318
580
|
The goal of the policy is to ensure proper functioning in reasonable
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
ActiveRecordDoctor.configure do
|
4
|
+
global :ignore_tables, [
|
5
|
+
"ar_internal_metadata",
|
6
|
+
"schema_migrations",
|
7
|
+
"active_storage_blobs",
|
8
|
+
"active_storage_attachments",
|
9
|
+
"action_text_rich_texts"
|
10
|
+
]
|
11
|
+
|
12
|
+
detector :extraneous_indexes,
|
13
|
+
enabled: true,
|
14
|
+
ignore_tables: [],
|
15
|
+
ignore_indexes: []
|
16
|
+
|
17
|
+
detector :incorrect_boolean_presence_validation,
|
18
|
+
enabled: true,
|
19
|
+
ignore_models: [],
|
20
|
+
ignore_attributes: []
|
21
|
+
|
22
|
+
detector :incorrect_length_validation,
|
23
|
+
enabled: true,
|
24
|
+
ignore_models: [],
|
25
|
+
ignore_attributes: []
|
26
|
+
|
27
|
+
detector :incorrect_dependent_option,
|
28
|
+
enabled: true,
|
29
|
+
ignore_models: [],
|
30
|
+
ignore_associations: []
|
31
|
+
|
32
|
+
detector :mismatched_foreign_key_type,
|
33
|
+
enabled: true,
|
34
|
+
ignore_tables: [],
|
35
|
+
ignore_columns: []
|
36
|
+
|
37
|
+
detector :missing_foreign_keys,
|
38
|
+
enabled: true,
|
39
|
+
ignore_tables: [],
|
40
|
+
ignore_columns: []
|
41
|
+
|
42
|
+
detector :missing_non_null_constraint,
|
43
|
+
enabled: true,
|
44
|
+
ignore_tables: [],
|
45
|
+
ignore_columns: []
|
46
|
+
|
47
|
+
detector :missing_presence_validation,
|
48
|
+
enabled: true,
|
49
|
+
ignore_models: [],
|
50
|
+
ignore_attributes: []
|
51
|
+
|
52
|
+
detector :missing_unique_indexes,
|
53
|
+
enabled: true,
|
54
|
+
ignore_models: [],
|
55
|
+
ignore_columns: []
|
56
|
+
|
57
|
+
detector :short_primary_key_type,
|
58
|
+
enabled: true,
|
59
|
+
ignore_tables: []
|
60
|
+
|
61
|
+
detector :undefined_table_references,
|
62
|
+
enabled: true,
|
63
|
+
ignore_models: []
|
64
|
+
|
65
|
+
detector :unindexed_deleted_at,
|
66
|
+
enabled: true,
|
67
|
+
ignore_tables: [],
|
68
|
+
ignore_columns: [],
|
69
|
+
ignore_indexes: [],
|
70
|
+
column_names: ["deleted_at", "discarded_at"]
|
71
|
+
|
72
|
+
detector :unindexed_foreign_keys,
|
73
|
+
enabled: true,
|
74
|
+
ignore_tables: [],
|
75
|
+
ignore_columns: []
|
76
|
+
end
|