active_record_doctor 1.7.2 → 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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -0
  3. data/lib/active_record_doctor.rb +16 -12
  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 +11 -7
  7. data/lib/active_record_doctor/{tasks → detectors}/incorrect_boolean_presence_validation.rb +9 -6
  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 +13 -10
  10. data/lib/active_record_doctor/{tasks → detectors}/missing_non_null_constraint.rb +11 -7
  11. data/lib/active_record_doctor/{tasks → detectors}/missing_presence_validation.rb +11 -8
  12. data/lib/active_record_doctor/{tasks → detectors}/missing_unique_indexes.rb +8 -4
  13. data/lib/active_record_doctor/{tasks → detectors}/undefined_table_references.rb +11 -12
  14. data/lib/active_record_doctor/{tasks → detectors}/unindexed_deleted_at.rb +12 -6
  15. data/lib/active_record_doctor/{tasks → detectors}/unindexed_foreign_keys.rb +13 -10
  16. data/lib/active_record_doctor/printers.rb +3 -1
  17. data/lib/active_record_doctor/printers/io_printer.rb +63 -35
  18. data/lib/active_record_doctor/railtie.rb +2 -0
  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 +25 -25
  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 +14 -9
  34. data/test/model_factory.rb +78 -0
  35. data/test/setup.rb +69 -40
  36. metadata +70 -64
  37. data/lib/active_record_doctor/tasks.rb +0 -10
  38. data/lib/active_record_doctor/tasks/base.rb +0 -86
  39. data/test/active_record_doctor/tasks/extraneous_indexes_test.rb +0 -77
  40. data/test/active_record_doctor/tasks/incorrect_boolean_presence_validation_test.rb +0 -38
  41. data/test/active_record_doctor/tasks/missing_foreign_keys_test.rb +0 -23
  42. data/test/active_record_doctor/tasks/missing_non_null_constraint_test.rb +0 -113
  43. data/test/active_record_doctor/tasks/missing_presence_validation_test.rb +0 -115
  44. data/test/active_record_doctor/tasks/missing_unique_indexes_test.rb +0 -126
  45. data/test/active_record_doctor/tasks/undefined_table_references_test.rb +0 -47
  46. data/test/active_record_doctor/tasks/unindexed_deleted_at_test.rb +0 -59
  47. data/test/active_record_doctor/tasks/unindexed_foreign_keys_test.rb +0 -23
metadata CHANGED
@@ -1,100 +1,100 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_doctor
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.2
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Navis
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-27 00:00:00.000000000 Z
11
+ date: 2021-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: railties
14
+ name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4.2'
19
+ version: 4.2.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '4.2'
26
+ version: 4.2.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: activerecord
28
+ name: minitest-fork_executor
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '4.2'
34
- type: :runtime
33
+ version: 1.0.2
34
+ type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '4.2'
40
+ version: 1.0.2
41
41
  - !ruby/object:Gem::Dependency
42
- name: activesupport
42
+ name: mysql2
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '4.2'
48
- type: :runtime
47
+ version: 0.5.3
48
+ type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '4.2'
54
+ version: 0.5.3
55
55
  - !ruby/object:Gem::Dependency
56
- name: rails
56
+ name: pg
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '4.2'
61
+ version: 1.1.4
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '4.2'
68
+ version: 1.1.4
69
69
  - !ruby/object:Gem::Dependency
70
- name: temping
70
+ name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '3.10'
75
+ version: 12.3.3
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '3.10'
82
+ version: 12.3.3
83
83
  - !ruby/object:Gem::Dependency
84
- name: minitest-fork_executor
84
+ name: rubocop
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '1.0'
89
+ version: 1.14.0
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '1.0'
97
- description:
96
+ version: 1.14.0
97
+ description:
98
98
  email:
99
99
  - contact@gregnavis.com
100
100
  executables: []
@@ -104,40 +104,44 @@ files:
104
104
  - MIT-LICENSE.txt
105
105
  - README.md
106
106
  - lib/active_record_doctor.rb
