active_record_doctor 1.5.0 → 1.8.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 (99) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +152 -12
  3. data/lib/active_record_doctor.rb +20 -2
  4. data/lib/active_record_doctor/detectors.rb +13 -0
  5. data/lib/active_record_doctor/detectors/base.rb +64 -0
  6. data/lib/active_record_doctor/{tasks → detectors}/extraneous_indexes.rb +12 -29
  7. data/lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb +40 -0
  8. data/lib/active_record_doctor/detectors/incorrect_dependent_option.rb +71 -0
  9. data/lib/active_record_doctor/{tasks → detectors}/missing_foreign_keys.rb +17 -35
  10. data/lib/active_record_doctor/detectors/missing_non_null_constraint.rb +60 -0
  11. data/lib/active_record_doctor/detectors/missing_presence_validation.rb +78 -0
  12. data/lib/active_record_doctor/detectors/missing_unique_indexes.rb +61 -0
  13. data/lib/active_record_doctor/detectors/undefined_table_references.rb +34 -0
  14. data/lib/active_record_doctor/detectors/unindexed_deleted_at.rb +29 -0
  15. data/lib/active_record_doctor/detectors/unindexed_foreign_keys.rb +48 -0
  16. data/lib/active_record_doctor/printers.rb +3 -1
  17. data/lib/active_record_doctor/printers/io_printer.rb +101 -26
  18. data/lib/active_record_doctor/railtie.rb +3 -1
  19. data/lib/active_record_doctor/task.rb +28 -0
  20. data/lib/active_record_doctor/version.rb +3 -1
  21. data/lib/generators/active_record_doctor/add_indexes/add_indexes_generator.rb +15 -11
  22. data/lib/tasks/active_record_doctor.rake +33 -0
  23. data/test/active_record_doctor/detectors/extraneous_indexes_test.rb +67 -0
  24. data/test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb +36 -0
  25. data/test/active_record_doctor/detectors/incorrect_dependent_option_test.rb +117 -0
  26. data/test/active_record_doctor/detectors/missing_foreign_keys_test.rb +24 -0
  27. data/test/active_record_doctor/detectors/missing_non_null_constraint_test.rb +102 -0
  28. data/test/active_record_doctor/detectors/missing_presence_validation_test.rb +107 -0
  29. data/test/active_record_doctor/detectors/missing_unique_indexes_test.rb +114 -0
  30. data/test/active_record_doctor/detectors/undefined_table_references_test.rb +44 -0
  31. data/test/active_record_doctor/detectors/unindexed_deleted_at_test.rb +67 -0
  32. data/test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb +26 -0
  33. data/test/active_record_doctor/printers/io_printer_test.rb +23 -10
  34. data/test/model_factory.rb +78 -0
  35. data/test/setup.rb +126 -0
  36. metadata +93 -149
  37. data/Rakefile +0 -28
  38. data/lib/active_record_doctor/compatibility.rb +0 -11
  39. data/lib/active_record_doctor/tasks.rb +0 -4
  40. data/lib/active_record_doctor/tasks/undefined_table_references.rb +0 -34
  41. data/lib/active_record_doctor/tasks/unindexed_deleted_at.rb +0 -40
  42. data/lib/active_record_doctor/tasks/unindexed_foreign_keys.rb +0 -66
  43. data/lib/tasks/active_record_doctor_tasks.rake +0 -27
  44. data/test/active_record_doctor/tasks/extraneous_indexes_test.rb +0 -27
  45. data/test/active_record_doctor/tasks/missing_foreign_keys_test.rb +0 -19
  46. data/test/active_record_doctor/tasks/undefined_table_references_test.rb +0 -19
  47. data/test/active_record_doctor/tasks/unindexed_deleted_at_test.rb +0 -19
  48. data/test/active_record_doctor/tasks/unindexed_foreign_keys_test.rb +0 -19
  49. data/test/dummy/README.rdoc +0 -28
  50. data/test/dummy/Rakefile +0 -6
  51. data/test/dummy/app/assets/javascripts/application.js +0 -13
  52. data/test/dummy/app/assets/stylesheets/application.css +0 -15
  53. data/test/dummy/app/controllers/application_controller.rb +0 -5
  54. data/test/dummy/app/helpers/application_helper.rb +0 -2
  55. data/test/dummy/app/models/application_record.rb +0 -3
  56. data/test/dummy/app/models/comment.rb +0 -3
  57. data/test/dummy/app/models/contract.rb +0 -3
  58. data/test/dummy/app/models/employer.rb +0 -2
  59. data/test/dummy/app/models/profile.rb +0 -2
  60. data/test/dummy/app/models/user.rb +0 -3
  61. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  62. data/test/dummy/bin/bundle +0 -3
  63. data/test/dummy/bin/rails +0 -4
  64. data/test/dummy/bin/rake +0 -4
  65. data/test/dummy/bin/setup +0 -29
  66. data/test/dummy/config.ru +0 -4
  67. data/test/dummy/config/application.rb +0 -23
  68. data/test/dummy/config/boot.rb +0 -5
  69. data/test/dummy/config/database.yml +0 -19
  70. data/test/dummy/config/database.yml.travis +0 -5
  71. data/test/dummy/config/environment.rb +0 -5
  72. data/test/dummy/config/environments/development.rb +0 -41
  73. data/test/dummy/config/environments/production.rb +0 -79
  74. data/test/dummy/config/environments/test.rb +0 -47
  75. data/test/dummy/config/initializers/assets.rb +0 -11
  76. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  77. data/test/dummy/config/initializers/cookies_serializer.rb +0 -3
  78. data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  79. data/test/dummy/config/initializers/inflections.rb +0 -16
  80. data/test/dummy/config/initializers/mime_types.rb +0 -4
  81. data/test/dummy/config/initializers/session_store.rb +0 -3
  82. data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
  83. data/test/dummy/config/locales/en.yml +0 -23
  84. data/test/dummy/config/routes.rb +0 -56
  85. data/test/dummy/config/secrets.yml +0 -22
  86. data/test/dummy/db/migrate/20160213101213_create_employers.rb +0 -15
  87. data/test/dummy/db/migrate/20160213101221_create_users.rb +0 -23
  88. data/test/dummy/db/migrate/20160213101232_create_profiles.rb +0 -15
  89. data/test/dummy/db/migrate/20160604081452_create_comments.rb +0 -11
  90. data/test/dummy/db/migrate/base_migration.rb +0 -5
  91. data/test/dummy/db/schema.rb +0 -68
  92. data/test/dummy/log/development.log +0 -532
  93. data/test/dummy/log/test.log +0 -2699
  94. data/test/dummy/public/404.html +0 -67
  95. data/test/dummy/public/422.html +0 -67
  96. data/test/dummy/public/500.html +0 -66
  97. data/test/dummy/public/favicon.ico +0 -0
  98. data/test/support/spy_printer.rb +0 -52
  99. data/test/test_helper.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: dd6b18b16b7a356e1760befd1d59d7f2b7a77ed3
