database_consistency 1.3.4 → 1.3.5

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: 1f1aad5cf1e4812b31d9bc7df0402f64dd32bb7ebdb9ab4ec7bf41bb195622dc
4
- data.tar.gz: 1fc95e58bd89e9faf0004d7faf5cc2635d5432785d2e1a57d9965e413abf4c41
3
+ metadata.gz: 8f935ca51faca06dad689d217e897a035253f4ce9884f1826befdf1bcbbee3b5
4
+ data.tar.gz: 38bde32ffee8b05b04bc8a1af261764f967b64f322352ef8d79729f1de485e13
5
5
  SHA512:
6
- metadata.gz: 6d5d98750608c54e20a5f6990b54c37df89717d7009d46b099b62695b90c2a29ab44f35dab299c1e3e0f83ff4d77f5709e6a42aa7130190fcbb1807b5a6628b9
7
- data.tar.gz: ff7a4d0fa92518ed33393c8e1614852ef9ec168ee5a4ac857a653881a268511ad877b982633553e7341ce8a740dbf7ae402002e991210784614e397dbe07ed36
6
+ metadata.gz: 2352fa358c91e6dbec9a22c9abac34fb27b45e662c1402e88cfe94864b7bfa4966628f246a6f6f5d6c6af323e1320a3fe03c74df644f86ec6d14e4bde7c81962
7
+ data.tar.gz: ad812a3ce4c8d4d0a7d24d9031815a26ae2c32bfca6d0721430f3c31f01d8c5910f4af091fb7d088e4860d65d887ba0a27531bdd0d8aefc50b589a944ef29290
@@ -4,17 +4,21 @@ module DatabaseConsistency
4
4
  module Checkers
5
5
  # This class checks if association's foreign key type covers associated model's primary key (same or bigger)
6
6
  class ForeignKeyTypeChecker < AssociationChecker
7
- class Report < DatabaseConsistency::Report # :nodoc:
8
- attr_reader :pk_name, :pk_type, :fk_name, :fk_type
9
-
10
- def initialize(fk_name: nil, fk_type: nil, pk_name: nil, pk_type: nil, **args)
11
- super(**args)
12
- @fk_name = fk_name
13
- @fk_type = fk_type
14
- @pk_name = pk_name
15
- @pk_type = pk_type
7
+ ALLOWED_TYPES =
8
+
9
+ class Report < DatabaseConsistency::Report # :nodoc:
10
+ attr_reader :pk_name, :pk_type, :fk_name, :fk_type, :table_to_change, :type_to_set
11
+
12
+ def initialize(fk_name: nil, fk_type: nil, pk_name: nil, pk_type: nil, table_to_change: nil, type_to_set: nil, **args) # rubocop:disable Metrics/ParameterLists, Layout/LineLength
13
+ super(**args)
14
+ @table_to_change = table_to_change
15
+ @type_to_set = type_to_set
16
+ @fk_name = fk_name
17
+ @fk_type = fk_type
18
+ @pk_name = pk_name
19
+ @pk_type = pk_type
20
+ end
16
21
  end
17
- end
18
22
 
19
23
  private
20
24
 
@@ -37,9 +41,11 @@ module DatabaseConsistency
37
41
  # | ------------- | ------ |
38
42
  # | covers | ok |
39
43
  # | doesn't cover | fail |
40
- def check # rubocop:disable Metrics/MethodLength
44
+ def check # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
41
45
  if converted_type(associated_column).cover?(converted_type(primary_column))
42
46
  report_template(:ok)
47
+ elsif !(converted_type(associated_column).numeric? && converted_type(primary_column).numeric?)
48
+ report_template(:ok)
43
49
  else
44
50
  report_template(:fail, error_slug: :inconsistent_types)
45
51
  end
@@ -52,11 +58,13 @@ module DatabaseConsistency
52
58
  )
53
59
  end
54
60
 
