database_consistency 0.8.2 → 0.8.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/lib/database_consistency.rb +5 -0
- data/lib/database_consistency/checkers/association_checkers/foreign_key_type_checker.rb +117 -0
- data/lib/database_consistency/checkers/column_checkers/length_constraint_checker.rb +7 -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 +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1dff50eb6317680dc36fc0260a90c4071bfada212d1124169437123b93d1f65
|
4
|
+
data.tar.gz: 1fcd504bfaed4674386b3af7d53d9d2d2bd2b1f28d2fb65cd3ae1c1c0f1f1eae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22aaae1a55a6c7bc6dd3027852cf8e8b0e94e970c06ac445455e1c5abe787be2a273ed9d7f2b7e9c13b5455f827b1bec82d02eb117eed2599c8e90ebd12719ab
|
7
|
+
data.tar.gz: e9203782f9364aa086c1ee20e03d6497c92ce17d5e5ece293390549ce4e4f3dbeb0ff07980a9be5548558d59527c4e2a5d0c3928b67991ca27d99fa5f9908dfb
|
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,117 @@
|
|
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 ||= (
|
55
|
+
if belongs_to_association?
|
56
|
+
association.foreign_key
|
57
|
+
else
|
58
|
+
association.active_record_primary_key
|
59
|
+
end
|
60
|
+
).to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [String]
|
64
|
+
def associated_key
|
65
|
+
@associated_key ||= (
|
66
|
+
if belongs_to_association?
|
67
|
+
association.active_record_primary_key
|
68
|
+
else
|
69
|
+
association.foreign_key
|
70
|
+
end
|
71
|
+
).to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [ActiveRecord::ConnectionAdapters::Column]
|
75
|
+
def base_column
|
76
|
+
@base_column ||= column(association.active_record, base_key)
|
77
|
+
end
|
78
|
+
|
79
|
+
# @return [ActiveRecord::ConnectionAdapters::Column]
|
80
|
+
def associated_column
|
81
|
+
@associated_column ||= column(association.klass, associated_key)
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return [DatabaseConsistency::Databases::Factory]
|
85
|
+
def database_factory
|
86
|
+
@database_factory ||= Databases::Factory.new(association.active_record.connection.adapter_name)
|
87
|
+
end
|
88
|
+
|
89
|
+
# @param [ActiveRecord::Base] model
|
90
|
+
# @param [String] column_name
|
91
|
+
#
|
92
|
+
# @return [ActiveRecord::ConnectionAdapters::Column]
|
93
|
+
def column(model, column_name)
|
94
|
+
model.connection.columns(model.table_name).find { |column| column.name == column_name }
|
95
|
+
end
|
96
|
+
|
97
|
+
# @param [ActiveRecord::ConnectionAdapters::Column] column
|
98
|
+
#
|
99
|
+
# @return [String]
|
100
|
+
def type(column)
|
101
|
+
column.sql_type
|
102
|
+
end
|
103
|
+
|
104
|
+
# @param [ActiveRecord::ConnectionAdapters::Column]
|
105
|
+
#
|
106
|
+
# @return [String]
|
107
|
+
def converted_type(column)
|
108
|
+
database_factory.type(type(column)).convert
|
109
|
+
end
|
110
|
+
|
111
|
+
# @return [Boolean]
|
112
|
+
def belongs_to_association?
|
113
|
+
association.macro == :belongs_to
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
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
|
@@ -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.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: 2020-
|
11
|
+
date: 2020-09-23 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_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
|
@@ -175,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
179
|
- !ruby/object:Gem::Version
|
176
180
|
version: '0'
|
177
181
|
requirements: []
|
178
|
-
rubygems_version: 3.
|
182
|
+
rubygems_version: 3.0.8
|
179
183
|
signing_key:
|
180
184
|
specification_version: 4
|
181
185
|
summary: Provide an easy way to check the consistency of the database constraints
|