4
- data.tar.gz: ea1c526b3080d7c8ee5ae4e4eb9a833e3b797cc4
2
+ SHA256:
3
+ metadata.gz: 296a8bbb4a326fd78afb08638435754646e22a4416dc18b02dc66f54198a9130
4
+ data.tar.gz: 5548b71ee3bb95344e34896a7c972cf39ce33f8a4bd1b03a1a2bf02b4081a1dd
5
5
  SHA512:
6
- metadata.gz: 91035e0b5fa47e0d4e5b84a42f53573efc1c333555978a5130ecf05695b12aa17ffcf542b7e0d4c253332ad52b2688f5645e138bd755e3649ea88b0574e41c63
7
- data.tar.gz: 137b3c1a9fd054e0b1af8fd1aad40f5b79ce068b0b0db458ca63d0f64cdf4c23342131a775a5f5148aec34e8c8ff2c32fd1e2a31b1bd86a69a3271c07e34b104
6
+ metadata.gz: 48c447ca18f35ee44db354414980f54d0ee9416ecc81b6b3fef9401afbaeb44b2951f0fabe43c89027d8783430bdbb6b055ca1000779e37d3729a171649ce795
7
+ data.tar.gz: 89ab1d3e4cca4df9c94022e0f11217b41239a3b5f2b3c579d8e466a4fe337909ab48f0b05d28eb5a67fc93afa4afe88a47c7c0ebbd3dc7d45c6d0259db22de40
data/README.md CHANGED
@@ -3,11 +3,16 @@
3
3
  Active Record Doctor helps to keep the database in a good shape. Currently, it