55
- def report_template(status, error_slug: nil)
61
+ def report_template(status, error_slug: nil) # rubocop:disable Metrics/MethodLength
56
62
  Report.new(
57
63
  status: status,
58
64
  error_slug: error_slug,
59
65
  error_message: nil,
66
+ table_to_change: table_to_change,
67
+ type_to_set: converted_type(primary_column).convert,
60
68
  fk_type: converted_type(associated_column).type,
61
69
  fk_name: associated_key,
62
70
  pk_type: converted_type(primary_column).type,
@@ -65,6 +73,14 @@ module DatabaseConsistency
65
73
  )
66
74
  end
67
75
 
76
+ def table_to_change
77
+ @table_to_change ||= if belongs_to_association?
78
+ association.active_record.table_name
79
+ else
80
+ association.klass.table_name
81
+ end
82
+ end
83
+
68
84
  # @return [String]
69
85
  def primary_key
70
86
  @primary_key ||= if belongs_to_association?
@@ -4,6 +4,16 @@ module DatabaseConsistency
4
4
  module Checkers
5
5
  # This class checks if association's foreign key has index in the database
6
6
  class MissingIndexChecker < AssociationChecker
7
+ class Report < DatabaseConsistency::Report # :nodoc:
8
+ attr_reader :table_name, :columns
9
+
10
+ def initialize(table_name:, columns:, **args)
11
+ super(**args)
12
+ @table_name = table_name
13
+ @columns = columns
14
+ end
15
+ end
16
+
7
17
  private
8
18
 
9
19
  # We skip check when:
@@ -48,6 +58,17 @@ module DatabaseConsistency
48
58
  end
49
59
  end
50
60
 
61
+ def report_template(status, error_slug: nil)
62
+ Report.new(
63
+ status: status,
64
+ error_slug: error_slug,
65
+ error_message: nil,
66
+ table_name: association.klass.table_name,
67
+ columns: association_keys,
68
+ **report_attributes
69
+ )
70
+ end
71
+
51
72
  def unique_has_one_association?
52
73
  association.scope.nil? && association.macro == :has_one && !association.options[:as].present?
53
74
  end
@@ -4,9 +4,24 @@ module DatabaseConsistency
4
4
  module Checkers
5
5
  # This class checks missing presence validator
6
6
  class PrimaryKeyTypeChecker < ColumnChecker
7
+ class Report < DatabaseConsistency::Report # :nodoc:
8
+ attr_reader :fk_name, :table_to_change, :type_to_set
9
+
10
+ def initialize(fk_name: nil, table_to_change: nil, type_to_set: nil, **args)
11
+ super(**args)
12
+ @table_to_change = table_to_change
13
+ @type_to_set = type_to_set
14
+ @fk_name = fk_name
15
+ end
16
+ end
17
+
7
18
  private
8
19
 
9
20
  VALID_TYPES = %w[bigserial bigint uuid].freeze
21
+ VALID_TYPES_MAP = {
22
+ 'serial' => 'bigserial',
23
+ 'integer' => 'bigint'
24
+ }.freeze
10
25
  SQLITE_ADAPTER_NAME = 'SQLite'
11
26
 
12
27
  # We skip check when:
@@ -21,14 +36,26 @@ module DatabaseConsistency
21
36
  # | --------------------- | ------ |
22
37
  # | yes | ok |
23
38
  # | no | fail |
24
- def check
39
+ def check # rubocop:disable Metrics/MethodLength
25
40
  if valid?
26
41
  report_template(:ok)
27
42
  else
28
- report_template(:fail, error_slug: :small_primary_key)
43
+ Report.new(
44
+ status: :fail,
45
+ error_slug: :small_primary_key,
46
+ error_message: nil,
47
+ table_to_change: model.table_name,
48
+ fk_name: column.name,
49
+ type_to_set: type_to_set,
50
+ **report_attributes
51
+ )
29
52
  end
30
53
  end
31
54
 
55
+ def type_to_set
56
+ VALID_TYPES_MAP[column.sql_type.to_s] || 'bigserial'
57
+ end
58
+
32
59
  # @return [Boolean]
33
60
  def valid?
