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.
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