active_record_doctor 1.4.1 → 1.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +140 -10
- data/lib/active_record_doctor.rb +15 -1
- data/lib/active_record_doctor/printers/io_printer.rb +63 -7
- data/lib/active_record_doctor/railtie.rb +1 -1
- data/lib/active_record_doctor/tasks.rb +6 -0
- data/lib/active_record_doctor/tasks/base.rb +86 -0
- data/lib/active_record_doctor/tasks/extraneous_indexes.rb +5 -26
- data/lib/active_record_doctor/tasks/incorrect_boolean_presence_validation.rb +37 -0
- data/lib/active_record_doctor/tasks/missing_foreign_keys.rb +7 -28
- data/lib/active_record_doctor/tasks/missing_non_null_constraint.rb +56 -0
- data/lib/active_record_doctor/tasks/missing_presence_validation.rb +75 -0
- data/lib/active_record_doctor/tasks/missing_unique_indexes.rb +57 -0
- data/lib/active_record_doctor/tasks/undefined_table_references.rb +22 -21
- data/lib/active_record_doctor/tasks/unindexed_deleted_at.rb +23 -0
- data/lib/active_record_doctor/tasks/unindexed_foreign_keys.rb +7 -28
- data/lib/active_record_doctor/version.rb +1 -1
- data/lib/tasks/active_record_doctor.rake +33 -0
- data/test/active_record_doctor/printers/io_printer_test.rb +15 -7
- data/test/active_record_doctor/tasks/extraneous_indexes_test.rb +69 -19
- data/test/active_record_doctor/tasks/incorrect_boolean_presence_validation_test.rb +38 -0
- data/test/active_record_doctor/tasks/missing_foreign_keys_test.rb +17 -13
- data/test/active_record_doctor/tasks/missing_non_null_constraint_test.rb +113 -0
- data/test/active_record_doctor/tasks/missing_presence_validation_test.rb +115 -0
- data/test/active_record_doctor/tasks/missing_unique_indexes_test.rb +126 -0
- data/test/active_record_doctor/tasks/undefined_table_references_test.rb +39 -11
- data/test/active_record_doctor/tasks/unindexed_deleted_at_test.rb +59 -0
- data/test/active_record_doctor/tasks/unindexed_foreign_keys_test.rb +17 -13
- data/test/setup.rb +97 -0
- metadata +58 -117
- data/Rakefile +0 -28
- data/lib/active_record_doctor/compatibility.rb +0 -11
- data/lib/tasks/active_record_doctor_tasks.rake +0 -22
- data/test/dummy/README.rdoc +0 -28
- data/test/dummy/Rakefile +0 -6
- data/test/dummy/app/assets/javascripts/application.js +0 -13
- data/test/dummy/app/assets/stylesheets/application.css +0 -15
- data/test/dummy/app/controllers/application_controller.rb +0 -5
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/models/application_record.rb +0 -3
- data/test/dummy/app/models/comment.rb +0 -3
- data/test/dummy/app/models/contract.rb +0 -3
- data/test/dummy/app/models/employer.rb +0 -2
- data/test/dummy/app/models/profile.rb +0 -2
- data/test/dummy/app/models/user.rb +0 -3
- data/test/dummy/app/views/layouts/application.html.erb +0 -14
- data/test/dummy/bin/bundle +0 -3
- data/test/dummy/bin/rails +0 -4
- data/test/dummy/bin/rake +0 -4
- data/test/dummy/bin/setup +0 -29
- data/test/dummy/config.ru +0 -4
- data/test/dummy/config/application.rb +0 -23
- data/test/dummy/config/boot.rb +0 -5
- data/test/dummy/config/database.yml +0 -19
- data/test/dummy/config/database.yml.travis +0 -5
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -41
- data/test/dummy/config/environments/production.rb +0 -79
- data/test/dummy/config/environments/test.rb +0 -47
- data/test/dummy/config/initializers/assets.rb +0 -11
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/test/dummy/config/initializers/cookies_serializer.rb +0 -3
- data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
- data/test/dummy/config/initializers/inflections.rb +0 -16
- data/test/dummy/config/initializers/mime_types.rb +0 -4
- data/test/dummy/config/initializers/session_store.rb +0 -3
- data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/test/dummy/config/locales/en.yml +0 -23
- data/test/dummy/config/routes.rb +0 -56
- data/test/dummy/config/secrets.yml +0 -22
- data/test/dummy/db/migrate/20160213101213_create_employers.rb +0 -13
- data/test/dummy/db/migrate/20160213101221_create_users.rb +0 -23
- data/test/dummy/db/migrate/20160213101232_create_profiles.rb +0 -12
- data/test/dummy/db/migrate/20160604081452_create_comments.rb +0 -11
- data/test/dummy/db/migrate/base_migration.rb +0 -5
- data/test/dummy/db/schema.rb +0 -63
- data/test/dummy/log/development.log +0 -136
- data/test/dummy/log/test.log +0 -1720
- data/test/dummy/public/404.html +0 -67
- data/test/dummy/public/422.html +0 -67
- data/test/dummy/public/500.html +0 -66
- data/test/dummy/public/favicon.ico +0 -0
- data/test/support/spy_printer.rb +0 -43
- data/test/test_helper.rb +0 -20
@@ -1,29 +1,16 @@
|
|
1
|
-
require "active_record_doctor/
|
2
|
-
require "active_record_doctor/printers/io_printer"
|
1
|
+
require "active_record_doctor/tasks/base"
|
3
2
|
|
4
3
|
module ActiveRecordDoctor
|
5
4
|
module Tasks
|
6
|
-
class ExtraneousIndexes
|
7
|
-
|
8
|
-
|
9
|
-
def self.run
|
10
|
-
new.run
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(printer: ActiveRecordDoctor::Printers::IOPrinter.new)
|
14
|
-
@printer = printer
|
15
|
-
end
|
5
|
+
class ExtraneousIndexes < Base
|
6
|
+
@description = 'Detect extraneous indexes'
|
16
7
|
|
17
8
|
def run
|
18
|
-
|
9
|
+
success(subindexes_of_multi_column_indexes + indexed_primary_keys)
|
19
10
|
end
|
20
11
|
|
21
12
|
private
|
22
13
|
|
23
|
-
def extraneous_indexes
|
24
|
-
subindexes_of_multi_column_indexes + indexed_primary_keys
|
25
|
-
end
|
26
|
-
|
27
14
|
def subindexes_of_multi_column_indexes
|
28
15
|
tables.reject do |table|
|
29
16
|
"schema_migrations" == table
|
@@ -90,15 +77,7 @@ module ActiveRecordDoctor
|
|
90
77
|
end
|
91
78
|
|
92
79
|
def indexes(table_name)
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
def tables
|
97
|
-
@tables ||= connection_tables
|
98
|
-
end
|
99
|
-
|
100
|
-
def connection
|
101
|
-
@connection ||= ActiveRecord::Base.connection
|
80
|
+
super.select { |index| index.columns.kind_of?(Array) }
|
102
81
|
end
|
103
82
|
end
|
104
83
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "active_record_doctor/tasks/base"
|
2
|
+
|
3
|
+
module ActiveRecordDoctor
|
4
|
+
module Tasks
|
5
|
+
class IncorrectBooleanPresenceValidation < Base
|
6
|
+
@description = 'Detect boolean columns with presence/absence instead of includes/excludes validators'
|
7
|
+
|
8
|
+
def run
|
9
|
+
eager_load!
|
10
|
+
|
11
|
+
success(hash_from_pairs(models.reject do |model|
|
12
|
+
model.table_name.nil? ||
|
13
|
+
model.table_name == 'schema_migrations' ||
|
14
|
+
!table_exists?(model.table_name)
|
15
|
+
end.map do |model|
|
16
|
+
[
|
17
|
+
model.name,
|
18
|
+
connection.columns(model.table_name).select do |column|
|
19
|
+
column.type == :boolean &&
|
20
|
+
has_presence_validator?(model, column)
|
21
|
+
end.map(&:name)
|
22
|
+
]
|
23
|
+
end.reject do |model_name, columns|
|
24
|
+
columns.empty?
|
25
|
+
end))
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def has_presence_validator?(model, column)
|
31
|
+
model.validators.any? do |validator|
|
32
|
+
validator.kind == :presence && validator.attributes.include?(column.name.to_sym)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,27 +1,12 @@
|
|
1
|
-
require "active_record_doctor/
|
2
|
-
require "active_record_doctor/printers/io_printer"
|
1
|
+
require "active_record_doctor/tasks/base"
|
3
2
|
|
4
3
|
module ActiveRecordDoctor
|
5
4
|
module Tasks
|
6
|
-
class MissingForeignKeys
|
7
|
-
|
8
|
-
|
9
|
-
def self.run
|
10
|
-
new.run
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(printer: ActiveRecordDoctor::Printers::IOPrinter.new)
|
14
|
-
@printer = printer
|
15
|
-
end
|
5
|
+
class MissingForeignKeys < Base
|
6
|
+
@description = 'Detect association columns without a foreign key constraint'
|
16
7
|
|
17
8
|
def run
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def missing_foreign_keys
|
24
|
-
hash_from_pairs(connection_tables.select do |table|
|
9
|
+
success(hash_from_pairs(tables.select do |table|
|
25
10
|
"schema_migrations" != table
|
26
11
|
end.map do |table|
|
27
12
|
[
|
@@ -37,9 +22,11 @@ module ActiveRecordDoctor
|
|
37
22
|
]
|
38
23
|
end.select do |table, columns|
|
39
24
|
!columns.empty?
|
40
|
-
end)
|
25
|
+
end))
|
41
26
|
end
|
42
27
|
|
28
|
+
private
|
29
|
+
|
43
30
|
def id?(table, column)
|
44
31
|
column.name.end_with?("_id")
|
45
32
|
end
|
@@ -56,14 +43,6 @@ module ActiveRecordDoctor
|
|
56
43
|
another_column.name == type_column_name
|
57
44
|
end
|
58
45
|
end
|
59
|
-
|
60
|
-
def connection
|
61
|
-
@connection ||= ActiveRecord::Base.connection
|
62
|
-
end
|
63
|
-
|
64
|
-
def hash_from_pairs(pairs)
|
65
|
-
Hash[*pairs.flatten(1)]
|
66
|
-
end
|
67
46
|
end
|
68
47
|
end
|
69
48
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "active_record_doctor/tasks/base"
|
2
|
+
|
3
|
+
module ActiveRecordDoctor
|
4
|
+
module Tasks
|
5
|
+
class MissingNonNullConstraint < Base
|
6
|
+
@description = 'Detect presence validators not backed by a non-NULL constraint'
|
7
|
+
|
8
|
+
def run
|
9
|
+
eager_load!
|
10
|
+
|
11
|
+
success(hash_from_pairs(models.reject do |model|
|
12
|
+
model.table_name.nil? ||
|
13
|
+
model.table_name == 'schema_migrations' ||
|
14
|
+
!table_exists?(model.table_name)
|
15
|
+
end.map do |model|
|
16
|
+
[
|
17
|
+
model.table_name,
|
18
|
+
connection.columns(model.table_name).select do |column|
|
19
|
+
validator_needed?(model, column) &&
|
20
|
+
has_mandatory_presence_validator?(model, column) &&
|
21
|
+
column.null
|
22
|
+
end.map(&:name)
|
23
|
+
]
|
24
|
+
end.reject do |model_name, columns|
|
25
|
+
columns.empty?
|
26
|
+
end))
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def validator_needed?(model, column)
|
32
|
+
![model.primary_key, 'created_at', 'updated_at'].include?(column.name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def has_mandatory_presence_validator?(model, column)
|
36
|
+
# A foreign key can be validates via the column name (e.g. company_id)
|
37
|
+
# or the association name (e.g. company). We collect the allowed names
|
38
|
+
# in an array to check for their presence in the validator definition
|
39
|
+
# in one go.
|
40
|
+
attribute_name_forms = [column.name.to_sym]
|
41
|
+
belongs_to = model.reflect_on_all_associations(:belongs_to).find do |reflection|
|
42
|
+
reflection.foreign_key == column.name
|
43
|
+
end
|
44
|
+
attribute_name_forms << belongs_to.name.to_sym if belongs_to
|
45
|
+
|
46
|
+
model.validators.any? do |validator|
|
47
|
+
validator.is_a?(ActiveRecord::Validations::PresenceValidator) &&
|
48
|
+
(validator.attributes & attribute_name_forms).present? &&
|
49
|
+
!validator.options[:allow_nil] &&
|
50
|
+
!validator.options[:if] &&
|
51
|
+
!validator.options[:unless]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "active_record_doctor/tasks/base"
|
2
|
+
|
3
|
+
module ActiveRecordDoctor
|
4
|
+
module Tasks
|
5
|
+
class MissingPresenceValidation < Base
|
6
|
+
@description = 'Detect non-NULL columns without a presence validator'
|
7
|
+
|
8
|
+
def run
|
9
|
+
eager_load!
|
10
|
+
|
11
|
+
success(hash_from_pairs(models.reject do |model|
|
12
|
+
model.table_name.nil? ||
|
13
|
+
model.table_name == 'schema_migrations' ||
|
14
|
+
!table_exists?(model.table_name)
|
15
|
+
end.map do |model|
|
16
|
+
[
|
17
|
+
model.name,
|
18
|
+
connection.columns(model.table_name).select do |column|
|
19
|
+
validator_needed?(model, column) &&
|
20
|
+
!validator_present?(model, column)
|
21
|
+
end.map(&:name)
|
22
|
+
]
|
23
|
+
end.reject do |model_name, columns|
|
24
|
+
columns.empty?
|
25
|
+
end))
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def validator_needed?(model, column)
|
31
|
+
![model.primary_key, 'created_at', 'updated_at'].include?(column.name) &&
|
32
|
+
!column.null
|
33
|
+
end
|
34
|
+
|
35
|
+
def validator_present?(model, column)
|
36
|
+
if column.type == :boolean
|
37
|
+
inclusion_validator_present?(model, column) ||
|
38
|
+
exclusion_validator_present?(model, column)
|
39
|
+
else
|
40
|
+
presence_validator_present?(model, column)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def inclusion_validator_present?(model, column)
|
45
|
+
model.validators.any? do |validator|
|
46
|
+
validator.is_a?(ActiveModel::Validations::InclusionValidator) &&
|
47
|
+
validator.attributes.include?(column.name.to_sym) &&
|
48
|
+
!validator.options.fetch(:in, []).include?(nil)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def exclusion_validator_present?(model, column)
|
53
|
+
model.validators.any? do |validator|
|
54
|
+
validator.is_a?(ActiveModel::Validations::ExclusionValidator) &&
|
55
|
+
validator.attributes.include?(column.name.to_sym) &&
|
56
|
+
validator.options.fetch(:in, []).include?(nil)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def presence_validator_present?(model, column)
|
61
|
+
allowed_attributes = [column.name.to_sym]
|
62
|
+
|
63
|
+
belongs_to = model.reflect_on_all_associations(:belongs_to).find do |reflection|
|
64
|
+
reflection.foreign_key == column.name
|
65
|
+
end
|
66
|
+
allowed_attributes << belongs_to.name.to_sym if belongs_to
|
67
|
+
|
68
|
+
model.validators.any? do |validator|
|
69
|
+
validator.is_a?(ActiveRecord::Validations::PresenceValidator) &&
|
70
|
+
(validator.attributes & allowed_attributes).present?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "active_record_doctor/tasks/base"
|
2
|
+
|
3
|
+
module ActiveRecordDoctor
|
4
|
+
module Tasks
|
5
|
+
class MissingUniqueIndexes < Base
|
6
|
+
@description = 'Detect columns covered by a uniqueness validator without a unique index'
|
7
|
+
|
8
|
+
def run
|
9
|
+
eager_load!
|
10
|
+
|
11
|
+
success(hash_from_pairs(models.reject do |model|
|
12
|
+
model.table_name.nil?
|
13
|
+
end.map do |model|
|
14
|
+
[
|
15
|
+
model.table_name,
|
16
|
+
model.validators.select do |validator|
|
17
|
+
table_name = model.table_name
|
18
|
+
scope = validator.options.fetch(:scope, [])
|
19
|
+
|
20
|
+
validator.is_a?(ActiveRecord::Validations::UniquenessValidator) &&
|
21
|
+
supported_validator?(validator) &&
|
22
|
+
!unique_index?(table_name, validator.attributes, scope)
|
23
|
+
end.map do |validator|
|
24
|
+
scope = Array(validator.options.fetch(:scope, []))
|
25
|
+
attributes = validator.attributes
|
26
|
+
(scope + attributes).map(&:to_s)
|
27
|
+
end
|
28
|
+
]
|
29
|
+
end.reject do |_table_name, indexes|
|
30
|
+
indexes.empty?
|
31
|
+
end))
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def supported_validator?(validator)
|
37
|
+
validator.options[:if].nil? &&
|
38
|
+
validator.options[:unless].nil? &&
|
39
|
+
validator.options[:conditions].nil? &&
|
40
|
+
|
41
|
+
# In Rails 6, default option values are no longer explicitly set on
|
42
|
+
# options so if the key is absent we must fetch the default value
|
43
|
+
# ourselves. case_sensitive is the default in 4.2+ so it's safe to
|
44
|
+
# put true literally.
|
45
|
+
validator.options.fetch(:case_sensitive, true)
|
46
|
+
end
|
47
|
+
|
48
|
+
def unique_index?(table_name, columns, scope)
|
49
|
+
columns = (Array(scope) + columns).map(&:to_s)
|
50
|
+
|
51
|
+
indexes(table_name).any? do |index|
|
52
|
+
index.columns.to_set == columns.to_set && index.unique
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,33 +1,34 @@
|
|
1
|
-
require "active_record_doctor/
|
2
|
-
require "active_record_doctor/printers/io_printer"
|
1
|
+
require "active_record_doctor/tasks/base"
|
3
2
|
|
4
3
|
module ActiveRecordDoctor
|
5
4
|
module Tasks
|
6
|
-
class UndefinedTableReferences
|
7
|
-
|
8
|
-
|
9
|
-
def self.run
|
10
|
-
new.run
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(printer: ActiveRecordDoctor::Printers::IOPrinter.new)
|
14
|
-
@printer = printer
|
15
|
-
end
|
5
|
+
class UndefinedTableReferences < Base
|
6
|
+
@description = 'Detect models referencing undefined tables or views'
|
16
7
|
|
17
8
|
def run
|
18
|
-
|
19
|
-
undefined_table_references.present? ? 1 : 0
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
9
|
+
eager_load!
|
23
10
|
|
24
|
-
|
25
|
-
|
11
|
+
# If we can't list views due to old Rails version or unsupported
|
12
|
+
# database then existing_views is nil. We inform the caller we haven't
|
13
|
+
# consulted views so that it can display an appropriate warning.
|
14
|
+
existing_views = views
|
26
15
|
|
27
|
-
|
16
|
+
offending_models = models.select do |model|
|
28
17
|
model.table_name.present? &&
|
29
|
-
!
|
18
|
+
!tables.include?(model.table_name) &&
|
19
|
+
existing_views &&
|
20
|
+
!existing_views.include?(model.table_name)
|
21
|
+
end.map do |model|
|
22
|
+
[model.name, model.table_name]
|
30
23
|
end
|
24
|
+
|
25
|
+
[
|
26
|
+
[
|
27
|
+
offending_models, # Actual results
|
28
|
+
!existing_views.nil? # true if views were checked, false otherwise
|
29
|
+
],
|
30
|
+
offending_models.blank?
|
31
|
+
]
|
31
32
|
end
|
32
33
|
end
|
33
34
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "active_record_doctor/tasks/base"
|
2
|
+
|
3
|
+
module ActiveRecordDoctor
|
4
|
+
module Tasks
|
5
|
+
class UnindexedDeletedAt < Base
|
6
|
+
COLUMNS = %w[deleted_at discarded_at].freeze
|
7
|
+
PATTERN = COLUMNS.join('|').freeze
|
8
|
+
@description = 'Detect unindexed deleted_at columns'
|
9
|
+
|
10
|
+
def run
|
11
|
+
success(connection.tables.select do |table|
|
12
|
+
connection.columns(table).any? { |column| column.name =~ /^#{PATTERN}$/ }
|
13
|
+
end.flat_map do |table|
|
14
|
+
connection.indexes(table).reject do |index|
|
15
|
+
index.where =~ /\b#{PATTERN}\s+IS\s+NULL\b/i
|
16
|
+
end.map do |index|
|
17
|
+
index.name
|
18
|
+
end
|
19
|
+
end)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,27 +1,12 @@
|
|
1
|
-
require "active_record_doctor/
|
2
|
-
require "active_record_doctor/printers/io_printer"
|
1
|
+
require "active_record_doctor/tasks/base"
|
3
2
|
|
4
3
|
module ActiveRecordDoctor
|
5
4
|
module Tasks
|
6
|
-
class UnindexedForeignKeys
|
7
|
-
|
8
|
-
|
9
|
-
def self.run
|
10
|
-
new.run
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(printer: ActiveRecordDoctor::Printers::IOPrinter.new)
|
14
|
-
@printer = printer
|
15
|
-
end
|
5
|
+
class UnindexedForeignKeys < Base
|
6
|
+
@description = 'Detect foreign keys without an index on them'
|
16
7
|
|
17
8
|
def run
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def unindexed_foreign_keys
|
24
|
-
hash_from_pairs(connection_tables.select do |table|
|
9
|
+
success(hash_from_pairs(tables.select do |table|
|
25
10
|
"schema_migrations" != table
|
26
11
|
end.map do |table|
|
27
12
|
[
|
@@ -34,9 +19,11 @@ module ActiveRecordDoctor
|
|
34
19
|
]
|
35
20
|
end.select do |table, columns|
|
36
21
|
!columns.empty?
|
37
|
-
end)
|
22
|
+
end))
|
38
23
|
end
|
39
24
|
|
25
|
+
private
|
26
|
+
|
40
27
|
def foreign_key?(table, column)
|
41
28
|
column.name.end_with?("_id")
|
42
29
|
end
|
@@ -53,14 +40,6 @@ module ActiveRecordDoctor
|
|
53
40
|
index.columns == [type_column_name, column.name]
|
54
41
|
end
|
55
42
|
end
|
56
|
-
|
57
|
-
def connection
|
58
|
-
@connection ||= ActiveRecord::Base.connection
|
59
|
-
end
|
60
|
-
|
61
|
-
def hash_from_pairs(pairs)
|
62
|
-
Hash[*pairs.flatten(1)]
|
63
|
-
end
|
64
43
|
end
|
65
44
|
end
|
66
45
|
end
|