4
4
  can:
5
5
 
6
- * index unindexed foreign keys
7
- * detect extraneous indexes
8
- * detect unindexed `deleted_at` columns
9
- * detect missing foreign key constraints
10
- * detect models referencing undefined tables
6
+ * 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)
11
16
 
12
17
  More features coming soon!
13
18
 
@@ -42,7 +47,7 @@ three-step process:
42
47
  1. Generate a list of unindexed foreign keys by running
43
48
 
44
49
  ```bash
45
- rake active_record_doctor:unindexed_foreign_keys > unindexed_foreign_keys.txt
50
+ bundle exec rake active_record_doctor:unindexed_foreign_keys > unindexed_foreign_keys.txt
46
51
  ```
47
52
 
48
53
  2. Remove columns that should _not_ be indexed from `unindexed_foreign_keys.txt`
@@ -58,7 +63,7 @@ three-step process:
58
63
  4. Run the migrations
59
64
 
60
65
  ```bash
61
- rake db:migrate
66
+ bundle exec rake db:migrate
62
67
  ```
63
68
 
64
69
  ### Removing Extraneous Indexes
@@ -83,7 +88,7 @@ To discover such indexes automatically just follow these steps:
83
88
  1. List extraneous indexes by running:
84
89
 
85
90
  ```bash
86
- rake active_record_doctor:extraneous_indexes
91
+ bundle exec rake active_record_doctor:extraneous_indexes
87
92
  ```
88
93
 
89
94
  2. Confirm that each of the indexes can be indeed dropped.
@@ -113,7 +118,7 @@ of the time they should only cover columns satisfying `deleted_at IS NULL`.
113
118
  `deleted_at` column. Just run:
114
119
 
115
120
  ```
116
- rake active_record_doctor:unindexed_soft_delete
121
+ bundle exec rake active_record_doctor:unindexed_deleted_at
117
122
  ```
118
123
 
119
124
  This will print a list of indexes that don't have the `deleted_at IS NULL`
@@ -134,7 +139,7 @@ add the constraint; for now, it's your job). You can obtain the list of foreign
134
139
  keys with the following command:
135
140
 
136
141
  ```bash
137
- rake active_record_doctor:missing_foreign_keys
142
+ bundle exec rake active_record_doctor:missing_foreign_keys
138
143
  ```
139
144
 
140
145
  The output will look like:
@@ -167,10 +172,15 @@ cases where the name can be wrong (e.g. you forgot to commit a migration or
167
172
  changed the table name). Active Record Doctor can help you identify these cases
168
173
  before they hit production.
169
174
 
170
- The only think you need to do is run:
175
+ **IMPORTANT**. Models backed by views are supported only in:
176
+
177
+ * Rails 5+ and _any_ database or
178
+ * Rails 4.2 with PostgreSQL.
179
+
180
+ The only think you need to do is run:
171
181
 
172
182
  ```
173
- rake active_record_doctor:undefined_table_references
183
+ bundle exec rake active_record_doctor:undefined_table_references
174
184
  ```
