database_consistency 1.1.2 → 1.1.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: dc9590f6144cef94d2046971c259258a8428cddd9b7083f7225a04d3c5c19933
4
- data.tar.gz: 6a272364d20b0fdfb9be5155dc0e56c3becc4fc98ef0dc777537ae5a5f1c5f0f
3
+ metadata.gz: 482d9a79d349bbe6d49233cf8ffb05accbf61c9d30db72b8e2d9c5d5517602a6
4
+ data.tar.gz: 14309f3ef7aab0e6aa0d20e4f9a0c0148a2be89e30d09235fb558cc55085d38d
5
5
  SHA512:
6
- metadata.gz: dcb1f1eae53a0d783c6e83a6f3ca77ae4f5ab4c3086291b268e0e3745abce10d760214c313687db98086ccee9322fe88cbcaade5def8290dea17939447534868
7
- data.tar.gz: 90fcc238b355768e1f2e7b11909b7eb5f2d679b724be2ef3264a158fcdeb916aa751b97e6e14ecfae39f5de985e83887f862565e86c716bb3e43391a463b3a8a
6
+ metadata.gz: 7abe1d2b81e1f557b20da7c61a33c9fab8ebe709dd459e78ec16c4b088b3a457b094985428d72f882c6e946d028a444bfaf6aa1058764f03aef6bb54c2ecd89c
7
+ data.tar.gz: d2b930d6d7a0f639c9f7f139f207d90df85af5409d918d77cf7f3c781602844220996eb3fef6639f0c490f33235efa74f92817a254eb5ee4930ec429c4a4c02d
@@ -2,18 +2,23 @@
2
2
 
3
3
  module DatabaseConsistency
4
4
  module Checkers
5
- # This class checks if required +belongs_to+ has foreign key constraint
6
- class BelongsToPresenceChecker < ValidatorChecker
7
- MISSING_FOREIGN_KEY = 'model should have proper foreign key in the database'
5
+ # This class checks if non polymorphic +belongs_to+ association has foreign key constraint
6
+ class ForeignKeyChecker < AssociationChecker
7
+ MISSING_FOREIGN_KEY = 'should have foreign key in the database'
8
8
 
9
9
  private
10
10
 
11
11
  # We skip check when:
12
- # - validator is a not a presence validator
13
- # - there is no belongs_to association with given name
14
- # - belongs_to association is polymorphic
12
+ # - association isn't belongs_to association
13
+ # - association is polymorphic
15
14
  def preconditions
16
- validator.kind == :presence && association && association.belongs_to? && !association.polymorphic?
15
+ supported? && association.belongs_to? && !association.polymorphic?
16
+ end
17
+
18
+ def supported?
19
+ return false if ActiveRecord::VERSION::MAJOR < 5 && ActiveRecord::Base.connection_config[:adapter] == 'sqlite3'
20
+
21
+ true
17
22
  end
18
23
 
19
24
  # Table of possible statuses
@@ -28,10 +33,6 @@ module DatabaseConsistency
28
33
  report_template(:fail, MISSING_FOREIGN_KEY)
29
34
  end
30
35
  end
31
-
32
- def association
33
- @association ||= model.reflect_on_association(attribute)
34
- end
35
36
  end
36
37
  end
37
38
  end
@@ -42,6 +42,8 @@ module DatabaseConsistency
42
42
  else
43
43
  report_template(:fail, render_text)
44
44
  end
45
+ rescue Errors::MissingField => e
46
+ report_template(:fail, e.message)
45
47
  end
46
48
 
47
49
  # @return [String]
@@ -101,8 +103,8 @@ module DatabaseConsistency
101
103
 
102
104
  # @return [String]
103
105
  def missing_field_error(table_name, column_name)
104
- "Association (#{association.name}) of class (#{association.active_record}) relies on "\
105
- "field (#{column_name}) of table (#{table_name}) but it is missing."
106
+ "association (#{association.name}) of class (#{association.active_record}) relies on "\
107
+ "field (#{column_name}) of table (#{table_name}) but it is missing"
106
108
  end
107
109
 
108
110
  # @param [ActiveRecord::ConnectionAdapters::Column] column
@@ -6,6 +6,8 @@ module DatabaseConsistency
6
6
  class NullConstraintChecker < ColumnChecker
7
7
  # Message templates
8
8
  VALIDATOR_MISSING = 'column is required in the database but do not have presence validator'
9
+ ASSOCIATION_VALIDATOR_MISSING = 'column is required in the database but do '\
10
+ 'not have presence validator for association (%a_n)'
9
11
 
10
12
  private
11
13
 
@@ -26,21 +28,23 @@ module DatabaseConsistency
26
28
  # | missing | fail |
