database_consistency 1.5.2 → 1.6.0

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: 007b8fe2bddf76beb68b7b8f1aad6d2ccfd54619a52d359c243af7b52138c92a
4
- data.tar.gz: 7de86e161f98d852944dc499358c774bc5cb66e9c57f75c32a3b6cc7470e445c
3
+ metadata.gz: de35bff4be332ceca4a982d5cde674db49db44a53d3fce22e177cc561cfe6253
4
+ data.tar.gz: 9f519c9b7aef026da3033790b47690c513871b725b5271e77afa55f29014480f
5
5
  SHA512:
6
- metadata.gz: 3a340470481bc101839adaa2b144c4ed1862477739b8432d3610e0249299051997dd2ff066af606ac8dd56b1e53706369a753c38a9c30f719acad2add5483dc4
7
- data.tar.gz: 9bb40d5e967f4f5f75863ea3f6b57df96d22152d3453b0c4e394deaacb7e293a4905911fb21974dbdb465352d04b76676531b2318b1ca25226420ffc09d0fc2b
6
+ metadata.gz: bdbd9750c5a9f8470a0c73fbae78a50193b423367b934f244fe0132e0dc9af29011cf55b8ef67a6dbe22f7fee3b4bbc012069dac2ae329ccad6f43452f83c80d
7
+ data.tar.gz: 133abb119e8a80c25b6c4fe0936273dd328adbd6df7bc290ea16f97e695b6468f99a0b9c10bea0420dbc1788de88c73948ea429981b30812002a7b103b2f9ddf
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Checkers
5
+ # This class checks missing presence validator
6
+ class EnumValueChecker < ColumnChecker
7
+ Report = ReportBuilder.define(
8
+ DatabaseConsistency::Report,
9
+ :enum_values,
10
+ :declared_values
11
+ )
12
+
13
+ private
14
+
15
+ def report_template(status, declared_values, error_slug: nil)
16
+ Report.new(
17
+ status: status,
18
+ error_slug: error_slug,
19
+ error_message: nil,
20
+ enum_values: enum_column_values,
21
+ declared_values: declared_values,
22
+ **report_attributes
23
+ )
24
+ end
25
+
26
+ def preconditions
27
+ postgresql? && column.type == :enum && (enum || inclusion_validator)
28
+ end
29
+
30
+ def postgresql?
31
+ Helper.adapter == 'postgresql'
32
+ end
33
+
34
+ def check
35
+ [
36
+ (verify_enum if enum),
37
+ (verify_inclusion_validator if inclusion_validator)
38
+ ].compact
39
+ end
40
+
41
+ def enum_column_values
42
+ @enum_column_values ||= begin
43
+ _, values = model.connection.enum_types.find { |(enum, _)| enum == column.sql_type }
44
+ values.split(',').map(&:strip)
45
+ end
46
+ end
47
+
48
+ def verify_enum
49
+ values = enum.values.uniq
50
+
51
+ if enum_column_values == values
52
+ report_template(:ok, values)
53
+ else
54
+ report_template(:fail, values, error_slug: :enum_values_inconsistent_with_ar_enum)
55
+ end
56
+ end
57
+
58
+ def verify_inclusion_validator
59
+ values = validator_values(inclusion_validator).uniq
60
+
61
+ if enum_column_values == values
62
+ report_template(:ok, values)
63
+ else
64
+ report_template(:fail, values, error_slug: :enum_values_inconsistent_with_inclusion)
65
+ end
66
+ end
67
+
68
+ def validator_values(validator)
69
+ validator.options[:in] || validator.options[:within]
70
+ end
71
+
72
+ def simple_values_validator?(validator)
73
+ values = validator_values(validator)
74
+
75
+ values.is_a?(Array) && values.all? { |val| val.is_a?(String) }
76
+ end
77
+
78
+ def inclusion_validator
79
+ @inclusion_validator ||= model.validators.find do |validator|
80
+ validator.kind == :inclusion &&
81
+ Helper.check_inclusion?(validator.attributes, column.name) &&
82
+ simple_values_validator?(validator)
83
+ end
84
+ end
85
+
86
+ def enum
87
+ @enum ||= model.defined_enums[column.name]
88
+ end
89
+ end
90
+ end
91
+ end
@@ -18,22 +18,17 @@ module DatabaseConsistency
18
18
  validator.kind == :presence
19
19
  end
20
20
 
21
- # We skip the check when there are no presence validators
22
21
  def preconditions
23
- column && validators.any? && !association?
22
+ (regular_column || association) && validators.any?
24
23
  end
25
24
 
