database_consistency 0.8.1 → 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 20658faa1cdd6ae16c25dd7a9b2b85b608f81e2da8ffeb74f8f692ad5c8eb6cb
4
- data.tar.gz: 7a6c555f10de5253af9744eea4ada2c95525dd127d081e939ffa6ad6c4923bab
3
+ metadata.gz: e4c8440238a874ddd2ae11563b8b034c64f94e467426ef85afcd8befdf9a55c2
4
+ data.tar.gz: 7e7275bf7029cf03a0a9d3e35975d1a927a7317d323c2c7db00eb22306f67aca
5
5
  SHA512:
6
- metadata.gz: 95ff5510806f67c8e612c488c608f2685e1abebbb3ecba455e706db34a104ab9c1135cac0193692884a7cb9f61301c91e0a27ea07d61eb08a3b89ca3dd5d952d
7
- data.tar.gz: 75a78627604fec92c8fd3703c70fa35e125139e188adf1d8592a480fcef44a50a6b4f670f2eaae139dd07c03ca397745b437d44fcdc35f96ea44b41aaaab6b7d
6
+ metadata.gz: dcd5d6ee89a07990da47ad72231aa5884ee8209f071f21a4ab9ae3521f5ae2bd85167b2b31d32052bff081cd240f5965b4eef3f814ab8ce1ee83ab480d9ccaff
7
+ data.tar.gz: 2ff692d1618d7ef53500ccf3f88bb361c856d62ea7f577ce66a1c18122af0f05c1c55d5023b3313c4a17f55dbd20d14a0193d6116575e1b551651ad5549829ff
@@ -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] + Array.wrap(validator.options[:scope])).map(&:to_s)
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
@@ -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
@@ -5,7 +5,8 @@ module DatabaseConsistency
5
5
  # The class to process associations
6
6
  class AssociationsProcessor < BaseProcessor
7
7
  CHECKERS = [
8
- Checkers::MissingIndexChecker
8
+ Checkers::MissingIndexChecker,
9
+ Checkers::ForeignKeyTypeChecker
9
10
  ].freeze
10
11
 
11
12
  private
@@ -33,7 +33,7 @@ module DatabaseConsistency
33
33
  private
34
34
 
35
35
  def filename
36
- "database_consistency_#{Time.now.strftime('%Y_%m_%d_%H_%M_%S')}"
36
+ @filename ||= "database_consistency_#{Time.now.strftime('%Y_%m_%d_%H_%M_%S')}"
37
37
  end
38
38
  end
39
39
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseConsistency
4
- VERSION = '0.8.1'
4
+ VERSION = '0.8.6'
5
5
  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.1
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-06-03 00:00:00.000000000 Z
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
- rubyforge_project:
179
- rubygems_version: 2.7.9
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.