27
29
  #
28
30
  # We consider PresenceValidation, InclusionValidation, ExclusionValidation, NumericalityValidator with nil,
29
- # or BelongsTo association using this column
31
+ # or required BelongsTo association using this column
30
32
  def check
31
33
  if valid?
32
34
  report_template(:ok)
35
+ elsif belongs_to_association
36
+ report_template(:fail, ASSOCIATION_VALIDATOR_MISSING.gsub('%a_n', belongs_to_association.name.to_s))
33
37
  else
34
38
  report_template(:fail, VALIDATOR_MISSING)
35
39
  end
36
40
  end
37
41
 
38
42
  def valid?
39
- validator?(ActiveModel::Validations::PresenceValidator) ||
40
- validator?(ActiveModel::Validations::InclusionValidator) ||
43
+ validator?(:presence, column.name) ||
44
+ validator?(:inclusion, column.name) ||
41
45
  numericality_validator_without_allow_nil? ||
42
46
  nil_exclusion_validator? ||
43
- belongs_to_association?
47
+ (belongs_to_association && validator?(:presence, belongs_to_association.name))
44
48
  end
45
49
 
46
50
  def primary_field?
@@ -52,28 +56,30 @@ module DatabaseConsistency
52
56
  end
53
57
 
54
58
  def nil_exclusion_validator?
55
- model.validators.grep(ActiveModel::Validations::ExclusionValidator).any? do |validator|
56
- Helper.check_inclusion?(validator.attributes, column.name) &&
59
+ model.validators.any? do |validator|
60
+ validator.kind == :exclusion &&
61
+ Helper.check_inclusion?(validator.attributes, column.name) &&
57
62
  validator.options[:in].include?(nil)
58
63
  end
59
64
  end
60
65
 
61
66
  def numericality_validator_without_allow_nil?
62
- model.validators.grep(ActiveModel::Validations::NumericalityValidator).any? do |validator|
63
- Helper.check_inclusion?(validator.attributes, column.name) &&
67
+ model.validators.any? do |validator|
68
+ validator.kind == :numericality &&
69
+ Helper.check_inclusion?(validator.attributes, column.name) &&
64
70
  !validator.options[:allow_nil]
65
71
  end
66
72
  end
67
73
 
68
- def validator?(validator_class)
69
- model.validators.grep(validator_class).any? do |validator|
70
- Helper.check_inclusion?(validator.attributes, column.name)
74
+ def validator?(kind, attribute)
75
+ model.validators.any? do |validator|
76
+ validator.kind == kind && Helper.check_inclusion?(validator.attributes, attribute)
71
77
  end
72
78
  end
73
79
 
74
- def belongs_to_association?
75
- model.reflect_on_all_associations.grep(ActiveRecord::Reflection::BelongsToReflection).any? do |r|
76
- Helper.check_inclusion?([r.foreign_key, r.foreign_type], column.name)
80
+ def belongs_to_association
81
+ @belongs_to_association ||= model.reflect_on_all_associations.find do |r|
82
+ r.belongs_to? && Helper.check_inclusion?([r.foreign_key, r.foreign_type], column.name)
77
83
  end
78
84
  end
79
85
  end
@@ -7,6 +7,7 @@ module DatabaseConsistency
7
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
+ ASSOCIATION_FOREIGN_KEY_CONSTRAINT_MISSING = 'association foreign key column should be required in the database'
10
11
  POSSIBLE_NULL = 'column is required but there is possible null value insert'
11
12
 
12
13
  private
@@ -15,11 +16,9 @@ module DatabaseConsistency
15
16
  validator.kind == :presence
16
17
  end
17
18
 
18
- # We skip check when:
19
- # - there is no presence validators
20
- # - there is no column in the database with given name
19
+ # We skip the check when there are no presence validators
21
20
  def preconditions
22
- validators.any? && column
21
+ validators.any?
23
22
  end
24
23
 
25
24
  # Table of possible statuses
@@ -30,20 +29,52 @@ module DatabaseConsistency
30
29
  # | all missing | required | ok |
31
30
  # | all missing | optional | fail |
32
31
  def check
32
+ report_message
33
+ rescue Errors::MissingField => e
34
+ report_template(:fail, e.message)
35
+ end
36
+
37
+ def has_weak_option?
38
+ validators.all? { |validator| validator.options.slice(*WEAK_OPTIONS).any? }
39
+ end
40
+
41
+ def report_message
33
42
  can_be_null = column.null
34
- has_weak_option = validators.all? { |validator| validator.options.slice(*WEAK_OPTIONS).any? }
43
+ has_weak_option = has_weak_option?
35
44
 
