database_consistency 1.1.3 → 1.1.7
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.
- checksums.yaml +4 -4
- data/bin/database_consistency +37 -12
- data/lib/database_consistency/checkers/{validator_checkers/belongs_to_presence_checker.rb → association_checkers/foreign_key_checker.rb} +12 -11
- data/lib/database_consistency/checkers/association_checkers/foreign_key_type_checker.rb +4 -2
- data/lib/database_consistency/checkers/column_checkers/null_constraint_checker.rb +20 -14
- data/lib/database_consistency/checkers/validators_fraction_checkers/column_presence_checker.rb +12 -2
- data/lib/database_consistency/configuration.rb +24 -9
- data/lib/database_consistency/processors/associations_processor.rb +1 -0
- data/lib/database_consistency/processors/validators_processor.rb +0 -1
- data/lib/database_consistency/version.rb +1 -1
- data/lib/database_consistency.rb +3 -3
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2a742a08f374228e004b155fef15ec5ddaa23404c5aacf42b462eb2cb82ba09a
|
|
4
|
+
data.tar.gz: 8359e199cdbb89fc9d4b8700728bbef938d2d90a1474e4c2400c2aaa6d31f28f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 962c814731ed2c85427e4b3e765df897c8cd8574f6075e3566e65b72af5effa370a14593019748ff9833a9658a0e55841219acdf3fe9caf212aa9db4bc426df3
|
|
7
|
+
data.tar.gz: df3aab9a1b6b5d5b2a815ad237b76ff3ffe127cd7921050c09a57e5f53e08087344ad5777224a603c52978382f6568c0c8a19c16038863cb759cac147bb7f9be
|
data/bin/database_consistency
CHANGED
|
@@ -1,27 +1,52 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
+
require_relative '../lib/database_consistency/configuration'
|
|
4
|
+
|
|
5
|
+
default_config = DatabaseConsistency::Configuration::DEFAULT_PATH
|
|
6
|
+
|
|
3
7
|
if ARGV.include?('install')
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
file_exists = File.exists?(
|
|
8
|
+
require 'pathname'
|
|
9
|
+
|
|
10
|
+
file_exists = File.exists?(default_config)
|
|
7
11
|
rules = Pathname.new(__FILE__).dirname.join('..', 'lib', 'database_consistency', 'templates', 'rails_defaults.yml').read
|
|
8
|
-
if file_exists && File.foreach(
|
|
9
|
-
puts "#{
|
|
12
|
+
if file_exists && File.foreach(default_config).grep(Regexp.new(rules.lines.first.chomp)).any?
|
|
13
|
+
puts "#{default_config} is already present"
|
|
10
14
|
else
|
|
11
|
-
File.open(
|
|
15
|
+
File.open(default_config, 'a') do |file|
|
|
12
16
|
file << "\n" * 2 if file_exists
|
|
13
17
|
file << rules
|
|
14
18
|
end
|
|
15
|
-
puts "#{
|
|
19
|
+
puts "#{default_config} #{file_exists ? 'updated' : 'added'}"
|
|
16
20
|
end
|
|
17
21
|
exit 0
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
require 'optparse'
|
|
25
|
+
|
|
26
|
+
config = [default_config]
|
|
27
|
+
opt_parser = OptionParser.new do |opts|
|
|
28
|
+
opts.banner = <<-DESC
|
|
29
|
+
Usage: database_consistency install - run installation
|
|
30
|
+
database_consistency [options]
|
|
31
|
+
DESC
|
|
32
|
+
|
|
33
|
+
opts.on('-cFILE', '--config=FILE', 'Use additional configuration file') do |f|
|
|
34
|
+
config << f
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
opts.on('-h', '--help', 'Prints this help') do
|
|
38
|
+
puts opts
|
|
39
|
+
exit
|
|
22
40
|
end
|
|
23
41
|
end
|
|
24
42
|
|
|
43
|
+
opt_parser.parse!
|
|
44
|
+
|
|
45
|
+
base_dir = File.join(Dir.pwd, ARGV.first.to_s)
|
|
46
|
+
unless File.realpath(base_dir).start_with?(Dir.pwd)
|
|
47
|
+
puts "\nWarning! You are going out of current directory, ruby version may be wrong and some gems may be missing.\n"
|
|
48
|
+
end
|
|
49
|
+
|
|
25
50
|
# Load Rails project
|
|
26
51
|
begin
|
|
27
52
|
require File.join(base_dir, 'config', 'boot')
|
|
@@ -41,5 +66,5 @@ $LOAD_PATH.unshift(File.expand_path('lib', __dir__))
|
|
|
41
66
|
require 'database_consistency'
|
|
42
67
|
|
|
43
68
|
# Process checks
|
|
44
|
-
code = DatabaseConsistency.run
|
|
69
|
+
code = DatabaseConsistency.run(config)
|
|
45
70
|
exit code
|
|
@@ -2,18 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
module DatabaseConsistency
|
|
4
4
|
module Checkers
|
|
5
|
-
# This class checks if
|
|
6
|
-
class
|
|
7
|
-
MISSING_FOREIGN_KEY = '
|
|
5
|
+
# This class checks if non polymorphic +belongs_to+ association has foreign key constraint
|
|
6
|
+
class ForeignKeyChecker < AssociationChecker
|
|
7
|
+
MISSING_FOREIGN_KEY = 'should have foreign key in the database'
|
|
8
8
|
|
|
9
9
|
private
|
|
10
10
|
|
|
11
11
|
# We skip check when:
|
|
12
|
-
# -
|
|
13
|
-
# -
|
|
14
|
-
# - belongs_to association is polymorphic
|
|
12
|
+
# - association isn't belongs_to association
|
|
13
|
+
# - association is polymorphic
|
|
15
14
|
def preconditions
|
|
16
|
-
|
|
15
|
+
supported? && association.belongs_to? && !association.polymorphic?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def supported?
|
|
19
|
+
return false if ActiveRecord::VERSION::MAJOR < 5 && ActiveRecord::Base.connection_config[:adapter] == 'sqlite3'
|
|
20
|
+
|
|
21
|
+
true
|
|
17
22
|
end
|
|
18
23
|
|
|
19
24
|
# Table of possible statuses
|
|
@@ -28,10 +33,6 @@ module DatabaseConsistency
|
|
|
28
33
|
report_template(:fail, MISSING_FOREIGN_KEY)
|
|
29
34
|
end
|
|
30
35
|
end
|
|
31
|
-
|
|
32
|
-
def association
|
|
33
|
-
@association ||= model.reflect_on_association(attribute)
|
|
34
|
-
end
|
|
35
36
|
end
|
|
36
37
|
end
|
|
37
38
|
end
|
|
@@ -42,6 +42,8 @@ module DatabaseConsistency
|
|
|
42
42
|
else
|
|
43
43
|
report_template(:fail, render_text)
|
|
44
44
|
end
|
|
45
|
+
rescue Errors::MissingField => e
|
|
46
|
+
report_template(:fail, e.message)
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
# @return [String]
|
|
@@ -101,8 +103,8 @@ module DatabaseConsistency
|
|
|
101
103
|
|
|
102
104
|
# @return [String]
|
|
103
105
|
def missing_field_error(table_name, column_name)
|
|
104
|
-
"
|
|
105
|
-
"field (#{column_name}) of table (#{table_name}) but it is missing
|
|
106
|
+
"association (#{association.name}) of class (#{association.active_record}) relies on "\
|
|
107
|
+
"field (#{column_name}) of table (#{table_name}) but it is missing"
|
|
106
108
|
end
|
|
107
109
|
|
|
108
110
|
# @param [ActiveRecord::ConnectionAdapters::Column] column
|
|
@@ -6,6 +6,8 @@ module DatabaseConsistency
|
|
|
6
6
|
class NullConstraintChecker < ColumnChecker
|
|
7
7
|
# Message templates
|
|
8
8
|
VALIDATOR_MISSING = 'column is required in the database but do not have presence validator'
|
|
9
|
+
ASSOCIATION_VALIDATOR_MISSING = 'column is required in the database but do '\
|
|
10
|
+
'not have presence validator for association (%a_n)'
|
|
9
11
|
|
|
10
12
|
private
|
|
11
13
|
|
|
@@ -26,21 +28,23 @@ module DatabaseConsistency
|
|
|
26
28
|
# | missing | fail |
|
|
27
29
|
#
|
|
28
30
|
# We consider PresenceValidation, InclusionValidation, ExclusionValidation, NumericalityValidator with nil,
|
|
29
|
-
# or BelongsTo association using this column
|
|
31
|
+
# or required BelongsTo association using this column
|
|
30
32
|
def check
|
|
31
33
|
if valid?
|
|
32
34
|
report_template(:ok)
|
|
35
|
+
elsif belongs_to_association
|
|
36
|
+
report_template(:fail, ASSOCIATION_VALIDATOR_MISSING.gsub('%a_n', belongs_to_association.name.to_s))
|
|
33
37
|
else
|
|
34
38
|
report_template(:fail, VALIDATOR_MISSING)
|
|
35
39
|
end
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
def valid?
|
|
39
|
-
validator?(
|
|
40
|
-
validator?(
|
|
43
|
+
validator?(:presence, column.name) ||
|
|
44
|
+
validator?(:inclusion, column.name) ||
|
|
41
45
|
numericality_validator_without_allow_nil? ||
|
|
42
46
|
nil_exclusion_validator? ||
|
|
43
|
-
belongs_to_association?
|
|
47
|
+
(belongs_to_association && validator?(:presence, belongs_to_association.name))
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
def primary_field?
|
|
@@ -52,28 +56,30 @@ module DatabaseConsistency
|
|
|
52
56
|
end
|
|
53
57
|
|
|
54
58
|
def nil_exclusion_validator?
|
|
55
|
-
model.validators.
|
|
56
|
-
|
|
59
|
+
model.validators.any? do |validator|
|
|
60
|
+
validator.kind == :exclusion &&
|
|
61
|
+
Helper.check_inclusion?(validator.attributes, column.name) &&
|
|
57
62
|
validator.options[:in].include?(nil)
|
|
58
63
|
end
|
|
59
64
|
end
|
|
60
65
|
|
|
61
66
|
def numericality_validator_without_allow_nil?
|
|
62
|
-
model.validators.
|
|
63
|
-
|
|
67
|
+
model.validators.any? do |validator|
|
|
68
|
+
validator.kind == :numericality &&
|
|
69
|
+
Helper.check_inclusion?(validator.attributes, column.name) &&
|
|
64
70
|
!validator.options[:allow_nil]
|
|
65
71
|
end
|
|
66
72
|
end
|
|
67
73
|
|
|
68
|
-
def validator?(
|
|
69
|
-
model.validators.
|
|
70
|
-
Helper.check_inclusion?(validator.attributes,
|
|
74
|
+
def validator?(kind, attribute)
|
|
75
|
+
model.validators.any? do |validator|
|
|
76
|
+
validator.kind == kind && Helper.check_inclusion?(validator.attributes, attribute)
|
|
71
77
|
end
|
|
72
78
|
end
|
|
73
79
|
|
|
74
|
-
def belongs_to_association
|
|
75
|
-
model.reflect_on_all_associations.
|
|
76
|
-
Helper.check_inclusion?([r.foreign_key, r.foreign_type], column.name)
|
|
80
|
+
def belongs_to_association
|
|
81
|
+
@belongs_to_association ||= model.reflect_on_all_associations.find do |r|
|
|
82
|
+
r.belongs_to? && Helper.check_inclusion?([r.foreign_key, r.foreign_type], column.name)
|
|
77
83
|
end
|
|
78
84
|
end
|
|
79
85
|
end
|
data/lib/database_consistency/checkers/validators_fraction_checkers/column_presence_checker.rb
CHANGED
|
@@ -29,8 +29,18 @@ module DatabaseConsistency
|
|
|
29
29
|
# | all missing | required | ok |
|
|
30
30
|
# | all missing | optional | fail |
|
|
31
31
|
def check
|
|
32
|
+
report_message
|
|
33
|
+
rescue Errors::MissingField => e
|
|
34
|
+
report_template(:fail, e.message)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def has_weak_option?
|
|
38
|
+
validators.all? { |validator| validator.options.slice(*WEAK_OPTIONS).any? }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def report_message
|
|
32
42
|
can_be_null = column.null
|
|
33
|
-
has_weak_option =
|
|
43
|
+
has_weak_option = has_weak_option?
|
|
34
44
|
|
|
35
45
|
return report_template(:ok) if can_be_null == has_weak_option
|
|
36
46
|
return report_template(:fail, POSSIBLE_NULL) unless can_be_null
|
|
@@ -44,7 +54,7 @@ module DatabaseConsistency
|
|
|
44
54
|
|
|
45
55
|
def column
|
|
46
56
|
@column ||= regular_column || association_reference_column ||
|
|
47
|
-
(raise Errors::MissingField, "
|
|
57
|
+
(raise Errors::MissingField, "column (#{attribute}) is missing in table (#{model.table_name}) but used for presence validation")
|
|
48
58
|
end
|
|
49
59
|
|
|
50
60
|
def regular_column
|
|
@@ -5,15 +5,20 @@ require 'yaml'
|
|
|
5
5
|
module DatabaseConsistency
|
|
6
6
|
# The class to access configuration options
|
|
7
7
|
class Configuration
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def initialize(
|
|
11
|
-
@configuration =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
DEFAULT_PATH = '.database_consistency.yml'
|
|
9
|
+
|
|
10
|
+
def initialize(filepaths = DEFAULT_PATH)
|
|
11
|
+
@configuration = Array(filepaths).each_with_object({}) do |filepath, result|
|
|
12
|
+
content =
|
|
13
|
+
if filepath && File.exist?(filepath)
|
|
14
|
+
data = YAML.load_file(filepath)
|
|
15
|
+
data.is_a?(Hash) ? data : {}
|
|
16
|
+
else
|
|
17
|
+
{}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
combine_configs!(result, content)
|
|
21
|
+
end
|
|
17
22
|
end
|
|
18
23
|
|
|
19
24
|
def debug?
|
|
@@ -48,6 +53,16 @@ module DatabaseConsistency
|
|
|
48
53
|
|
|
49
54
|
attr_reader :configuration
|
|
50
55
|
|
|
56
|
+
def combine_configs!(config, new_config)
|
|
57
|
+
config.merge!(new_config) do |_key, val, new_val|
|
|
58
|
+
if val.is_a?(Hash) && new_val.is_a?(Hash)
|
|
59
|
+
combine_configs!(val, new_val)
|
|
60
|
+
else
|
|
61
|
+
new_val
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
51
66
|
def settings
|
|
52
67
|
@settings ||= configuration['DatabaseConsistencySettings']
|
|
53
68
|
end
|
data/lib/database_consistency.rb
CHANGED
|
@@ -19,6 +19,7 @@ require 'database_consistency/checkers/base_checker'
|
|
|
19
19
|
|
|
20
20
|
require 'database_consistency/checkers/association_checkers/association_checker'
|
|
21
21
|
require 'database_consistency/checkers/association_checkers/missing_index_checker'
|
|
22
|
+
require 'database_consistency/checkers/association_checkers/foreign_key_checker'
|
|
22
23
|
require 'database_consistency/checkers/association_checkers/foreign_key_type_checker'
|
|
23
24
|
|
|
24
25
|
require 'database_consistency/checkers/column_checkers/column_checker'
|
|
@@ -27,7 +28,6 @@ require 'database_consistency/checkers/column_checkers/length_constraint_checker
|
|
|
27
28
|
require 'database_consistency/checkers/column_checkers/primary_key_type_checker'
|
|
28
29
|
|
|
29
30
|
require 'database_consistency/checkers/validator_checkers/validator_checker'
|
|
30
|
-
require 'database_consistency/checkers/validator_checkers/belongs_to_presence_checker'
|
|
31
31
|
require 'database_consistency/checkers/validator_checkers/missing_unique_index_checker'
|
|
32
32
|
|
|
33
33
|
require 'database_consistency/checkers/validators_fraction_checkers/validators_fraction_checker'
|
|
@@ -48,8 +48,8 @@ require 'database_consistency/processors/indexes_processor'
|
|
|
48
48
|
# The root module
|
|
49
49
|
module DatabaseConsistency
|
|
50
50
|
class << self
|
|
51
|
-
def run
|
|
52
|
-
configuration = Configuration.new
|
|
51
|
+
def run(*args)
|
|
52
|
+
configuration = Configuration.new(*args)
|
|
53
53
|
reports = Processors.reports(configuration)
|
|
54
54
|
|
|
55
55
|
Writers::SimpleWriter.write(
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: database_consistency
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evgeniy Demin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-12-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -133,6 +133,7 @@ files:
|
|
|
133
133
|
- bin/database_consistency
|
|
134
134
|
- lib/database_consistency.rb
|
|
135
135
|
- lib/database_consistency/checkers/association_checkers/association_checker.rb
|
|
136
|
+
- lib/database_consistency/checkers/association_checkers/foreign_key_checker.rb
|
|
136
137
|
- lib/database_consistency/checkers/association_checkers/foreign_key_type_checker.rb
|
|
137
138
|
- lib/database_consistency/checkers/association_checkers/missing_index_checker.rb
|
|
138
139
|
- lib/database_consistency/checkers/base_checker.rb
|
|
@@ -144,7 +145,6 @@ files:
|
|
|
144
145
|
- lib/database_consistency/checkers/index_checkers/redundant_index_checker.rb
|
|
145
146
|
- lib/database_consistency/checkers/index_checkers/redundant_unique_index_checker.rb
|
|
146
147
|
- lib/database_consistency/checkers/index_checkers/unique_index_checker.rb
|
|
147
|
-
- lib/database_consistency/checkers/validator_checkers/belongs_to_presence_checker.rb
|
|
148
148
|
- lib/database_consistency/checkers/validator_checkers/missing_unique_index_checker.rb
|
|
149
149
|
- lib/database_consistency/checkers/validator_checkers/validator_checker.rb
|
|
150
150
|
- lib/database_consistency/checkers/validators_fraction_checkers/column_presence_checker.rb
|