database_consistency 0.3.0 → 0.4.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.
- 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
|