36
- if can_be_null == has_weak_option
37
- report_template(:ok)
38
- elsif can_be_null
45
+ return report_template(:ok) if can_be_null == has_weak_option
46
+ return report_template(:fail, POSSIBLE_NULL) unless can_be_null
47
+
48
+ if regular_column
39
49
  report_template(:fail, CONSTRAINT_MISSING)
40
50
  else
41
- report_template(:fail, POSSIBLE_NULL)
51
+ report_template(:fail, ASSOCIATION_FOREIGN_KEY_CONSTRAINT_MISSING)
42
52
  end
43
53
  end
44
54
 
45
55
  def column
46
- @column ||= model.columns.select.find { |field| field.name == attribute.to_s }
56
+ @column ||= regular_column || association_reference_column ||
57
+ (raise Errors::MissingField, "column (#{attribute}) is missing in table (#{model.table_name}) but used for presence validation")
58
+ end
59
+
60
+ def regular_column
61
+ @regular_column ||= column_for_name(attribute.to_s)
62
+ end
63
+
64
+ def column_for_name(name)
65
+ model.columns.find { |field| field.name == name.to_s }
66
+ end
67
+
68
+ def association_reference_column
69
+ return unless association_reflection
70
+
71
+ column_for_name(association_reflection.foreign_key)
72
+ end
73
+
74
+ def association_reflection
75
+ model
76
+ .reflect_on_all_associations
77
+ .find { |reflection| reflection.belongs_to? && reflection.name == attribute }
47
78
  end
48
79
  end
49
80
  end
@@ -6,6 +6,7 @@ module DatabaseConsistency
6
6
  class AssociationsProcessor < BaseProcessor
7
7
  CHECKERS = [
8
8
  Checkers::MissingIndexChecker,
9
+ Checkers::ForeignKeyChecker,
9
10
  Checkers::ForeignKeyTypeChecker
10
11
  ].freeze
11
12
 
@@ -5,7 +5,6 @@ module DatabaseConsistency
5
5
  # The class to process validators
6
6
  class ValidatorsProcessor < BaseProcessor
7
7
  CHECKERS = [
8
- Checkers::BelongsToPresenceChecker,
9
8
  Checkers::MissingUniqueIndexChecker
10
9
  ].freeze
11
10
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseConsistency
4
- VERSION = '1.1.2'
4
+ VERSION = '1.1.6'
5
5
  end
@@ -19,6 +19,7 @@ require 'database_consistency/checkers/base_checker'
19
19
 
20
20
  require 'database_consistency/checkers/association_checkers/association_checker'
21
21
  require 'database_consistency/checkers/association_checkers/missing_index_checker'
22
+ require 'database_consistency/checkers/association_checkers/foreign_key_checker'
22
23
  require 'database_consistency/checkers/association_checkers/foreign_key_type_checker'
23
24
 
24
25
  require 'database_consistency/checkers/column_checkers/column_checker'
@@ -27,7 +28,6 @@ require 'database_consistency/checkers/column_checkers/length_constraint_checker
27
28
  require 'database_consistency/checkers/column_checkers/primary_key_type_checker'
28
29
 
29
30
  require 'database_consistency/checkers/validator_checkers/validator_checker'
30
- require 'database_consistency/checkers/validator_checkers/belongs_to_presence_checker'
31
31
  require 'database_consistency/checkers/validator_checkers/missing_unique_index_checker'
32
32
 
33
33
  require 'database_consistency/checkers/validators_fraction_checkers/validators_fraction_checker'
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: 1.1.2
4
+ version: 1.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgeniy Demin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-03 00:00:00.000000000 Z
11
+ date: 2021-11-10 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_checker.rb
136
137
  - lib/database_consistency/checkers/association_checkers/foreign_key_type_checker.rb
137
138
  - lib/database_consistency/checkers/association_checkers/missing_index_checker.rb
138
139
  - lib/database_consistency/checkers/base_checker.rb
@@ -144,7 +145,6 @@ files:
144
145
  - lib/database_consistency/checkers/index_checkers/redundant_index_checker.rb
145
146
  - lib/database_consistency/checkers/index_checkers/redundant_unique_index_checker.rb
146
147
  - lib/database_consistency/checkers/index_checkers/unique_index_checker.rb
147
- - lib/database_consistency/checkers/validator_checkers/belongs_to_presence_checker.rb
148
148
  - lib/database_consistency/checkers/validator_checkers/missing_unique_index_checker.rb
149
149
  - lib/database_consistency/checkers/validator_checkers/validator_checker.rb
150
150
  - lib/database_consistency/checkers/validators_fraction_checkers/column_presence_checker.rb