175
185
 
176
186
  If there a model references an undefined table then you'll see a message like
@@ -184,6 +194,136 @@ The following models reference undefined tables:
184
194
  On top of that `rake` will exit with status code of 1. This allows you to use
185
195
  this check as part of your Continuous Integration pipeline.
186
196
 
197
+ ### Detecting Uniqueness Validations not Backed by an Index
198
+
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.
202
+
203
+ In order to detect such validations run:
204
+
205
+ ```
206
+ bundle exec rake active_record_doctor:missing_unique_indexes
207
+ ```
208
+
209
+ If there are such indexes then the command will print:
210
+
211
+ ```
212
+ The following indexes should be created to back model-level uniqueness validations:
213
+ users: email
214
+ ```
215
+
216
+ This means that you should create a unique index on `users.email`.
217
+
218
+ ### Detecting Missing Non-`NULL` Constraints
219
+
220
+ If there's an unconditional presence validation on a column then it should be
221
+ marked as non-`NULL`-able at the database level.
222
+
223
+ In order to detect columns whose presence is required but that are marked
224
+ `null: true` in the database run the following command:
225
+
226
+ ```
227
+ bundle exec rake active_record_doctor:missing_non_null_constraint
228
+ ```
229
+
230
+ The output of the command is similar to:
231
+
232
+ ```
233
+ The following columns should be marked as `null: false`:
234
+ users: name
235
+
236
+ ```
237
+
238
+ You can mark the columns mentioned in the output as `null: false` by creating a
239
+ migration and calling `change_column_null`.
240
+
241
+ This validator skips models whose corresponding database tables don't exist.
242
+
243
+ ### Detecting Missing Presence Validations
244
+
245
+ If a column is marked as `null: false` then it's likely it should have the
246
+ corresponding presence validator.
247
+
248
+ In order to detect models lacking these validations run:
249
+
250
+ ```
251
+ bundle exec rake active_record_doctor:missing_presence_validation
252
+ ```
253
+
254
+ The output of the command looks like this:
255
+
256
+ ```
257
+ The following models and columns should have presence validations:
258
+ User: email, name
259
+ ```
260
+
261
+ This means `User` should have a presence validator on `email` and `name`.
262
+
263
+ This validator skips models whose corresponding database tables don't exist.
264
+
265
+ ### Detecting Incorrect Presence Validations on Boolean Columns
266
+
267
+ A boolean column's presence should be validated using inclusion or exclusion
268
+ validators instead of the usual presence validator.
269
+
270
+ In order to detect boolean columns whose presence is validated incorrectly run:
271
+
272
+ ```
273
+ bundle exec rake active_record_doctor:incorrect_boolean_presence_validation
274
+ ```
275
+
276
+ The output of the command looks like this:
277
+
278
+ ```
279
+ The presence of the following boolean columns is validated incorrectly:
280
+ User: active
281
+ ```
282
+
283
+ This means `active` is validated with `presence: true` instead of
284
+ `inclusion: { in: [true, false] }` or `exclusion: { in: [nil] }`.
285
+
286
+ This validator skips models whose corresponding database tables don't exist.
287
+
288
+ ### Detecting Incorrect `dependent` Option on Associations
289
+
290
+ Cascading model deletions can be sped up with `dependent: :delete_all` (to
291
+ delete all dependent models with one SQL query) but only if the deleted models
292
+ have no callbacks as they're skipped.
293
+
294
+ This can lead to two types of errors:
295
+
296
+ - Using `delete_all` when dependent models define callbacks - they will NOT be
297
+ invoked.
298
+ - Using `destroy` when dependent models define no callbacks - dependent models
299
+ will be loaded one-by-one with no reason
300
+
301
+ In order to detect associations affected by the two aforementioned problems run
302
+ the following command:
303
+
304
+ ```
305
+ bundle exec rake active_record_doctor:incorrect_dependent_option
306
+ ```
307
+
308
+ The output of the command looks like this:
309
+
310
+ ```
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
314
+ ```
315
+
316
+ ## Ruby and Rails Compatibility Policy
317
+
318
+ The goal of the policy is to ensure proper functioning in reasonable
319
+ combinations of Ruby and Rails versions. Specifically:
320
+
321
+ 1. If a Rails version is officially supported by the Rails Core Team then it's
322
+ supported by `active_record_doctor`.
323
+ 2. If a Ruby version is compatible with a supported Rails version then it's
324
+ also supported by `active_record_doctor`.
325
+ 3. Only most recent teeny Ruby versions and patch Rails versions are supported.
326
+
187
327
  ## Author
