database_consistency 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/database_consistency/checkers/base_checker.rb +11 -7
- data/lib/database_consistency/checkers/belongs_to_presence_checker.rb +37 -0
- data/lib/database_consistency/checkers/{presence_validation_checker.rb → column_presence_checker.rb} +9 -19
- data/lib/database_consistency/checkers/null_constraint_checker.rb +27 -30
- data/lib/database_consistency/checkers/table_checker.rb +23 -0
- data/lib/database_consistency/checkers/validator_checker.rb +24 -0
- data/lib/database_consistency/helper.rb +0 -5
- data/lib/database_consistency/processors/models_processor.rb +9 -8
- data/lib/database_consistency/version.rb +1 -1
- data/lib/database_consistency.rb +4 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0980b34790693d62df725d9e527a8694480d343c44c5061250abe3900802b32
|
4
|
+
data.tar.gz: 514a6e6590244f3c31b43d7c997fc90d4f3eae5e4748930e4d28eefb6945c01a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f79aa59df5649f0313cb78023781c9ce59e73e2add191268606353530a957a299bed6b6834352b2fbb694cdef90c072ae1c5281cdf15e3187f65da04a758e874
|
7
|
+
data.tar.gz: d1b45c15a664d2832bc3cde9a511a779fa1bd9ca39b2c7c990cdfdedeced6a6f9fcf8a50b526f07eb79b8664ea2183fab4445465d1507e9aea8770394933560b
|
@@ -4,14 +4,10 @@ module DatabaseConsistency
|
|
4
4
|
module Checkers
|
5
5
|
# The base class for checkers
|
6
6
|
class BaseChecker
|
7
|
-
|
8
|
-
@table_or_model = table_or_model
|
9
|
-
@column_or_attribute = column_or_attribute
|
10
|
-
@opts = opts
|
11
|
-
end
|
12
|
-
|
13
|
-
# @return [Hash]
|
7
|
+
# @return [Hash, nil]
|
14
8
|
def report
|
9
|
+
return unless preconditions
|
10
|
+
|
15
11
|
@report ||= check
|
16
12
|
end
|
17
13
|
|
@@ -22,6 +18,14 @@ module DatabaseConsistency
|
|
22
18
|
|
23
19
|
private
|
24
20
|
|
21
|
+
def check
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
|
25
|
+
def preconditions
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
|
25
29
|
attr_reader :table_or_model, :column_or_attribute, :opts
|
26
30
|
|
27
31
|
# @return [String]
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Checkers
|
5
|
+
# This class checks if required +belongs_to+ has foreign key constraint
|
6
|
+
class BelongsToPresenceChecker < ValidatorChecker
|
7
|
+
MISSING_FOREIGN_KEY = 'should have foreign key in the database'
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# We skip check when:
|
12
|
+
# - validator is a not a presence validator
|
13
|
+
# - there is no belongs_to reflection with given name
|
14
|
+
# - belongs_to reflection is polymorphic
|
15
|
+
def preconditions
|
16
|
+
validator.kind == :presence && reflection && !reflection.polymorphic?
|
17
|
+
end
|
18
|
+
|
19
|
+
# Table of possible statuses
|
20
|
+
# | foreign key | status |
|
21
|
+
# | ----------- | ------ |
|
22
|
+
# | persisted | ok |
|
23
|
+
# | missed | fail |
|
24
|
+
def check
|
25
|
+
if model.connection.foreign_keys(model.table_name).find { |fk| fk.column == reflection.foreign_key.to_s }
|
26
|
+
report_template(:ok)
|
27
|
+
else
|
28
|
+
report_template(:fail, MISSING_FOREIGN_KEY)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def reflection
|
33
|
+
@reflection ||= model.reflect_on_association(attribute)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/database_consistency/checkers/{presence_validation_checker.rb → column_presence_checker.rb}
RENAMED
@@ -3,7 +3,7 @@
|
|
3
3
|
module DatabaseConsistency
|
4
4
|
module Checkers
|
5
5
|
# This class checks PresenceValidator
|
6
|
-
class
|
6
|
+
class ColumnPresenceChecker < ValidatorChecker
|
7
7
|
WEAK_OPTIONS = %i[allow_nil allow_blank if unless].freeze
|
8
8
|
# Message templates
|
9
9
|
CONSTRAINT_MISSING = 'should be required in the database'
|
@@ -11,6 +11,13 @@ module DatabaseConsistency
|
|
11
11
|
|
12
12
|
private
|
13
13
|
|
14
|
+
# We skip check when:
|
15
|
+
# - validator is not a presence validator
|
16
|
+
# - there is no column in the database with given name
|
17
|
+
def preconditions
|
18
|
+
validator.kind == :presence && column
|
19
|
+
end
|
20
|
+
|
14
21
|
# Table of possible statuses
|
15
22
|
# | allow_nil/allow_blank/if/unless | database | status |
|
16
23
|
# | ------------------------------- | -------- | ------ |
|
@@ -18,12 +25,7 @@ module DatabaseConsistency
|
|
18
25
|
# | at least one provided | optional | ok |
|
19
26
|
# | all missed | required | ok |
|
20
27
|
# | all missed | optional | fail |
|
21
|
-
#
|
22
|
-
# We skip check when:
|
23
|
-
# - there is no column in the database with given name
|
24
28
|
def check
|
25
|
-
return unless column
|
26
|
-
|
27
29
|
can_be_null = column.null
|
28
30
|
has_weak_option = validator.options.slice(*WEAK_OPTIONS).any?
|
29
31
|
|
@@ -37,19 +39,7 @@ module DatabaseConsistency
|
|
37
39
|
end
|
38
40
|
|
39
41
|
def column
|
40
|
-
@column ||=
|
41
|
-
end
|
42
|
-
|
43
|
-
def column_or_attribute_name
|
44
|
-
column_or_attribute.to_s
|
45
|
-
end
|
46
|
-
|
47
|
-
def table_or_model_name
|
48
|
-
table_or_model.name.to_s
|
49
|
-
end
|
50
|
-
|
51
|
-
def validator
|
52
|
-
opts[:validator]
|
42
|
+
@column ||= model.columns.select.find { |field| field.name == attribute.to_s }
|
53
43
|
end
|
54
44
|
end
|
55
45
|
end
|
@@ -3,62 +3,59 @@
|
|
3
3
|
module DatabaseConsistency
|
4
4
|
module Checkers
|
5
5
|
# This class checks missing presence validator
|
6
|
-
class NullConstraintChecker <
|
6
|
+
class NullConstraintChecker < TableChecker
|
7
7
|
# Message templates
|
8
8
|
VALIDATOR_MISSING = 'is required but do not have presence validator'
|
9
9
|
|
10
10
|
private
|
11
11
|
|
12
|
-
# Table of possible statuses
|
13
|
-
# | validation | database | status |
|
14
|
-
# | ---------- | -------- | ------ |
|
15
|
-
# | missed | required | fail |
|
16
|
-
#
|
17
12
|
# We skip check when:
|
18
13
|
# - column hasn't null constraint
|
19
14
|
# - column has default value
|
20
15
|
# - column is a primary key
|
21
16
|
# - column is a timestamp
|
22
|
-
|
23
|
-
|
24
|
-
# - belongs_to reflection exists with given column as foreign key or foreign type
|
25
|
-
def check
|
26
|
-
return if skip? ||
|
27
|
-
validator?(ActiveModel::Validations::PresenceValidator) ||
|
28
|
-
validator?(ActiveModel::Validations::InclusionValidator) ||
|
29
|
-
belongs_to_reflection?
|
30
|
-
|
31
|
-
report_template(:fail, VALIDATOR_MISSING)
|
17
|
+
def preconditions
|
18
|
+
!column.null && column.default.nil? && !primary_field? && !timestamp_field?
|
32
19
|
end
|
33
20
|
|
34
|
-
|
35
|
-
|
21
|
+
# Table of possible statuses
|
22
|
+
# | validation | status |
|
23
|
+
# | ---------- | ------ |
|
24
|
+
# | provided | ok |
|
25
|
+
# | missed | fail |
|
26
|
+
#
|
27
|
+
# We consider PresenceValidation, InclusionValidation or BelongsTo reflection using this column
|
28
|
+
def check
|
29
|
+
if valid?
|
30
|
+
report_template(:ok)
|
31
|
+
else
|
32
|
+
report_template(:fail, VALIDATOR_MISSING)
|
33
|
+
end
|
36
34
|
end
|
37
35
|
|
38
|
-
def
|
39
|
-
|
36
|
+
def valid?
|
37
|
+
validator?(ActiveModel::Validations::PresenceValidator) ||
|
38
|
+
validator?(ActiveModel::Validations::InclusionValidator) ||
|
39
|
+
belongs_to_reflection?
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
43
|
-
|
44
|
-
!column_or_attribute.default.nil? ||
|
45
|
-
column_or_attribute.name == table_or_model.primary_key ||
|
46
|
-
timestamp_field?
|
42
|
+
def primary_field?
|
43
|
+
column.name.to_s == model.primary_key.to_s
|
47
44
|
end
|
48
45
|
|
49
46
|
def timestamp_field?
|
50
|
-
|
47
|
+
model.record_timestamps? && %w[created_at updated_at].include?(column.name)
|
51
48
|
end
|
52
49
|
|
53
50
|
def validator?(validator_class)
|
54
|
-
|
55
|
-
Helper.check_inclusion?(validator.attributes,
|
51
|
+
model.validators.grep(validator_class).any? do |validator|
|
52
|
+
Helper.check_inclusion?(validator.attributes, column.name)
|
56
53
|
end
|
57
54
|
end
|
58
55
|
|
59
56
|
def belongs_to_reflection?
|
60
|
-
|
61
|
-
Helper.check_inclusion?([r.foreign_key, r.foreign_type],
|
57
|
+
model.reflect_on_all_associations.grep(ActiveRecord::Reflection::BelongsToReflection).any? do |r|
|
58
|
+
Helper.check_inclusion?([r.foreign_key, r.foreign_type], column.name)
|
62
59
|
end
|
63
60
|
end
|
64
61
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Checkers
|
5
|
+
# The base class for table checkers
|
6
|
+
class TableChecker < BaseChecker
|
7
|
+
attr_reader :model, :column
|
8
|
+
|
9
|
+
def initialize(model, column)
|
10
|
+
@model = model
|
11
|
+
@column = column
|
12
|
+
end
|
13
|
+
|
14
|
+
def column_or_attribute_name
|
15
|
+
@column_or_attribute_name ||= column.name.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def table_or_model_name
|
19
|
+
@table_or_model_name ||= model.name.to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Checkers
|
5
|
+
# The base class for validator checker
|
6
|
+
class ValidatorChecker < BaseChecker
|
7
|
+
attr_reader :model, :attribute, :validator
|
8
|
+
|
9
|
+
def initialize(model, attribute, validator)
|
10
|
+
@model = model
|
11
|
+
@attribute = attribute
|
12
|
+
@validator = validator
|
13
|
+
end
|
14
|
+
|
15
|
+
def column_or_attribute_name
|
16
|
+
@column_or_attribute_name ||= attribute.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def table_or_model_name
|
20
|
+
@table_or_model_name ||= model.name.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -22,11 +22,6 @@ module DatabaseConsistency
|
|
22
22
|
Rails.application.eager_load! if defined?(Rails)
|
23
23
|
end
|
24
24
|
|
25
|
-
# Find a database field with name equals to attribute
|
26
|
-
def find_field(model, attribute)
|
27
|
-
model.columns.select.find { |field| field.name == attribute }
|
28
|
-
end
|
29
|
-
|
30
25
|
# @return [Boolean]
|
31
26
|
def check_inclusion?(array, element)
|
32
27
|
array.include?(element.to_s) || array.include?(element.to_sym)
|
@@ -4,9 +4,10 @@ module DatabaseConsistency
|
|
4
4
|
module Processors
|
5
5
|
# The class to process all comparators
|
6
6
|
class ModelsProcessor < BaseProcessor
|
7
|
-
CHECKERS =
|
8
|
-
|
9
|
-
|
7
|
+
CHECKERS = [
|
8
|
+
Checkers::ColumnPresenceChecker,
|
9
|
+
Checkers::BelongsToPresenceChecker
|
10
|
+
].freeze
|
10
11
|
|
11
12
|
private
|
12
13
|
|
@@ -14,11 +15,11 @@ module DatabaseConsistency
|
|
14
15
|
def check
|
15
16
|
Helper.parent_models.flat_map do |model|
|
16
17
|
model.validators.flat_map do |validator|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
validator.attributes.flat_map do |attribute|
|
19
|
+
CHECKERS.map do |checker_class|
|
20
|
+
checker = checker_class.new(model, attribute, validator)
|
21
|
+
checker.report if checker.enabled?(configuration)
|
22
|
+
end
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end.compact
|
data/lib/database_consistency.rb
CHANGED
@@ -10,8 +10,11 @@ require 'database_consistency/writers/base_writer'
|
|
10
10
|
require 'database_consistency/writers/simple_writer'
|
11
11
|
|
12
12
|
require 'database_consistency/checkers/base_checker'
|
13
|
-
require 'database_consistency/checkers/
|
13
|
+
require 'database_consistency/checkers/table_checker'
|
14
|
+
require 'database_consistency/checkers/validator_checker'
|
15
|
+
require 'database_consistency/checkers/column_presence_checker'
|
14
16
|
require 'database_consistency/checkers/null_constraint_checker'
|
17
|
+
require 'database_consistency/checkers/belongs_to_presence_checker'
|
15
18
|
|
16
19
|
require 'database_consistency/processors/base_processor'
|
17
20
|
require 'database_consistency/processors/models_processor'
|
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: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evgeniy Demin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-01-
|
11
|
+
date: 2019-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -111,8 +111,11 @@ files:
|
|
111
111
|
- bin/database_consistency
|
112
112
|
- lib/database_consistency.rb
|
113
113
|
- lib/database_consistency/checkers/base_checker.rb
|
114
|
+
- lib/database_consistency/checkers/belongs_to_presence_checker.rb
|
115
|
+
- lib/database_consistency/checkers/column_presence_checker.rb
|
114
116
|
- lib/database_consistency/checkers/null_constraint_checker.rb
|
115
|
-
- lib/database_consistency/checkers/
|
117
|
+
- lib/database_consistency/checkers/table_checker.rb
|
118
|
+
- lib/database_consistency/checkers/validator_checker.rb
|
116
119
|
- lib/database_consistency/configuration.rb
|
117
120
|
- lib/database_consistency/helper.rb
|
118
121
|
- lib/database_consistency/processors/base_processor.rb
|