database_consistency 1.5.2 → 1.6.0

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