database_consistency 0.2.5 → 0.3.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 +52 -0
- data/lib/database_consistency/checkers/null_constraint_checker.rb +66 -0
- data/lib/database_consistency/{comparators/presence_comparator.rb → checkers/presence_validation_checker.rb} +31 -12
- data/lib/database_consistency/configuration.rb +24 -5
- data/lib/database_consistency/helper.rb +2 -7
- data/lib/database_consistency/processors/base_processor.rb +33 -0
- data/lib/database_consistency/processors/models_processor.rb +28 -0
- data/lib/database_consistency/processors/tables_processor.rb +25 -0
- data/lib/database_consistency/version.rb +3 -1
- data/lib/database_consistency/writers/base_writer.rb +3 -1
- data/lib/database_consistency/writers/simple_writer.rb +5 -7
- data/lib/database_consistency.rb +11 -30
- metadata +8 -9
- data/lib/database_consistency/column_verifiers/base_verifier.rb +0 -23
- data/lib/database_consistency/column_verifiers/presence_missing_verifier.rb +0 -45
- data/lib/database_consistency/comparators/base_comparator.rb +0 -24
- data/lib/database_consistency/database_processor.rb +0 -18
- data/lib/database_consistency/report.rb +0 -17
- data/lib/database_consistency/validators_processor.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0cb421a3fbd1c94df1816ecaf52487578c2a68c2aa13f384d1f80a9c68b35613
|
4
|
+
data.tar.gz: 5a38ad0d44e448e08e7474e74ecd0f934f0b9bf86e2fe43a80c2bb4540b154e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6195f162dac4165dd70ce10fddc1dde43527f2d3dd845f9a4f2f4b9f0def589ed125fbe5cce3a5e61eb80c91ccefb609de4bc42ea0dcbe4ff26edd556522884
|
7
|
+
data.tar.gz: 47ebc91829df9bc38a2f518b0b5d4b938a51d79d441984c257c9fa7429d12cc22d3d6184029396d29c0e422ac890c9311391ae9cbc6e2d052fae33e22694f11c
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Checkers
|
5
|
+
# The base class for checkers
|
6
|
+
class BaseChecker
|
7
|
+
def initialize(table_or_model, column_or_attribute, opts = {})
|
8
|
+
@table_or_model = table_or_model
|
9
|
+
@column_or_attribute = column_or_attribute
|
10
|
+
@opts = opts
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Hash]
|
14
|
+
def report
|
15
|
+
@report ||= check
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [DatabaseConsistency::Configuration] configuration
|
19
|
+
def enabled?(configuration)
|
20
|
+
configuration.enabled?(checker_name, table_or_model_name, column_or_attribute_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :table_or_model, :column_or_attribute, :opts
|
26
|
+
|
27
|
+
# @return [String]
|
28
|
+
def checker_name
|
29
|
+
@checker_name ||= self.class.name.split('::').last
|
30
|
+
end
|
31
|
+
|
32
|
+
def table_or_model_name
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
def column_or_attribute_name
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Hash]
|
41
|
+
def report_template(status, message = nil)
|
42
|
+
OpenStruct.new(
|
43
|
+
checker_name: checker_name,
|
44
|
+
table_or_model_name: table_or_model_name,
|
45
|
+
column_or_attribute_name: column_or_attribute_name,
|
46
|
+
status: status,
|
47
|
+
message: message
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Checkers
|
5
|
+
# This class checks missing presence validator
|
6
|
+
class NullConstraintChecker < BaseChecker
|
7
|
+
# Message templates
|
8
|
+
VALIDATOR_MISSING = 'is required but do not have presence validator'
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# Table of possible statuses
|
13
|
+
# | validation | database | status |
|
14
|
+
# | ---------- | -------- | ------ |
|
15
|
+
# | missed | required | fail |
|
16
|
+
#
|
17
|
+
# We skip check when:
|
18
|
+
# - column hasn't null constraint
|
19
|
+
# - column has default value
|
20
|
+
# - column is a primary key
|
21
|
+
# - column is a timestamp
|
22
|
+
# - presence validation exists
|
23
|
+
# - inclusion validation exists
|
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)
|
32
|
+
end
|
33
|
+
|
34
|
+
def column_or_attribute_name
|
35
|
+
column_or_attribute.name.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
def table_or_model_name
|
39
|
+
table_or_model.name.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def skip?
|
43
|
+
column_or_attribute.null ||
|
44
|
+
!column_or_attribute.default.nil? ||
|
45
|
+
column_or_attribute.name == table_or_model.primary_key ||
|
46
|
+
timestamp_field?
|
47
|
+
end
|
48
|
+
|
49
|
+
def timestamp_field?
|
50
|
+
table_or_model.record_timestamps? && %w[created_at updated_at].include?(column_or_attribute.name)
|
51
|
+
end
|
52
|
+
|
53
|
+
def validator?(validator_class)
|
54
|
+
table_or_model.validators.grep(validator_class).any? do |validator|
|
55
|
+
Helper.check_inclusion?(validator.attributes, column_or_attribute.name)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def belongs_to_reflection?
|
60
|
+
table_or_model.reflect_on_all_associations.grep(ActiveRecord::Reflection::BelongsToReflection).any? do |r|
|
61
|
+
Helper.check_inclusion?([r.foreign_key, r.foreign_type], column_or_attribute.name)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -1,11 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DatabaseConsistency
|
2
|
-
module
|
3
|
-
#
|
4
|
-
class
|
4
|
+
module Checkers
|
5
|
+
# This class checks PresenceValidator
|
6
|
+
class PresenceValidationChecker < BaseChecker
|
5
7
|
WEAK_OPTIONS = %i[allow_nil allow_blank if unless].freeze
|
6
8
|
# Message templates
|
7
|
-
CONSTRAINT_MISSING = 'should be required in the database'
|
8
|
-
POSSIBLE_NULL = 'is required but possible null value insert'
|
9
|
+
CONSTRAINT_MISSING = 'should be required in the database'
|
10
|
+
POSSIBLE_NULL = 'is required but possible null value insert'
|
11
|
+
|
12
|
+
private
|
9
13
|
|
10
14
|
# Table of possible statuses
|
11
15
|
# | allow_nil/allow_blank/if/unless | database | status |
|
@@ -14,23 +18,38 @@ module DatabaseConsistency
|
|
14
18
|
# | at least one provided | optional | ok |
|
15
19
|
# | all missed | required | ok |
|
16
20
|
# | all missed | optional | fail |
|
17
|
-
|
21
|
+
#
|
22
|
+
# We skip check when:
|
23
|
+
# - there is no column in the database with given name
|
24
|
+
def check
|
25
|
+
return unless column
|
26
|
+
|
18
27
|
can_be_null = column.null
|
19
28
|
has_weak_option = validator.options.slice(*WEAK_OPTIONS).any?
|
20
29
|
|
21
30
|
if can_be_null == has_weak_option
|
22
|
-
|
31
|
+
report_template(:ok)
|
23
32
|
elsif can_be_null
|
24
|
-
|
33
|
+
report_template(:fail, CONSTRAINT_MISSING)
|
25
34
|
else
|
26
|
-
|
35
|
+
report_template(:fail, POSSIBLE_NULL)
|
27
36
|
end
|
28
37
|
end
|
29
38
|
|
30
|
-
|
39
|
+
def column
|
40
|
+
@column ||= Helper.find_field(table_or_model, column_or_attribute.to_s)
|
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
|
31
50
|
|
32
|
-
def
|
33
|
-
|
51
|
+
def validator
|
52
|
+
opts[:validator]
|
34
53
|
end
|
35
54
|
end
|
36
55
|
end
|
@@ -1,19 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'yaml'
|
2
4
|
|
3
5
|
module DatabaseConsistency
|
4
6
|
# The class to access configuration options
|
5
7
|
class Configuration
|
6
|
-
|
7
|
-
|
8
|
+
CONFIGURATION_PATH = '.database_consistency.yml'
|
9
|
+
|
10
|
+
def initialize(filepath = CONFIGURATION_PATH)
|
11
|
+
@configuration = if filepath && File.exist?(filepath)
|
8
12
|
YAML.load_file(filepath)
|
9
13
|
else
|
10
14
|
{}
|
11
15
|
end
|
12
16
|
end
|
13
17
|
|
14
|
-
|
15
|
-
|
16
|
-
|
18
|
+
# @return [Boolean]
|
19
|
+
def enabled?(*path)
|
20
|
+
current = configuration
|
21
|
+
|
22
|
+
path.each do |key|
|
23
|
+
current = current[key.to_s]
|
24
|
+
return true unless current.is_a?(Hash)
|
25
|
+
|
26
|
+
next if current['enabled'].nil?
|
27
|
+
|
28
|
+
return false unless current['enabled']
|
29
|
+
end
|
30
|
+
|
31
|
+
true
|
17
32
|
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :configuration
|
18
37
|
end
|
19
38
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DatabaseConsistency
|
2
4
|
# The module contains helper methods
|
3
5
|
module Helper
|
@@ -25,13 +27,6 @@ module DatabaseConsistency
|
|
25
27
|
model.columns.select.find { |field| field.name == attribute }
|
26
28
|
end
|
27
29
|
|
28
|
-
# @return [String]
|
29
|
-
def message(model, column, template = nil)
|
30
|
-
str = "column #{column.name} of table #{model.table_name} of model #{model.name}"
|
31
|
-
str += " #{template}" if template
|
32
|
-
str
|
33
|
-
end
|
34
|
-
|
35
30
|
# @return [Boolean]
|
36
31
|
def check_inclusion?(array, element)
|
37
32
|
array.include?(element.to_s) || array.include?(element.to_sym)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
# The module for processors
|
5
|
+
module Processors
|
6
|
+
def self.reports(configuration)
|
7
|
+
[ModelsProcessor, TablesProcessor].flat_map do |processor|
|
8
|
+
processor.new(configuration).reports
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# The base class for processors
|
13
|
+
class BaseProcessor
|
14
|
+
attr_reader :configuration
|
15
|
+
|
16
|
+
# @param [DatabaseConsistency::Configuration] configuration
|
17
|
+
def initialize(configuration = nil)
|
18
|
+
@configuration = configuration || Configuration.new
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Array<Hash>]
|
22
|
+
def reports
|
23
|
+
@reports ||= check
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def check
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Processors
|
5
|
+
# The class to process all comparators
|
6
|
+
class ModelsProcessor < BaseProcessor
|
7
|
+
CHECKERS = {
|
8
|
+
presence: Checkers::PresenceValidationChecker
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# @return [Array<Hash>]
|
14
|
+
def check
|
15
|
+
Helper.parent_models.flat_map do |model|
|
16
|
+
model.validators.flat_map do |validator|
|
17
|
+
next unless (checker_class = CHECKERS[validator.kind])
|
18
|
+
|
19
|
+
validator.attributes.map do |attribute|
|
20
|
+
checker = checker_class.new(model, attribute, validator: validator)
|
21
|
+
checker.report if checker.enabled?(configuration)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end.compact
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Processors
|
5
|
+
# The class to process missing validators
|
6
|
+
class TablesProcessor < BaseProcessor
|
7
|
+
CHECKERS = [
|
8
|
+
Checkers::NullConstraintChecker
|
9
|
+
].freeze
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def check
|
14
|
+
Helper.parent_models.flat_map do |model|
|
15
|
+
model.columns.flat_map do |column|
|
16
|
+
CHECKERS.map do |checker_class|
|
17
|
+
checker = checker_class.new(model, column)
|
18
|
+
checker.report if checker.enabled?(configuration)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end.compact
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DatabaseConsistency
|
2
4
|
module Writers
|
3
5
|
# The base class for writers
|
@@ -10,7 +12,7 @@ module DatabaseConsistency
|
|
10
12
|
end
|
11
13
|
|
12
14
|
def write?(status)
|
13
|
-
status == :fail
|
15
|
+
status == :fail || debug?
|
14
16
|
end
|
15
17
|
|
16
18
|
def debug?
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DatabaseConsistency
|
2
4
|
# The module contains formatters
|
3
5
|
module Writers
|
@@ -12,16 +14,12 @@ module DatabaseConsistency
|
|
12
14
|
next unless write?(result[:status])
|
13
15
|
|
14
16
|
line(result)
|
15
|
-
end.tap(&:compact!).map(&:lstrip).delete_if(&:empty?).join(
|
16
|
-
end
|
17
|
-
|
18
|
-
def delimiter
|
19
|
-
debug? ? "\n\n" : "\n"
|
17
|
+
end.tap(&:compact!).map(&:lstrip).delete_if(&:empty?).join("\n")
|
20
18
|
end
|
21
19
|
|
22
20
|
def line(result)
|
23
|
-
"#{result
|
24
|
-
|
21
|
+
"#{result.status} #{result.table_or_model_name} #{result.column_or_attribute_name} #{result.message}".tap do |s|
|
22
|
+
s.concat " (checker: #{result.checker_name})" if debug?
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
data/lib/database_consistency.rb
CHANGED
@@ -1,34 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_record'
|
2
4
|
|
3
5
|
require 'database_consistency/version'
|
4
|
-
require 'database_consistency/report'
|
5
6
|
require 'database_consistency/helper'
|
6
7
|
require 'database_consistency/configuration'
|
7
8
|
|
8
9
|
require 'database_consistency/writers/base_writer'
|
9
10
|
require 'database_consistency/writers/simple_writer'
|
10
11
|
|
11
|
-
require 'database_consistency/
|
12
|
-
require 'database_consistency/
|
13
|
-
require 'database_consistency/
|
12
|
+
require 'database_consistency/checkers/base_checker'
|
13
|
+
require 'database_consistency/checkers/presence_validation_checker'
|
14
|
+
require 'database_consistency/checkers/null_constraint_checker'
|
14
15
|
|
15
|
-
require 'database_consistency/
|
16
|
-
require 'database_consistency/
|
17
|
-
require 'database_consistency/
|
16
|
+
require 'database_consistency/processors/base_processor'
|
17
|
+
require 'database_consistency/processors/models_processor'
|
18
|
+
require 'database_consistency/processors/tables_processor'
|
18
19
|
|
19
20
|
# The root module
|
20
21
|
module DatabaseConsistency
|
21
|
-
CONFIGURATION_PATH = '.database_consistency.yml'.freeze
|
22
|
-
|
23
|
-
PROCESSORS = [
|
24
|
-
ValidatorsProcessor,
|
25
|
-
DatabaseProcessor
|
26
|
-
].freeze
|
27
|
-
|
28
22
|
class << self
|
29
23
|
def run
|
30
24
|
Helper.load_environment!
|
31
25
|
|
26
|
+
configuration = Configuration.new
|
27
|
+
reports = Processors.reports(configuration)
|
28
|
+
|
32
29
|
Writers::SimpleWriter.write(
|
33
30
|
reports,
|
34
31
|
ENV['LOG_LEVEL'] || 'INFO'
|
@@ -36,21 +33,5 @@ module DatabaseConsistency
|
|
36
33
|
|
37
34
|
reports.empty? ? 0 : 1
|
38
35
|
end
|
39
|
-
|
40
|
-
def enabled_processors
|
41
|
-
@enabled_processors ||= PROCESSORS.select { |processor| configuration.enabled?(processor) }
|
42
|
-
end
|
43
|
-
|
44
|
-
def reports
|
45
|
-
@reports ||= enabled_processors.map(&:new).flat_map(&:reports)
|
46
|
-
end
|
47
|
-
|
48
|
-
def configuration
|
49
|
-
@configuration ||= if File.exist?(CONFIGURATION_PATH)
|
50
|
-
Configuration.new(CONFIGURATION_PATH)
|
51
|
-
else
|
52
|
-
Configuration.new
|
53
|
-
end
|
54
|
-
end
|
55
36
|
end
|
56
37
|
end
|
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.3.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:
|
11
|
+
date: 2019-01-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -110,15 +110,14 @@ extra_rdoc_files: []
|
|
110
110
|
files:
|
111
111
|
- bin/database_consistency
|
112
112
|
- lib/database_consistency.rb
|
113
|
-
- lib/database_consistency/
|
114
|
-
- lib/database_consistency/
|
115
|
-
- lib/database_consistency/
|
116
|
-
- lib/database_consistency/comparators/presence_comparator.rb
|
113
|
+
- lib/database_consistency/checkers/base_checker.rb
|
114
|
+
- lib/database_consistency/checkers/null_constraint_checker.rb
|
115
|
+
- lib/database_consistency/checkers/presence_validation_checker.rb
|
117
116
|
- lib/database_consistency/configuration.rb
|
118
|
-
- lib/database_consistency/database_processor.rb
|
119
117
|
- lib/database_consistency/helper.rb
|
120
|
-
- lib/database_consistency/
|
121
|
-
- lib/database_consistency/
|
118
|
+
- lib/database_consistency/processors/base_processor.rb
|
119
|
+
- lib/database_consistency/processors/models_processor.rb
|
120
|
+
- lib/database_consistency/processors/tables_processor.rb
|
122
121
|
- lib/database_consistency/version.rb
|
123
122
|
- lib/database_consistency/writers/base_writer.rb
|
124
123
|
- lib/database_consistency/writers/simple_writer.rb
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module DatabaseConsistency
|
2
|
-
module ColumnVerifiers
|
3
|
-
# The base class for column verifiers
|
4
|
-
class BaseVerifier
|
5
|
-
attr_reader :model, :column
|
6
|
-
|
7
|
-
delegate :result, to: :report
|
8
|
-
|
9
|
-
def initialize(model, column)
|
10
|
-
@model = model
|
11
|
-
@column = column
|
12
|
-
end
|
13
|
-
|
14
|
-
def report
|
15
|
-
Report.new(column: column)
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.verify(model, column)
|
19
|
-
new(model, column).verify
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,45 +0,0 @@
|
|
1
|
-
module DatabaseConsistency
|
2
|
-
module ColumnVerifiers
|
3
|
-
# This class verifies that column needs a presence validator
|
4
|
-
class PresenceMissingVerifier < BaseVerifier
|
5
|
-
VALIDATOR_MISSING = 'is required but do not have presence validator'.freeze
|
6
|
-
|
7
|
-
def verify
|
8
|
-
return if skip? || presence_validator? || inclusion_validator? || belongs_to_reflection?
|
9
|
-
|
10
|
-
result(:fail, Helper.message(model, column, VALIDATOR_MISSING))
|
11
|
-
end
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
def skip?
|
16
|
-
column.null ||
|
17
|
-
!column.default.nil? ||
|
18
|
-
column.name == model.primary_key ||
|
19
|
-
timestamp_field?
|
20
|
-
end
|
21
|
-
|
22
|
-
def timestamp_field?
|
23
|
-
model.record_timestamps? && %w[created_at updated_at].include?(column.name)
|
24
|
-
end
|
25
|
-
|
26
|
-
def presence_validator?
|
27
|
-
model.validators.grep(ActiveModel::Validations::PresenceValidator).any? do |validator|
|
28
|
-
Helper.check_inclusion?(validator.attributes, column.name)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def inclusion_validator?
|
33
|
-
model.validators.grep(ActiveModel::Validations::InclusionValidator).any? do |validator|
|
34
|
-
Helper.check_inclusion?(validator.attributes, column.name)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def belongs_to_reflection?
|
39
|
-
model.reflect_on_all_associations.grep(ActiveRecord::Reflection::BelongsToReflection).any? do |reflection|
|
40
|
-
Helper.check_inclusion?([reflection.foreign_key, reflection.foreign_type], column.name)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
module DatabaseConsistency
|
2
|
-
module Comparators
|
3
|
-
# The base class for comparators
|
4
|
-
class BaseComparator
|
5
|
-
attr_reader :validator, :model, :column
|
6
|
-
|
7
|
-
delegate :result, to: :report
|
8
|
-
|
9
|
-
def initialize(validator, model, column)
|
10
|
-
@validator = validator
|
11
|
-
@model = model
|
12
|
-
@column = column
|
13
|
-
end
|
14
|
-
|
15
|
-
def report
|
16
|
-
Report.new(validator: validator, column: column)
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.compare(validator, model, column)
|
20
|
-
new(validator, model, column).compare
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module DatabaseConsistency
|
2
|
-
# The class to process missing validators
|
3
|
-
class DatabaseProcessor
|
4
|
-
VERIFIERS = [
|
5
|
-
ColumnVerifiers::PresenceMissingVerifier
|
6
|
-
].freeze
|
7
|
-
|
8
|
-
def reports
|
9
|
-
Helper.parent_models.flat_map do |model|
|
10
|
-
model.columns.flat_map do |column|
|
11
|
-
VERIFIERS.map do |verifier|
|
12
|
-
verifier.verify(model, column)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end.compact
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module DatabaseConsistency
|
2
|
-
# This class outputs the report result
|
3
|
-
class Report
|
4
|
-
attr_reader :opts
|
5
|
-
|
6
|
-
def initialize(opts = {})
|
7
|
-
@opts = opts
|
8
|
-
end
|
9
|
-
|
10
|
-
def result(status, message)
|
11
|
-
{
|
12
|
-
status: status,
|
13
|
-
message: message
|
14
|
-
}.tap { |hash| hash[:opts] = opts unless opts.empty? }
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module DatabaseConsistency
|
2
|
-
# The class to process all comparators
|
3
|
-
class ValidatorsProcessor
|
4
|
-
COMPARATORS = {
|
5
|
-
presence: DatabaseConsistency::Comparators::PresenceComparator
|
6
|
-
}.freeze
|
7
|
-
|
8
|
-
def reports
|
9
|
-
Helper.parent_models.flat_map do |model|
|
10
|
-
model.validators.flat_map do |validator|
|
11
|
-
next unless (comparator = COMPARATORS[validator.kind])
|
12
|
-
|
13
|
-
validator.attributes.map do |attribute|
|
14
|
-
next unless (column = Helper.find_field(model, attribute.to_s))
|
15
|
-
|
16
|
-
comparator.compare(validator, model, column)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end.compact
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|