26
- def association?
27
- model._reflect_on_association(attribute)&.macro == :has_one
28
- end
29
-
30
- def report_template(status, error_message: nil, error_slug: nil)
25
+ def report_template(status, column_name:, error_slug: nil)
31
26
  Report.new(
32
27
  status: status,
33
28
  error_slug: error_slug,
34
- error_message: error_message,
29
+ error_message: nil,
35
30
  table_name: model.table_name.to_s,
36
- column_name: attribute.to_s,
31
+ column_name: column_name,
37
32
  **report_attributes
38
33
  )
39
34
  end
@@ -42,56 +37,42 @@ module DatabaseConsistency
42
37
  validators.all? { |validator| validator.options.slice(*WEAK_OPTIONS).any? }
43
38
  end
44
39
 
45
- def check # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
46
- can_be_null = column.null
47
- has_weak_option = weak_option?
40
+ def check
41
+ return analyse(attribute.to_s, type: :null_constraint_missing) if regular_column
48
42
 
49
- return report_template(:ok) if can_be_null == has_weak_option
50
- return report_template(:fail, error_slug: :possible_null) unless can_be_null
51
-
52
- if regular_column
53
- Report.new(
54
- status: :fail,
55
- error_slug: :null_constraint_missing,
56
- error_message: nil,
57
- table_name: model.table_name.to_s,
58
- column_name: attribute.to_s,
59
- **report_attributes
60
- )
61
- else
62
- Report.new(
63
- status: :fail,
64
- error_slug: :association_missing_null_constraint,
65
- error_message: nil,
66
- table_name: model.table_name.to_s,
67
- column_name: association_reflection.foreign_key.to_s,
68
- **report_attributes
69
- )
43
+ reports = [analyse(association.foreign_key.to_s, type: :association_missing_null_constraint)]
44
+ if association.polymorphic?
45
+ reports << analyse(association.foreign_type.to_s, type: :association_foreign_type_missing_null_constraint)
70
46
  end
71
- end
72
47
 
73
- def column
74
- @column ||= (regular_column || association_reference_column)
48
+ reports
75
49
  end
76
50
 
77
- def regular_column
78
- @regular_column ||= column_for_name(attribute.to_s)
79
- end
51
+ def analyse(column_name, type:)
52
+ field = column(column_name)
80
53
 
81
- def column_for_name(name)
82
- model.columns.find { |field| field.name == name.to_s }
83
- end
54
+ can_be_null = field.null
55
+ has_weak_option = weak_option?
56
+
57
+ return report_template(:ok, column_name: column_name) if can_be_null == has_weak_option
58
+ return report_template(:fail, error_slug: :possible_null, column_name: column_name) unless can_be_null
84
59
 
85
- def association_reference_column
86
- return unless association_reflection
60
+ report_template(:fail, error_slug: type, column_name: column_name)
61
+ end
87
62
 
88
- column_for_name(association_reflection.foreign_key)
63
+ def regular_column
64
+ @regular_column ||= column(attribute.to_s)
89
65
  end
90
66
 
91
- def association_reflection
92
- model
67
+ def association
68
+ @association ||=
69
+ model
93
70
  .reflect_on_all_associations
94
- .find { |reflection| reflection.belongs_to? && reflection.name == attribute }
71
+ .find { |reflection| reflection.belongs_to? && reflection.name.to_s == attribute.to_s }
72
+ end
73
+
74
+ def column(name)
75
+ model.columns.find { |field| field.name == name.to_s }
95
76
  end
96
77
  end
97
78
  end
@@ -18,7 +18,7 @@ module DatabaseConsistency
18
18
  next unless configuration.enabled?(model.name.to_s)
19
19
 
20
20
  Helper.first_level_associations(model).flat_map do |association|
21
- enabled_checkers.map do |checker_class|
21
+ enabled_checkers.flat_map do |checker_class|
22
22
  checker = checker_class.new(model, association)
23
23
  checker.report_if_enabled?(configuration)
24
24
  end
@@ -7,7 +7,8 @@ module DatabaseConsistency
7
7
  CHECKERS = [
8
8
  Checkers::NullConstraintChecker,
9
9
  Checkers::LengthConstraintChecker,
10
- Checkers::PrimaryKeyTypeChecker
10
+ Checkers::PrimaryKeyTypeChecker,
11
+ Checkers::EnumValueChecker
11
12
  ].freeze
12
13
 
13
14
  private
@@ -17,7 +18,7 @@ module DatabaseConsistency
17
18
  next unless configuration.enabled?(model.name.to_s)
18
19
 
19
20
  model.columns.flat_map do |column|
20
- enabled_checkers.map do |checker_class|
21
+ enabled_checkers.flat_map do |checker_class|
21
22
  checker = checker_class.new(model, column)
22
23
  checker.report_if_enabled?(configuration)
23
24
  end
