active_record_doctor 1.5.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
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