active_record_doctor 1.8.0 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +316 -54
  3. data/lib/active_record_doctor/config/default.rb +76 -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 +142 -21
  7. data/lib/active_record_doctor/detectors/extraneous_indexes.rb +59 -48
  8. data/lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb +31 -23
  9. data/lib/active_record_doctor/detectors/incorrect_dependent_option.rb +102 -35
  10. data/lib/active_record_doctor/detectors/incorrect_length_validation.rb +63 -0
  11. data/lib/active_record_doctor/detectors/mismatched_foreign_key_type.rb +45 -0
  12. data/lib/active_record_doctor/detectors/missing_foreign_keys.rb +32 -23
  13. data/lib/active_record_doctor/detectors/missing_non_null_constraint.rb +41 -28
  14. data/lib/active_record_doctor/detectors/missing_presence_validation.rb +29 -23
  15. data/lib/active_record_doctor/detectors/missing_unique_indexes.rb +92 -32
  16. data/lib/active_record_doctor/detectors/short_primary_key_type.rb +45 -0
  17. data/lib/active_record_doctor/detectors/undefined_table_references.rb +17 -20
  18. data/lib/active_record_doctor/detectors/unindexed_deleted_at.rb +43 -18
  19. data/lib/active_record_doctor/detectors/unindexed_foreign_keys.rb +31 -20
  20. data/lib/active_record_doctor/detectors.rb +12 -4
  21. data/lib/active_record_doctor/errors.rb +226 -0
  22. data/lib/active_record_doctor/help.rb +39 -0
  23. data/lib/active_record_doctor/rake/task.rb +78 -0
  24. data/lib/active_record_doctor/runner.rb +41 -0
  25. data/lib/active_record_doctor/version.rb +1 -1
  26. data/lib/active_record_doctor.rb +8 -3
  27. data/lib/generators/active_record_doctor/add_indexes/add_indexes_generator.rb +34 -21
  28. data/lib/tasks/active_record_doctor.rake +9 -18
  29. data/test/active_record_doctor/config/loader_test.rb +120 -0
  30. data/test/active_record_doctor/config_test.rb +116 -0
  31. data/test/active_record_doctor/detectors/disable_test.rb +30 -0
  32. data/test/active_record_doctor/detectors/extraneous_indexes_test.rb +165 -8
  33. data/test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb +48 -5
  34. data/test/active_record_doctor/detectors/incorrect_dependent_option_test.rb +288 -12
  35. data/test/active_record_doctor/detectors/incorrect_length_validation_test.rb +105 -0
  36. data/test/active_record_doctor/detectors/mismatched_foreign_key_type_test.rb +82 -0
  37. data/test/active_record_doctor/detectors/missing_foreign_keys_test.rb +50 -4
  38. data/test/active_record_doctor/detectors/missing_non_null_constraint_test.rb +172 -24
  39. data/test/active_record_doctor/detectors/missing_presence_validation_test.rb +111 -14
  40. data/test/active_record_doctor/detectors/missing_unique_indexes_test.rb +223 -10
  41. data/test/active_record_doctor/detectors/short_primary_key_type_test.rb +72 -0
  42. data/test/active_record_doctor/detectors/undefined_table_references_test.rb +34 -21
  43. data/test/active_record_doctor/detectors/unindexed_deleted_at_test.rb +118 -8
  44. data/test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb +56 -4
  45. data/test/active_record_doctor/runner_test.rb +42 -0
  46. data/test/generators/active_record_doctor/add_indexes/add_indexes_generator_test.rb +131 -0
  47. data/test/model_factory.rb +73 -23
  48. data/test/setup.rb +65 -71
  49. metadata +43 -7
  50. data/lib/active_record_doctor/printers/io_printer.rb +0 -133
  51. data/lib/active_record_doctor/task.rb +0 -28
  52. 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: 296a8bbb4a326fd78afb08638435754646e22a4416dc18b02dc66f54198a9130
4
- data.tar.gz: 5548b71ee3bb95344e34896a7c972cf39ce33f8a4bd1b03a1a2bf02b4081a1dd
3
+ metadata.gz: 1e1bf7642b1c7b471fbf78956825067ff070d261d4c47a182d1aaf00f10b98b7
4
+ data.tar.gz: 6e6fc746327d6db30c282d7964ae778b254107281d9626a251f8593d6ce85db4
5
5
  SHA512:
6
- metadata.gz: 48c447ca18f35ee44db354414980f54d0ee9416ecc81b6b3fef9401afbaeb44b2951f0fabe43c89027d8783430bdbb6b055ca1000779e37d3729a171649ce795
7
- data.tar.gz: 89ab1d3e4cca4df9c94022e0f11217b41239a3b5f2b3c579d8e466a4fe337909ab48f0b05d28eb5a67fc93afa4afe88a47c7c0ebbd3dc7d45c6d0259db22de40
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
- More features coming soon!
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
+ [![Build Status](https://github.com/gregnavis/active_record_doctor/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/gregnavis/active_record_doctor/actions/workflows/test.yml)
22
24
 
23
25
  ## Installation
24
26
 
25
- The preferred installation method is adding `active_record_doctor` to your
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
- Then run:
34
+ and run `bundle install`. If you'd like to use the most recent development
35
+ version then use this instead:
33
36
 
34
- ```bash
35
- bundle install
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
- The output will look like:
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
- The following models reference undefined tables:
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
- A model-level uniqueness validations should be backed by a database index in
200
- order to be robust. Otherwise you risk inserting duplicate values under heavy
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
- The following indexes should be created to back model-level uniqueness validations:
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
- The following columns should be marked as `null: false`:
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
- The following models and columns should have presence validations:
258
- User: email, name
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
- The presence of the following boolean columns is validated incorrectly:
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
- The following associations might be using invalid dependent settings:
312
- Company: users loads models one-by-one to invoke callbacks even though the related model defines none - consider using `dependent: :delete_all`
313
- Post: comments skips callbacks that are defined on the associated model - consider changing to `dependent: :destroy` or similar
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