database_consistency 0.8.1 → 0.8.6
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.rb +5 -0
- data/lib/database_consistency/checkers/association_checkers/foreign_key_type_checker.rb +105 -0
- data/lib/database_consistency/checkers/column_checkers/length_constraint_checker.rb +7 -1
- data/lib/database_consistency/checkers/column_checkers/null_constraint_checker.rb +9 -1
- data/lib/database_consistency/checkers/validator_checkers/missing_unique_index_checker.rb +7 -1
- data/lib/database_consistency/checkers/validators_fraction_checkers/column_presence_checker.rb +1 -1
- data/lib/database_consistency/databases/factory.rb +27 -0
- data/lib/database_consistency/databases/types/base.rb +22 -0
- data/lib/database_consistency/databases/types/sqlite.rb +22 -0
- data/lib/database_consistency/processors/associations_processor.rb +2 -1
- data/lib/database_consistency/rescue_error.rb +1 -1
- data/lib/database_consistency/version.rb +1 -1
- metadata +11 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4c8440238a874ddd2ae11563b8b034c64f94e467426ef85afcd8befdf9a55c2
|
4
|
+
data.tar.gz: 7e7275bf7029cf03a0a9d3e35975d1a927a7317d323c2c7db00eb22306f67aca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcd5d6ee89a07990da47ad72231aa5884ee8209f071f21a4ab9ae3521f5ae2bd85167b2b31d32052bff081cd240f5965b4eef3f814ab8ce1ee83ab480d9ccaff
|
7
|
+
data.tar.gz: 2ff692d1618d7ef53500ccf3f88bb361c856d62ea7f577ce66a1c18122af0f05c1c55d5023b3313c4a17f55dbd20d14a0193d6116575e1b551651ad5549829ff
|
data/lib/database_consistency.rb
CHANGED
@@ -10,10 +10,15 @@ require 'database_consistency/rescue_error'
|
|
10
10
|
require 'database_consistency/writers/base_writer'
|
11
11
|
require 'database_consistency/writers/simple_writer'
|
12
12
|
|
13
|
+
require 'database_consistency/databases/factory'
|
14
|
+
require 'database_consistency/databases/types/base'
|
15
|
+
require 'database_consistency/databases/types/sqlite'
|
16
|
+
|
13
17
|
require 'database_consistency/checkers/base_checker'
|
14
18
|
|
15
19
|
require 'database_consistency/checkers/association_checkers/association_checker'
|
16
20
|
require 'database_consistency/checkers/association_checkers/missing_index_checker'
|
21
|
+
require 'database_consistency/checkers/association_checkers/foreign_key_type_checker'
|
17
22
|
|
18
23
|
require 'database_consistency/checkers/column_checkers/column_checker'
|
19
24
|
require 'database_consistency/checkers/column_checkers/null_constraint_checker'
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Checkers
|
5
|
+
# This class checks if association's foreign key type is the same as associated model's primary key
|
6
|
+
class ForeignKeyTypeChecker < AssociationChecker
|
7
|
+
INCONSISTENT_TYPE = 'associated model key (%a_f) with type (%a_t) mismatches key (%b_f) with type (%b_t)'
|
8
|
+
|
9
|
+
TYPES = {
|
10
|
+
'serial' => 'integer',
|
11
|
+
'integer' => 'integer',
|
12
|
+
'int' => 'integer',
|
13
|
+
'bigserial' => 'bigint',
|
14
|
+
'bigint' => 'bigint',
|
15
|
+
'bigint unsigned' => 'bigint unsigned'
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# We skip check when:
|
21
|
+
# - association is polymorphic association
|
22
|
+
# - association has `through` option
|
23
|
+
# - associated class doesn't exist
|
24
|
+
def preconditions
|
25
|
+
!association.polymorphic? && association.through_reflection.nil? && association.klass.present?
|
26
|
+
rescue NameError
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
# Table of possible statuses
|
31
|
+
# | type | status |
|
32
|
+
# | ------------ | ------ |
|
33
|
+
# | consistent | ok |
|
34
|
+
# | inconsistent | fail |
|
35
|
+
def check
|
36
|
+
if converted_type(base_column) == converted_type(associated_column)
|
37
|
+
report_template(:ok)
|
38
|
+
else
|
39
|
+
report_template(:fail, render_text)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [String]
|
44
|
+
def render_text
|
45
|
+
INCONSISTENT_TYPE
|
46
|
+
.gsub('%a_t', type(associated_column))
|
47
|
+
.gsub('%a_f', associated_key)
|
48
|
+
.gsub('%b_t', type(base_column))
|
49
|
+
.gsub('%b_f', base_key)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [String]
|
53
|
+
def base_key
|
54
|
+
@base_key ||= belongs_to_association? ? association.foreign_key : association.active_record_primary_key
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [String]
|
58
|
+
def associated_key
|
59
|
+
@associated_key ||= belongs_to_association? ? association.active_record_primary_key : association.foreign_key
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [ActiveRecord::ConnectionAdapters::Column]
|
63
|
+
def base_column
|
64
|
+
@base_column ||= column(association.active_record, base_key)
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [ActiveRecord::ConnectionAdapters::Column]
|
68
|
+
def associated_column
|
69
|
+
@associated_column ||= column(association.klass, associated_key)
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [DatabaseConsistency::Databases::Factory]
|
73
|
+
def database_factory
|
74
|
+
@database_factory ||= Databases::Factory.new(association.active_record.connection.adapter_name)
|
75
|
+
end
|
76
|
+
|
77
|
+
# @param [ActiveRecord::Base] model
|
78
|
+
# @param [String] column_name
|
79
|
+
#
|
80
|
+
# @return [ActiveRecord::ConnectionAdapters::Column]
|
81
|
+
def column(model, column_name)
|
82
|
+
model.connection.columns(model.table_name).find { |column| column.name == column_name.to_s }
|
83
|
+
end
|
84
|
+
|
85
|
+
# @param [ActiveRecord::ConnectionAdapters::Column] column
|
86
|
+
#
|
87
|
+
# @return [String]
|
88
|
+
def type(column)
|
89
|
+
column.sql_type
|
90
|
+
end
|
91
|
+
|
92
|
+
# @param [ActiveRecord::ConnectionAdapters::Column]
|
93
|
+
#
|
94
|
+
# @return [String]
|
95
|
+
def converted_type(column)
|
96
|
+
database_factory.type(type(column)).convert
|
97
|
+
end
|
98
|
+
|
99
|
+
# @return [Boolean]
|
100
|
+
def belongs_to_association?
|
101
|
+
association.macro == :belongs_to
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -21,8 +21,14 @@ module DatabaseConsistency
|
|
21
21
|
# We skip check when:
|
22
22
|
# - column hasn't limit constraint
|
23
23
|
# - column insn't string nor text
|
24
|
+
# - column is array (PostgreSQL only)
|
24
25
|
def preconditions
|
25
|
-
!column.limit.nil? && %i[string text].include?(column.type)
|
26
|
+
!column.limit.nil? && %i[string text].include?(column.type) && !postgresql_array?
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] true if it is an array (PostgreSQL only)
|
30
|
+
def postgresql_array?
|
31
|
+
column.respond_to?(:array) && column.array
|
26
32
|
end
|
27
33
|
|
28
34
|
# Table of possible statuses
|
@@ -25,7 +25,7 @@ module DatabaseConsistency
|
|
25
25
|
# | provided | ok |
|
26
26
|
# | missing | fail |
|
27
27
|
#
|
28
|
-
# We consider PresenceValidation, InclusionValidation, ExclusionValidation with nil,
|
28
|
+
# We consider PresenceValidation, InclusionValidation, ExclusionValidation, NumericalityValidator with nil,
|
29
29
|
# or BelongsTo association using this column
|
30
30
|
def check
|
31
31
|
if valid?
|
@@ -38,6 +38,7 @@ module DatabaseConsistency
|
|
38
38
|
def valid?
|
39
39
|
validator?(ActiveModel::Validations::PresenceValidator) ||
|
40
40
|
validator?(ActiveModel::Validations::InclusionValidator) ||
|
41
|
+
numericality_validator_without_allow_nil? ||
|
41
42
|
nil_exclusion_validator? ||
|
42
43
|
belongs_to_association?
|
43
44
|
end
|
@@ -57,6 +58,13 @@ module DatabaseConsistency
|
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
61
|
+
def numericality_validator_without_allow_nil?
|
62
|
+
model.validators.grep(ActiveModel::Validations::NumericalityValidator).any? do |validator|
|
63
|
+
Helper.check_inclusion?(validator.attributes, column.name) &&
|
64
|
+
!validator.options[:allow_nil]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
60
68
|
def validator?(validator_class)
|
61
69
|
model.validators.grep(validator_class).any? do |validator|
|
62
70
|
Helper.check_inclusion?(validator.attributes, column.name)
|
@@ -48,7 +48,13 @@ module DatabaseConsistency
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def index_columns
|
51
|
-
@index_columns ||= ([wrapped_attribute_name] +
|
51
|
+
@index_columns ||= ([wrapped_attribute_name] + scope_columns).map(&:to_s)
|
52
|
+
end
|
53
|
+
|
54
|
+
def scope_columns
|
55
|
+
@scope_columns ||= Array.wrap(validator.options[:scope]).map do |scope_item|
|
56
|
+
model._reflect_on_association(scope_item)&.foreign_key || scope_item
|
57
|
+
end
|
52
58
|
end
|
53
59
|
|
54
60
|
def sorted_index_columns
|
data/lib/database_consistency/checkers/validators_fraction_checkers/column_presence_checker.rb
CHANGED
@@ -4,7 +4,7 @@ module DatabaseConsistency
|
|
4
4
|
module Checkers
|
5
5
|
# This class checks if presence validator has non-null constraint in the database
|
6
6
|
class ColumnPresenceChecker < ValidatorsFractionChecker
|
7
|
-
WEAK_OPTIONS = %i[allow_nil allow_blank if unless].freeze
|
7
|
+
WEAK_OPTIONS = %i[allow_nil allow_blank if unless on].freeze
|
8
8
|
# Message templates
|
9
9
|
CONSTRAINT_MISSING = 'column should be required in the database'
|
10
10
|
POSSIBLE_NULL = 'column is required but there is possible null value insert'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Databases
|
5
|
+
# Factory for database adapters
|
6
|
+
class Factory
|
7
|
+
attr_reader :adapter
|
8
|
+
|
9
|
+
# @param [String] adapter
|
10
|
+
def initialize(adapter)
|
11
|
+
@adapter = adapter
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [DatabaseConsistency::Databases::Types::Base]
|
15
|
+
def type(type)
|
16
|
+
sqlite? ? Types::Sqlite.new(type) : Types::Base.new(type)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# @return [Boolean]
|
22
|
+
def sqlite?
|
23
|
+
adapter == 'SQLite'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Databases
|
5
|
+
module Types
|
6
|
+
# Base wrapper for database types
|
7
|
+
class Base
|
8
|
+
attr_reader :type
|
9
|
+
|
10
|
+
# @param [String] type
|
11
|
+
def initialize(type)
|
12
|
+
@type = type
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [String]
|
16
|
+
def convert
|
17
|
+
type
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Databases
|
5
|
+
module Types
|
6
|
+
# Wraps types for SQLite database
|
7
|
+
class Sqlite < Base
|
8
|
+
TYPES = {
|
9
|
+
'bigserial' => 'bigint',
|
10
|
+
'bigint' => 'bigint',
|
11
|
+
'serial' => 'integer',
|
12
|
+
'integer' => 'integer'
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
# @return [String]
|
16
|
+
def convert
|
17
|
+
TYPES[type] || type
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
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.8.
|
4
|
+
version: 0.8.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evgeniy Demin
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -122,7 +122,7 @@ dependencies:
|
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '1.3'
|
125
|
-
description:
|
125
|
+
description:
|
126
126
|
email:
|
127
127
|
- lawliet.djez@gmail.com
|
128
128
|
executables:
|
@@ -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_type_checker.rb
|
136
137
|
- lib/database_consistency/checkers/association_checkers/missing_index_checker.rb
|
137
138
|
- lib/database_consistency/checkers/base_checker.rb
|
138
139
|
- lib/database_consistency/checkers/column_checkers/column_checker.rb
|
@@ -145,6 +146,9 @@ files:
|
|
145
146
|
- lib/database_consistency/checkers/validators_fraction_checkers/column_presence_checker.rb
|
146
147
|
- lib/database_consistency/checkers/validators_fraction_checkers/validators_fraction_checker.rb
|
147
148
|
- lib/database_consistency/configuration.rb
|
149
|
+
- lib/database_consistency/databases/factory.rb
|
150
|
+
- lib/database_consistency/databases/types/base.rb
|
151
|
+
- lib/database_consistency/databases/types/sqlite.rb
|
148
152
|
- lib/database_consistency/helper.rb
|
149
153
|
- lib/database_consistency/processors/associations_processor.rb
|
150
154
|
- lib/database_consistency/processors/base_processor.rb
|
@@ -160,7 +164,7 @@ homepage: https://github.com/djezzzl/database_consistency
|
|
160
164
|
licenses:
|
161
165
|
- MIT
|
162
166
|
metadata: {}
|
163
|
-
post_install_message:
|
167
|
+
post_install_message:
|
164
168
|
rdoc_options: []
|
165
169
|
require_paths:
|
166
170
|
- lib
|
@@ -175,9 +179,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
179
|
- !ruby/object:Gem::Version
|
176
180
|
version: '0'
|
177
181
|
requirements: []
|
178
|
-
|
179
|
-
|
180
|
-
signing_key:
|
182
|
+
rubygems_version: 3.0.8
|
183
|
+
signing_key:
|
181
184
|
specification_version: 4
|
182
185
|
summary: Provide an easy way to check the consistency of the database constraints
|
183
186
|
with the application validations.
|