@@ -15,7 +15,7 @@ module DatabaseConsistency
15
15
  next unless configuration.enabled?(model.name.to_s)
16
16
 
17
17
  model.defined_enums.keys.flat_map do |enum|
18
- enabled_checkers.map do |checker_class|
18
+ enabled_checkers.flat_map do |checker_class|
19
19
  checker = checker_class.new(model, enum)
20
20
  checker.report_if_enabled?(configuration)
21
21
  end
@@ -19,7 +19,7 @@ module DatabaseConsistency
19
19
  indexes = model.connection.indexes(model.table_name)
20
20
 
21
21
  indexes.flat_map do |index|
22
- enabled_checkers.map do |checker_class|
22
+ enabled_checkers.flat_map do |checker_class|
23
23
  checker = checker_class.new(model, index)
24
24
  checker.report_if_enabled?(configuration)
25
25
  end
@@ -18,7 +18,7 @@ module DatabaseConsistency
18
18
  model._validators.flat_map do |attribute, validators|
19
19
  next unless attribute
20
20
 
21
- enabled_checkers.map do |checker_class|
21
+ enabled_checkers.flat_map do |checker_class|
22
22
  checker = checker_class.new(model, attribute, validators)
23
23
  checker.report_if_enabled?(configuration)
24
24
  end
@@ -19,7 +19,7 @@ module DatabaseConsistency
19
19
  next unless validator.respond_to?(:attributes)
20
20
 
21
21
  validator.attributes.flat_map do |attribute|
22
- enabled_checkers.map do |checker_class|
22
+ enabled_checkers.flat_map do |checker_class|
23
23
  checker = checker_class.new(model, attribute, validator)
24
24
  checker.report_if_enabled?(configuration)
25
25
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseConsistency
4
- VERSION = '1.5.2'
4
+ VERSION = '1.6.0'
5
5
  end