107
+ - lib/active_record_doctor/detectors.rb
108
+ - lib/active_record_doctor/detectors/base.rb
109
+ - lib/active_record_doctor/detectors/extraneous_indexes.rb
110
+ - lib/active_record_doctor/detectors/incorrect_boolean_presence_validation.rb
111
+ - lib/active_record_doctor/detectors/incorrect_dependent_option.rb
112
+ - lib/active_record_doctor/detectors/missing_foreign_keys.rb
113
+ - lib/active_record_doctor/detectors/missing_non_null_constraint.rb
114
+ - lib/active_record_doctor/detectors/missing_presence_validation.rb
115
+ - lib/active_record_doctor/detectors/missing_unique_indexes.rb
116
+ - lib/active_record_doctor/detectors/undefined_table_references.rb
117
+ - lib/active_record_doctor/detectors/unindexed_deleted_at.rb
118
+ - lib/active_record_doctor/detectors/unindexed_foreign_keys.rb
107
119
  - lib/active_record_doctor/printers.rb
108
120
  - lib/active_record_doctor/printers/io_printer.rb
109
121
  - lib/active_record_doctor/railtie.rb
110
- - lib/active_record_doctor/tasks.rb
111
- - lib/active_record_doctor/tasks/base.rb
112
- - lib/active_record_doctor/tasks/extraneous_indexes.rb
113
- - lib/active_record_doctor/tasks/incorrect_boolean_presence_validation.rb
114
- - lib/active_record_doctor/tasks/missing_foreign_keys.rb
115
- - lib/active_record_doctor/tasks/missing_non_null_constraint.rb
116
- - lib/active_record_doctor/tasks/missing_presence_validation.rb
117
- - lib/active_record_doctor/tasks/missing_unique_indexes.rb
118
- - lib/active_record_doctor/tasks/undefined_table_references.rb
119
- - lib/active_record_doctor/tasks/unindexed_deleted_at.rb
120
- - lib/active_record_doctor/tasks/unindexed_foreign_keys.rb
122
+ - lib/active_record_doctor/task.rb
121
123
  - lib/active_record_doctor/version.rb
122
124
  - lib/generators/active_record_doctor/add_indexes/USAGE
123
125
  - lib/generators/active_record_doctor/add_indexes/add_indexes_generator.rb
124
126
  - lib/tasks/active_record_doctor.rake
127
+ - test/active_record_doctor/detectors/extraneous_indexes_test.rb
128
+ - test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb
129
+ - test/active_record_doctor/detectors/incorrect_dependent_option_test.rb
130
+ - test/active_record_doctor/detectors/missing_foreign_keys_test.rb
131
+ - test/active_record_doctor/detectors/missing_non_null_constraint_test.rb
132
+ - test/active_record_doctor/detectors/missing_presence_validation_test.rb
133
+ - test/active_record_doctor/detectors/missing_unique_indexes_test.rb
134
+ - test/active_record_doctor/detectors/undefined_table_references_test.rb
135
+ - test/active_record_doctor/detectors/unindexed_deleted_at_test.rb
136
+ - test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb
125
137
  - test/active_record_doctor/printers/io_printer_test.rb
126
- - test/active_record_doctor/tasks/extraneous_indexes_test.rb
127
- - test/active_record_doctor/tasks/incorrect_boolean_presence_validation_test.rb
128
- - test/active_record_doctor/tasks/missing_foreign_keys_test.rb
129
- - test/active_record_doctor/tasks/missing_non_null_constraint_test.rb
130
- - test/active_record_doctor/tasks/missing_presence_validation_test.rb
131
- - test/active_record_doctor/tasks/missing_unique_indexes_test.rb
132
- - test/active_record_doctor/tasks/undefined_table_references_test.rb
133
- - test/active_record_doctor/tasks/unindexed_deleted_at_test.rb
134
- - test/active_record_doctor/tasks/unindexed_foreign_keys_test.rb
138
+ - test/model_factory.rb
135
139
  - test/setup.rb
136
140
  homepage: https://github.com/gregnavis/active_record_doctor
137
141
  licenses:
138
142
  - MIT
139
143
  metadata: {}
140
- post_install_message:
144
+ post_install_message:
141
145
  rdoc_options: []
142
146
  require_paths:
143
147
  - lib
@@ -145,26 +149,28 @@ required_ruby_version: !ruby/object:Gem::Requirement
145
149
  requirements:
146
150
  - - ">="
147
151
  - !ruby/object:Gem::Version
148
- version: '0'
152
+ version: 2.1.0
149
153
  required_rubygems_version: !ruby/object:Gem::Requirement
150
154
  requirements:
151
155
  - - ">="
152
156
  - !ruby/object:Gem::Version