34
61
  VALID_TYPES.any? do |type|
@@ -4,6 +4,16 @@ module DatabaseConsistency
4
4
  module Checkers
5
5
  # This class checks if uniqueness validator has unique index in the database
6
6
  class MissingUniqueIndexChecker < ValidatorChecker
7
+ class Report < DatabaseConsistency::Report # :nodoc:
8
+ attr_reader :table_name, :columns
9
+
10
+ def initialize(table_name:, columns:, **args)
11
+ super(**args)
12
+ @table_name = table_name
13
+ @columns = columns
14
+ end
15
+ end
16
+
7
17
  def column_or_attribute_name
8
18
  @column_or_attribute_name ||= Helper.uniqueness_validator_columns(attribute, validator, model).join('+')
9
19
  end
@@ -21,11 +31,18 @@ module DatabaseConsistency
21
31
  # | ------------ | ------ |
22
32
  # | persisted | ok |
23
33
  # | missing | fail |
24
- def check
34
+ def check # rubocop:disable Metrics/MethodLength
25
35
  if unique_index
26
36
  report_template(:ok)
27
37
  else
28
- report_template(:fail, error_slug: :missing_unique_index)
38
+ Report.new(
39
+ status: :fail,
40
+ error_slug: :missing_unique_index,
41
+ error_message: nil,
42
+ table_name: model.table_name,
43
+ columns: sorted_uniqueness_validator_columns,
44
+ **report_attributes
45
+ )
29
46
  end
30
47
  end
31
48
 
@@ -7,6 +7,8 @@ module DatabaseConsistency
7
7
  class Base
8
8
  attr_reader :type
9
9
 