@@ -8,6 +8,7 @@ module DatabaseConsistency
8
8
  SLUG_TO_GENERATOR = {
9
9
  association_missing_index: Autofix::AssociationMissingIndex,
10
10
  association_missing_null_constraint: Autofix::NullConstraintMissing,
11
+ association_foreign_type_missing_null_constraint: Autofix::NullConstraintMissing,
11
12
  has_one_missing_unique_index: Autofix::HasOneMissingUniqueIndex,
12
13
  inconsistent_types: Autofix::InconsistentTypes,
13
14
  missing_foreign_key: Autofix::MissingForeignKey,
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Writers
5
+ module Simple
6
+ class AssociationForeignTypeMissingNullConstraint < Base # :nodoc:
7
+ private
8
+
9
+ def template
10
+ 'association foreign type column should be required in the database'
11
+ end
12
+
13
+ def unique_attributes
14
+ {
15
+ table_name: report.table_name,
16
+ column_name: report.column_name
17
+ }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Writers
5
+ module Simple
6
+ class EnumValuesInconsistentWithArEnum < Base # :nodoc:
7
+ private
8
+
9
+ def template
10
+ 'enum has [%<enum_values>s] values but ActiveRecord enum has [%<declared_values>s] values'
11
+ end
12
+
13
+ def attributes
14
+ {
15
+ enum_values: report.enum_values.join(', '),
16
+ declared_values: report.declared_values.join(', ')
17
+ }
18
+ end
19
+
20
+ def unique_attributes
21
+ {
22
+ table_or_model_name: report.table_or_model_name,
23
+ column_or_attribute_name: report.column_or_attribute_name,
24
+ ar_enum: true
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Writers
5
+ module Simple
6
+ class EnumValuesInconsistentWithInclusion < Base # :nodoc:
7
+ private
8
+
9
+ def template
10
+ 'enum has [%<enum_values>s] values but ActiveRecord inclusion validation has [%<declared_values>s] values'
11
+ end
12
+
13
+ def attributes
14
+ {
15
+ enum_values: report.enum_values.join(', '),
16
+ declared_values: report.declared_values.join(', ')
17
+ }
18
+ end
19
+
20
+ def unique_attributes
21
+ {
22
+ table_or_model_name: report.table_or_model_name,
23
+ column_or_attribute_name: report.column_or_attribute_name,
24
+ inclusion: true
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -8,6 +8,7 @@ module DatabaseConsistency
8
8
  SLUG_TO_WRITER = {
9
9
  association_missing_index: Simple::AssociationMissingIndex,
10
10
  association_missing_null_constraint: Simple::AssociationMissingNullConstraint,
11
+ association_foreign_type_missing_null_constraint: Simple::AssociationForeignTypeMissingNullConstraint,
11
12
  has_one_missing_unique_index: Simple::HasOneMissingUniqueIndex,
12
13
  inconsistent_types: Simple::InconsistentTypes,
13
14
  length_validator_greater_limit: Simple::LengthValidatorGreaterLimit,
@@ -24,7 +25,9 @@ module DatabaseConsistency
24
25
  redundant_unique_index: Simple::RedundantUniqueIndex,
25
26
  small_primary_key: Simple::SmallPrimaryKey,
26
27
  inconsistent_enum_type: Simple::InconsistentEnumType,
27
- missing_foreign_key_cascade: Simple::MissingForeignKeyCascade
28
+ missing_foreign_key_cascade: Simple::MissingForeignKeyCascade,
29
+ enum_values_inconsistent_with_ar_enum: Simple::EnumValuesInconsistentWithArEnum,
30
+ enum_values_inconsistent_with_inclusion: Simple::EnumValuesInconsistentWithInclusion
28
31
  }.freeze
29
32
 
30
33
  def write
@@ -22,6 +22,7 @@ require 'database_consistency/writers/simple/missing_foreign_key_cascade'
22
22
  require 'database_consistency/writers/simple/redundant_unique_index'
23
23
  require 'database_consistency/writers/simple/association_missing_index'
24
24
  require 'database_consistency/writers/simple/association_missing_null_constraint'
25
+ require 'database_consistency/writers/simple/association_foreign_type_missing_null_constraint'
25
26
  require 'database_consistency/writers/simple/has_one_missing_unique_index'
26
27
  require 'database_consistency/writers/simple/length_validator_lower_limit'
27
28
  require 'database_consistency/writers/simple/length_validator_greater_limit'
@@ -34,6 +35,8 @@ require 'database_consistency/writers/simple/null_constraint_missing'
34
35
  require 'database_consistency/writers/simple/possible_null'
35
36
  require 'database_consistency/writers/simple/small_primary_key'
36
37
  require 'database_consistency/writers/simple/inconsistent_enum_type'
38
+ require 'database_consistency/writers/simple/enum_values_inconsistent_with_ar_enum'
39
+ require 'database_consistency/writers/simple/enum_values_inconsistent_with_inclusion'
37
40
  require 'database_consistency/writers/simple_writer'
38
41
 
39
42
  require 'database_consistency/writers/autofix/helpers/migration'
@@ -66,6 +69,7 @@ require 'database_consistency/checkers/column_checkers/column_checker'
66
69
  require 'database_consistency/checkers/column_checkers/null_constraint_checker'
67
70
  require 'database_consistency/checkers/column_checkers/length_constraint_checker'
68
71
  require 'database_consistency/checkers/column_checkers/primary_key_type_checker'
72
+ require 'database_consistency/checkers/column_checkers/enum_value_checker'
69
73
 
70
74
  require 'database_consistency/checkers/validator_checkers/validator_checker'
71
75
  require 'database_consistency/checkers/validator_checkers/missing_unique_index_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.5.2
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgeniy Demin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-30 00:00:00.000000000 Z
11
+ date: 2022-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -153,6 +153,7 @@ files:
153
153
  - lib/database_consistency/checkers/association_checkers/missing_index_checker.rb
154
154
  - lib/database_consistency/checkers/base_checker.rb
155
155
  - lib/database_consistency/checkers/column_checkers/column_checker.rb
156
+ - lib/database_consistency/checkers/column_checkers/enum_value_checker.rb
156
157
  - lib/database_consistency/checkers/column_checkers/length_constraint_checker.rb
157
158
  - lib/database_consistency/checkers/column_checkers/null_constraint_checker.rb
158
159
  - lib/database_consistency/checkers/column_checkers/primary_key_type_checker.rb
@@ -201,10 +202,13 @@ files:
201
202
  - lib/database_consistency/writers/autofix/templates/redundant_index.tt
202
203
  - lib/database_consistency/writers/autofix_writer.rb
203
204
  - lib/database_consistency/writers/base_writer.rb
205
+ - lib/database_consistency/writers/simple/association_foreign_type_missing_null_constraint.rb
204
206
  - lib/database_consistency/writers/simple/association_missing_index.rb
205
207
  - lib/database_consistency/writers/simple/association_missing_null_constraint.rb
206
208
  - lib/database_consistency/writers/simple/base.rb
207
209
  - lib/database_consistency/writers/simple/default_message.rb
210
+ - lib/database_consistency/writers/simple/enum_values_inconsistent_with_ar_enum.rb
211
+ - lib/database_consistency/writers/simple/enum_values_inconsistent_with_inclusion.rb
208
212
  - lib/database_consistency/writers/simple/has_one_missing_unique_index.rb
209
213
  - lib/database_consistency/writers/simple/inconsistent_enum_type.rb
210
214
  - lib/database_consistency/writers/simple/inconsistent_types.rb