188
328
 
189
329
  This gem is developed and maintained by [Greg Navis](http://www.gregnavis.com).
@@ -1,4 +1,22 @@
1
- require "active_record_doctor/railtie" if defined?(Rails)
1
+ # frozen_string_literal: true
2
2
 
3
- module ActiveRecordDoctor
3
+ require "active_record_doctor/printers"
4
+ require "active_record_doctor/printers/io_printer"
5
+ require "active_record_doctor/railtie" if defined?(Rails) && defined?(Rails::Railtie)
6
+ require "active_record_doctor/detectors"
7
+ require "active_record_doctor/detectors/base"
8
+ require "active_record_doctor/detectors/missing_presence_validation"
9
+ require "active_record_doctor/detectors/missing_foreign_keys"
10
+ require "active_record_doctor/detectors/missing_unique_indexes"
11
+ require "active_record_doctor/detectors/incorrect_boolean_presence_validation"
12
+ require "active_record_doctor/detectors/extraneous_indexes"
13
+ require "active_record_doctor/detectors/unindexed_deleted_at"
14
+ require "active_record_doctor/detectors/undefined_table_references"
15
+ require "active_record_doctor/detectors/missing_non_null_constraint"
16
+ require "active_record_doctor/detectors/unindexed_foreign_keys"
17
+ require "active_record_doctor/detectors/incorrect_dependent_option"
18
+ require "active_record_doctor/task"
19
+ require "active_record_doctor/version"
20
+
21
+ module ActiveRecordDoctor # :nodoc:
4
22
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/core_ext/class/subclasses"
5
+
6
+ module ActiveRecordDoctor
7
+ # Container module for all detectors, implemented as separate classes.
8
+ module Detectors
9
+ def self.all
10
+ ActiveRecordDoctor::Detectors::Base.subclasses
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordDoctor
4
+ module Detectors
5
+ # Base class for all active_record_doctor detectors.
6
+ class Base
7
+ class << self
8
+ attr_reader :description
9
+
10
+ def run
11
+ new.run
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def problems(problems, options = {})
18
+ [problems, options]
19
+ end
20
+
21
+ def connection
22
+ @connection ||= ActiveRecord::Base.connection
23
+ end
24
+
25
+ def indexes(table_name)
26
+ connection.indexes(table_name)
27
+ end
28
+
29
+ def tables
30
+ connection.tables
31
+ end
32
+
33
+ def table_exists?(table_name)
34
+ connection.table_exists?(table_name)
35
+ end
36
+
37
+ def views
38
+ @views ||=
39
+ if connection.respond_to?(:views)
40
+ connection.views
41
+ elsif connection.adapter_name == "PostgreSQL"
42
+ ActiveRecord::Base.connection.execute(<<-SQL).map { |tuple| tuple.fetch("relname") }
43
+ SELECT c.relname FROM pg_class c WHERE c.relkind IN ('m', 'v')
44
+ SQL
45
+ else # rubocop:disable Style/EmptyElse
46
+ # We don't support this Rails/database combination yet.
47
+ nil
48
+ end
49
+ end
50
+
51
+ def hash_from_pairs(pairs)
52
+ Hash[*pairs.flatten(1)]
53
+ end
54
+
55
+ def eager_load!
56
+ Rails.application.eager_load!
57
+ end
58
+
59
+ def models
60
+ ActiveRecord::Base.descendants
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,32 +1,23 @@
1
- require "active_record_doctor/compatibility"
2
- require "active_record_doctor/printers/io_printer"
1
+ # frozen_string_literal: true
3
2
 
4
- module ActiveRecordDoctor
5
- module Tasks
6
- class ExtraneousIndexes
7
- include Compatibility
8
-
9
- def self.run
10
- new.run
11
- end
3
+ require "active_record_doctor/detectors/base"
12
4
 
13
- def initialize(printer: ActiveRecordDoctor::Printers::IOPrinter.new)
14
- @printer = printer
15
- end
5
+ module ActiveRecordDoctor
6
+ module Detectors
7
+ # Detect indexes whose function can be overtaken by other indexes. For example, an index on columns A, B, and C
8
+ # can also serve as an index on A and A, B.
9
+ class ExtraneousIndexes < Base
10
+ @description = "Detect extraneous indexes"
16
11
 
17
12
  def run
18
- @printer.print_extraneous_indexes(extraneous_indexes)
13
+ problems(subindexes_of_multi_column_indexes + indexed_primary_keys)
19
14
  end
20
15
 
21
16
  private
22
17
 
23
- def extraneous_indexes
24
- subindexes_of_multi_column_indexes + indexed_primary_keys
25
- end
26
-
27
18
  def subindexes_of_multi_column_indexes
28
19
  tables.reject do |table|
29
- "schema_migrations" == table
20
+ table == "schema_migrations"
30
21
  end.flat_map do |table|
31
22
  indexes = indexes(table)
32
23
  maximum_indexes = indexes.select do |index|
@@ -51,7 +42,7 @@ module ActiveRecordDoctor
51
42
 
52
43
  def indexed_primary_keys
53
44
  @indexed_primary_keys ||= tables.reject do |table|
54
- "schema_migrations" == table
45
+ table == "schema_migrations"
55
46
  end.map do |table|
56
47
  [
57
48
  table,
@@ -90,15 +81,7 @@ module ActiveRecordDoctor
90
81
  end
91
82
 
92
83
  def indexes(table_name)
93
- @connection.indexes(table_name)
94
- end
95
-
96
- def tables
97
- @tables ||= connection_tables
98
- end
99
-
100
- def connection
101
- @connection ||= ActiveRecord::Base.connection
84
+ super.select { |index| index.columns.is_a?(Array) }
102
85
  end
103
86
  end
104
87
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record_doctor/detectors/base"
4
+
5
+ module ActiveRecordDoctor
6
+ module Detectors
7
+ # Find instances of boolean column presence validations that use presence/absence instead of includes/excludes.
8
+ class IncorrectBooleanPresenceValidation < Base
9
+ @description = "Detect boolean columns with presence/absence instead of includes/excludes validators"
10
+
11
+ def run
12
+ eager_load!
13
+
14
+ problems(hash_from_pairs(models.reject do |model|
15
+ model.table_name.nil? ||
16
+ model.table_name == "schema_migrations" ||
17
+ !table_exists?(model.table_name)
18
+ end.map do |model|
19
+ [
20
+ model.name,
21
+ connection.columns(model.table_name).select do |column|
22
+ column.type == :boolean &&
23
+ has_presence_validator?(model, column)
24
+ end.map(&:name)
25
+ ]
26
+ end.reject do |_model_name, columns|
27
+ columns.empty?
28
+ end))
29
+ end
30
+
31
+ private
32
+
33
+ def has_presence_validator?(model, column)
34
+ model.validators.any? do |validator|
35
+ validator.kind == :presence && validator.attributes.include?(column.name.to_sym)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end