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 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.