10
+ NUMERIC_TYPES = %w[bigserial serial bigint integer smallint].freeze
11
+
10
12
  COVERED_TYPES = {
11
13
  'bigint' => %w[integer bigint],
12
14
  'integer' => %w[integer smallint]
@@ -22,6 +24,10 @@ module DatabaseConsistency
22
24
  type
23
25
  end
24
26
 
27
+ def numeric?
28
+ NUMERIC_TYPES.include?(convert)
29
+ end
30
+
25
31
  # @param [DatabaseConsistency::Databases::Types::Base]
26
32
  #
27
33
  # @return [Boolean]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseConsistency
4
- VERSION = '1.3.4'
4
+ VERSION = '1.3.5'
5
5
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Writers
5
+ module Autofix
6
+ class AssociationMissingIndex < MigrationBase # :nodoc:
7
+ def attributes
8
+ {
9
+ table_name: report.table_name,
10
+ columns: columns,
11
+ index_name: index_name
12
+ }
13
+ end
14
+
15
+ private
16
+
17
+ def migration_name
18
+ "add_#{report.table_name}_#{columns_key}_index"
19
+ end
20
+
21
+ def template_path
22
+ File.join(__dir__, 'templates', 'association_missing_index.tt')
23
+ end
24
+
25
+ def columns
26
+ "%w[#{report.columns.join(' ')}]"
27
+ end
28
+
29
+ def columns_key
30
+ report.columns.join('_').gsub('(', '_').gsub(')', '_')
31
+ end
32
+
33
+ def index_name
34
+ "index_#{report.table_name}_#{columns_key}".first(63)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Writers
5
+ module Autofix
6
+ class HasOneMissingUniqueIndex < AssociationMissingIndex # :nodoc:
7
+ def attributes
8
+ super.merge(unique: true)
9
+ end
10
+
11
+ private
12
+
13
+ def template_path
14
+ File.join(__dir__, 'templates', 'has_one_missing_unique_index.tt')
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Writers
5
+ module Autofix
6
+ class InconsistentTypes < MigrationBase # :nodoc:
7
+ def attributes
8
+ {
9
+ table_to_change: report.table_to_change,
10
+ type_to_set: report.type_to_set,
11
+ fk_name: report.fk_name
12
+ }
13
+ end
14
+
15
+ private
16
+
17
+ def migration_name
18
+ "change_#{report.table_to_change}_#{report.fk_name}_to_#{report.type_to_set}"
19
+ end
20
+
21
+ def template_path
22
+ File.join(__dir__, 'templates', 'inconsistent_types.tt')
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ class %<migration_name>s < ActiveRecord::Migration[%<migration_version>s]
2
+ def change
3
+ add_index :%<table_name>s, %<columns>s, name: :%<index_name>s
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class %<migration_name>s < ActiveRecord::Migration[%<migration_version>s]
2
+ def change
3
+ add_index :%<table_name>s, %<columns>s, name: :%<index_name>s, unique: true
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class %<migration_name>s < ActiveRecord::Migration[%<migration_version>s]
2
+ def change
3
+ change_column :%<table_to_change>s, :%<fk_name>s, :%<type_to_set>s
4
+ end
5
+ end
@@ -1,5 +1,5 @@
1
1
  class %<migration_name>s < ActiveRecord::Migration[%<migration_version>s]
2
2
  def change
3
- change_column_null(:%<table_name>s, :%<column_name>s, false)
3
+ change_column_null :%<table_name>s, :%<column_name>s, false
4
4
  end
5
5
  end
@@ -6,11 +6,16 @@ module DatabaseConsistency
6
6
  # The simplest formatter
7
7
  class AutofixWriter < BaseWriter
8
8
  SLUG_TO_GENERATOR = {
9
+ association_missing_index: Autofix::AssociationMissingIndex,
10
+ association_missing_null_constraint: Autofix::NullConstraintMissing,
11
+ has_one_missing_unique_index: Autofix::HasOneMissingUniqueIndex,
12
+ inconsistent_types: Autofix::InconsistentTypes,
9
13
  missing_foreign_key: Autofix::MissingForeignKey,
14
+ missing_unique_index: Autofix::HasOneMissingUniqueIndex,
10
15
  null_constraint_missing: Autofix::NullConstraintMissing,
11
- association_missing_null_constraint: Autofix::NullConstraintMissing,
12
16
  redundant_index: Autofix::RedundantIndex,
13
- redundant_unique_index: Autofix::RedundantIndex
17
+ redundant_unique_index: Autofix::RedundantIndex,
18
+ small_primary_key: Autofix::InconsistentTypes
14
19
  }.freeze
15
20
 
16
21
  def write
@@ -6,23 +6,23 @@ module DatabaseConsistency
6
6
  # The simplest formatter
7
7
  class SimpleWriter < BaseWriter
8
8
  SLUG_TO_WRITER = {
9
- missing_foreign_key: Simple::Base.with('should have foreign key in the database'),
10
- inconsistent_types: Simple::InconsistentTypes,
11
- has_one_missing_unique_index: Simple::Base.with('associated model should have proper unique index in the database'), # rubocop:disable Layout/LineLength
12
9
  association_missing_index: Simple::Base.with('associated model should have proper index in the database'),
13
- length_validator_missing: Simple::Base.with('column has limit in the database but do not have length validator'), # rubocop:disable Layout/LineLength
10
+ association_missing_null_constraint: Simple::Base.with('association foreign key column should be required in the database'), # rubocop:disable Layout/LineLength
11
+ has_one_missing_unique_index: Simple::Base.with('associated model should have proper unique index in the database'), # rubocop:disable Layout/LineLength
12
+ inconsistent_types: Simple::InconsistentTypes,
14
13
  length_validator_greater_limit: Simple::Base.with('column has greater limit in the database than in length validator'), # rubocop:disable Layout/LineLength
15
14
  length_validator_lower_limit: Simple::Base.with('column has lower limit in the database than in length validator'), # rubocop:disable Layout/LineLength
16
- null_constraint_misses_validator: Simple::Base.with('column is required in the database but do not have presence validator'), # rubocop:disable Layout/LineLength
17
- small_primary_key: Simple::Base.with('column has int/serial type but recommended to have bigint/bigserial/uuid'), # rubocop:disable Layout/LineLength
18
- missing_uniqueness_validation: Simple::Base.with('index is unique in the database but do not have uniqueness validator'), # rubocop:disable Layout/LineLength
15
+ length_validator_missing: Simple::Base.with('column has limit in the database but do not have length validator'), # rubocop:disable Layout/LineLength
16
+ missing_foreign_key: Simple::Base.with('should have foreign key in the database'),
19
17
  missing_unique_index: Simple::Base.with('model should have proper unique index in the database'),
20
- possible_null: Simple::Base.with('column is required but there is possible null value insert'),
21
- null_constraint_missing: Simple::Base.with('column should be required in the database'),
22
- association_missing_null_constraint: Simple::Base.with('association foreign key column should be required in the database'), # rubocop:disable Layout/LineLength
18
+ missing_uniqueness_validation: Simple::Base.with('index is unique in the database but do not have uniqueness validator'), # rubocop:disable Layout/LineLength
23
19
  null_constraint_association_misses_validator: Simple::NullConstraintAssociationMissesValidator,
20
+ null_constraint_misses_validator: Simple::Base.with('column is required in the database but do not have presence validator'), # rubocop:disable Layout/LineLength
21
+ null_constraint_missing: Simple::Base.with('column should be required in the database'),
22
+ possible_null: Simple::Base.with('column is required but there is possible null value insert'),
24
23
  redundant_index: Simple::RedundantIndex,
25
- redundant_unique_index: Simple::RedundantUniqueIndex
24
+ redundant_unique_index: Simple::RedundantUniqueIndex,
25
+ small_primary_key: Simple::Base.with('column has int/serial type but recommended to have bigint/bigserial/uuid')
26
26
  }.freeze
27
27
 
28
28
  def write
@@ -24,6 +24,9 @@ require 'database_consistency/writers/autofix/helpers/migration'
24
24
  require 'database_consistency/writers/autofix/base'
25
25
  require 'database_consistency/writers/autofix/migration_base'
26
26
  require 'database_consistency/writers/autofix/missing_foreign_key'
27
+ require 'database_consistency/writers/autofix/inconsistent_types'
28
+ require 'database_consistency/writers/autofix/association_missing_index'
29
+ require 'database_consistency/writers/autofix/has_one_missing_unique_index'
27
30
  require 'database_consistency/writers/autofix/redundant_index'
28
31
  require 'database_consistency/writers/autofix/null_constraint_missing'
29
32
  require 'database_consistency/writers/autofix_writer'
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.3.4
4
+ version: 1.3.5
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-12 00:00:00.000000000 Z
11
+ date: 2022-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -179,12 +179,18 @@ files:
179
179
  - lib/database_consistency/rescue_error.rb
180
180
  - lib/database_consistency/templates/rails_defaults.yml
181
181
  - lib/database_consistency/version.rb
182
+ - lib/database_consistency/writers/autofix/association_missing_index.rb
182
183
  - lib/database_consistency/writers/autofix/base.rb
184
+ - lib/database_consistency/writers/autofix/has_one_missing_unique_index.rb
183
185
  - lib/database_consistency/writers/autofix/helpers/migration.rb
186
+ - lib/database_consistency/writers/autofix/inconsistent_types.rb
184
187
  - lib/database_consistency/writers/autofix/migration_base.rb
185
188
  - lib/database_consistency/writers/autofix/missing_foreign_key.rb
186
189
  - lib/database_consistency/writers/autofix/null_constraint_missing.rb
187
190
  - lib/database_consistency/writers/autofix/redundant_index.rb
191
+ - lib/database_consistency/writers/autofix/templates/association_missing_index.tt
192
+ - lib/database_consistency/writers/autofix/templates/has_one_missing_unique_index.tt
193
+ - lib/database_consistency/writers/autofix/templates/inconsistent_types.tt
188
194
  - lib/database_consistency/writers/autofix/templates/missing_foreign_key.tt
189
195
  - lib/database_consistency/writers/autofix/templates/null_constraint_missing.tt
190
196
  - lib/database_consistency/writers/autofix/templates/redundant_index.tt