153
157
  version: '0'
154
158
  requirements: []
155
- rubygems_version: 3.1.4
156
- signing_key:
159
+ rubygems_version: 3.2.15
160
+ signing_key:
157
161
  specification_version: 4
158
162
  summary: Identify database issues before they hit production.
159
163
  test_files:
160
- - test/setup.rb
164
+ - test/active_record_doctor/detectors/extraneous_indexes_test.rb
165
+ - test/active_record_doctor/detectors/incorrect_boolean_presence_validation_test.rb
166
+ - test/active_record_doctor/detectors/incorrect_dependent_option_test.rb
167
+ - test/active_record_doctor/detectors/missing_foreign_keys_test.rb
168
+ - test/active_record_doctor/detectors/missing_non_null_constraint_test.rb
169
+ - test/active_record_doctor/detectors/missing_presence_validation_test.rb
170
+ - test/active_record_doctor/detectors/missing_unique_indexes_test.rb
171
+ - test/active_record_doctor/detectors/undefined_table_references_test.rb
172
+ - test/active_record_doctor/detectors/unindexed_deleted_at_test.rb
173
+ - test/active_record_doctor/detectors/unindexed_foreign_keys_test.rb
161
174
  - test/active_record_doctor/printers/io_printer_test.rb
162
- - test/active_record_doctor/tasks/incorrect_boolean_presence_validation_test.rb
163
- - test/active_record_doctor/tasks/unindexed_deleted_at_test.rb
164
- - test/active_record_doctor/tasks/extraneous_indexes_test.rb
165
- - test/active_record_doctor/tasks/unindexed_foreign_keys_test.rb
166
- - test/active_record_doctor/tasks/missing_unique_indexes_test.rb
167
- - test/active_record_doctor/tasks/missing_non_null_constraint_test.rb
168
- - test/active_record_doctor/tasks/missing_presence_validation_test.rb
169
- - test/active_record_doctor/tasks/undefined_table_references_test.rb
170
- - test/active_record_doctor/tasks/missing_foreign_keys_test.rb
175
+ - test/model_factory.rb
176
+ - test/setup.rb
@@ -1,10 +0,0 @@
1
- require "active_support"
2
- require "active_support/core_ext/class/subclasses"
3
-
4
- module ActiveRecordDoctor
5
- module Tasks
6
- def self.all
7
- ActiveRecordDoctor::Tasks::Base.subclasses
8
- end
9
- end
10
- end
@@ -1,86 +0,0 @@
1
- require "active_record_doctor/printers/io_printer"
2
-
3
- module ActiveRecordDoctor
4
- module Tasks
5
- class Base
6
- def self.run
7
- new.run
8
- end
9
-
10
- def self.description
11
- @description
12
- end
13
-
14
- def initialize(printer = ActiveRecordDoctor::Printers::IOPrinter.new)
15
- @printer = printer
16
- end
17
-
18
- private
19
-
20
- def success(result)
21
- [result, true]
22
- end
23
-
24
- def connection
25
- @connection ||= ActiveRecord::Base.connection
26
- end
27
-
28
- def indexes(table_name)
29
- connection.indexes(table_name)
30
- end
31
-
32
- def tables
33
- @tables ||=
34
- if ActiveRecord::VERSION::MAJOR == 5
35
- connection.data_sources
36
- else
37
- connection.tables
38
- end
39
- end
40
-
41
- def table_exists?(table_name)
42
- connection.table_exists?(table_name)
43
- end
44
-
45
- def views
46
- @views ||=
47
- if ActiveRecord::VERSION::MAJOR == 5
48
- connection.views
49
- elsif connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
50
- ActiveRecord::Base.connection.execute(<<-SQL).map { |tuple| tuple.fetch("relname") }
51
- SELECT c.relname FROM pg_class c WHERE c.relkind IN ('m', 'v')
52
- SQL
53
- else
54
- # We don't support this Rails/database combination yet.
55
- nil
56
- end
57
- end
58
-
59
- def hash_from_pairs(pairs)
60
- Hash[*pairs.flatten(1)]
61
- end
62
-
63
- def eager_load!
64
- # We call GC.start to make the test suite work. It's (probably) not
65
- # needed for use during development. However, if we remove it then the
66
- # test suite will start accumulating temporary model classes in the
67
- # object space. Running the garbage collector gets rid of them.
68
- GC.start
69
-
70
- Rails.application.eager_load!
71
- end
72
-
73
- def models
74
- descendants(ActiveRecord::Base)
75
- end
76
-
77
- def descendants(superclass)
78
- superclass.descendants
79
- end
80
-
81
- def descendant?(klass, superclass)
82
- !klass.nil? && (klass.superclass == superclass || descendant?(klass.superclass, superclass))
83
- end
84
- end
85
- end
86
- end
@@ -1,77 +0,0 @@
1
- class ActiveRecordDoctor::Tasks::ExtraneousIndexesTest < Minitest::Test
2
- def test_index_on_primary_key_is_duplicate
3
- Temping.create(:user, temporary: false) do
4
- with_columns do |t|
5
- t.index :id
6
- end
7
- end
8
-
9
- assert_result([['index_users_on_id', [:primary_key, 'users']]])
10
- end
11
-
12
- def test_non_unique_version_of_index_is_duplicate
13
- Temping.create(:user, temporary: false) do
14
- with_columns do |t|
15
- t.string :email
16
- t.index :email, name: 'index_users_on_email'
17
- t.index :email, unique: true, name: 'unique_index_on_users_email'
18
- end
19
- end
20
-
21
- assert_result([
22
- ['index_users_on_email', [:multi_column, 'unique_index_on_users_email']]
23
- ])
24
- end
25
-
26
- def test_single_column_covered_by_unique_and_non_unique_multi_column_is_duplicate
27
- Temping.create(:user, temporary: false) do
28
- with_columns do |t|
29
- t.string :first_name
30
- t.string :last_name
31
- t.string :email
32
- t.index [:last_name, :first_name, :email]
33
- t.index [:last_name, :first_name],
34
- unique: true,
35
- name: 'unique_index_on_users_last_name_and_first_name'
36
- t.index :last_name
37
- end
38
- end
39
-
40
- assert_result([
41
- [
42
- 'index_users_on_last_name',
43
- [
44
- :multi_column,
45
- 'index_users_on_last_name_and_first_name_and_email',
46
- 'unique_index_on_users_last_name_and_first_name'
47
- ]
48
- ]
49
- ])
50
- end
51
-
52
- def test_multi_column_covered_by_unique_and_non_unique_multi_column_is_duplicate
53
- Temping.create(:user, temporary: false) do
54
- with_columns do |t|
55
- t.string :first_name
56
- t.string :last_name
57
- t.string :email
58
- t.index [:last_name, :first_name, :email]
59
- t.index [:last_name, :first_name],
60
- unique: true,
61
- name: 'unique_index_on_users_last_name_and_first_name'
62
- t.index [:last_name, :first_name]
63
- end
64
- end
65
-
66
- assert_result([
67
- [
68
- 'index_users_on_last_name_and_first_name',
69
- [
70
- :multi_column,
71
- 'index_users_on_last_name_and_first_name_and_email',
72
- 'unique_index_on_users_last_name_and_first_name'
73
- ]
74
- ]
75
- ])
76
- end
77
- end
@@ -1,38 +0,0 @@
1
- class ActiveRecordDoctor::Tasks::IncorrectBooleanPresenceValidationTest < Minitest::Test
2
- def test_presence_true_is_reported_on_boolean_only
3
- Temping.create(:users, temporary: false) do
4
- # email is a non-boolean column whose presence CAN be validated in the
5
- # usual way. We include it in the test model to ensure the task reports
6
- # only boolean columns.
7
- validates :email, :active, presence: true
8
-
9
- with_columns do |t|
10
- t.string :email, null: false
11
- t.boolean :active, null: false
12
- end
13
- end
14
-
15
- assert_equal({ 'User' => ['active'] }, run_task)
16
- end
17
-
18
- def test_inclusion_is_not_reported
19
- Temping.create(:users, temporary: false) do
20
- validates :active, inclusion: { in: [true, false] }
21
-
22
- with_columns do |t|
23
- t.boolean :active, null: false
24
- end
25
- end
26
-
27
- assert_equal({}, run_task)
28
- end
29
-
30
- def test_models_with_non_existent_tables_are_skipped
31
- klass = Class.new(ActiveRecord::Base) do
32
- self.table_name = 'action_text_rich_texts'
33
- end
34
-
35
- # No need to assert anything as merely not raising an exception is a success.
36
- run_task
37
- end
38
- end