database_consistency 1.4.0 → 1.5.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: 9bc1994c7c24ab9a38d97c764fb0d9ce55e4fa71934df313f2cc693c466cecb7
4
- data.tar.gz: 04110bdba61254592c2b22323555948ad06a78b8a63ce78a40175005096acb54
3
+ metadata.gz: 24c245f8df70e4e98c102b52a56ead68364ba0613a67d1d60d499c36ddfae5b2
4
+ data.tar.gz: c1d8ce538657703f75a56f3fa462922ddbc5176d328e94665c83f71fd92bb3ed
5
5
  SHA512:
6
- metadata.gz: 581c8660605072f0cd877310813d71781b50de0af57aa5d85b216b57dd3ed040dc90944e165e51ff9b4728d180474157281ce1e028d9295b7137657367a4fa80
7
- data.tar.gz: 0c9d42d9902485c01d208ac38110aa4c7742b4ceca00c369c4fc4c014e84df038b708f17b084f9cee7da8eef28a1f31b52d4a29fa2f597f7cf454af1cefc828c
6
+ metadata.gz: 00614cf53933e47d9599698d6b412294446b50c6757ea042374247b2b400b4fec12743d00a02401040630e9912aee54fa65e472a4d683e5a2116e93e5f7f48a6
7
+ data.tar.gz: 5b87a53b2566b6892920ff883909f0a3ae871f137c8ed482b85fdc1a4ece3bd83eb1f68218a391a75dbdfa977a66cd2a34e9eb33810083a024039ccd636ff833
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Checkers
5
+ # This class checks if non polymorphic +belongs_to+ association has foreign key constraint
6
+ class ForeignKeyCascadeChecker < AssociationChecker
7
+ Report = ReportBuilder.define(
8
+ DatabaseConsistency::Report,
9
+ :cascade_option,
10
+ :primary_table,
11
+ :foreign_table,
12
+ :primary_key,
13
+ :foreign_key
14
+ )
15
+
16
+ OPTION_TO_CASCADE = {
17
+ delete: :cascade,
18
+ delete_all: :cascade,
19
+ nullify: :nullify,
20
+ restrict_with_exception: :restrict,
21
+ restrict_with_error: :restrict
22
+ }.freeze
23
+
24
+ DEPENDENT_OPTIONS = OPTION_TO_CASCADE.keys.freeze
25
+
26
+ private
27
+
28
+ def preconditions
29
+ !association.polymorphic? &&
30
+ !association.belongs_to? &&
31
+ foreign_key &&
32
+ DEPENDENT_OPTIONS.include?(dependent_option)
33
+ rescue StandardError
34
+ false
35
+ end
36
+
37
+ # Table of possible statuses
38
+ # | foreign key | status |
39
+ # | ----------- | ------ |
40
+ # | persisted | ok |
41
+ # | missing | fail |
42
+ def check
43
+ if correlated_cascade_constraint?
44
+ report_template(:ok)
45
+ else
46
+ report_template(:fail, error_slug: :missing_foreign_key_cascade)
47
+ end
48
+ end
49
+
50
+ def required_foreign_key_cascade
51
+ OPTION_TO_CASCADE[dependent_option]
52
+ end
53
+
54
+ def correlated_cascade_constraint?
55
+ required_foreign_key_cascade == foreign_key_on_delete_option
56
+ end
57
+
58
+ def dependent_option
59
+ association.options[:dependent]
60
+ end
61
+
62
+ def foreign_key_on_delete_option
63
+ foreign_key.options[:on_delete]
64
+ end
65
+
66
+ def foreign_key
67
+ @foreign_key ||=
68
+ association.klass
69
+ .connection
70
+ .foreign_keys(association.klass.table_name)
71
+ .find { |fk| fk.column == association.foreign_key.to_s }
72
+ end
73
+
74
+ def report_template(status, error_slug: nil)
75
+ Report.new(
76
+ status: status,
77
+ error_message: nil,
78
+ error_slug: error_slug,
79
+ primary_table: association.table_name.to_s,
80
+ primary_key: association.association_primary_key.to_s,
81
+ foreign_table: association.active_record.table_name.to_s,
82
+ foreign_key: association.foreign_key.to_s,
83
+ cascade_option: required_foreign_key_cascade,
84
+ **report_attributes
85
+ )
86
+ end
87
+ end
88
+ end
89
+ end
@@ -4,8 +4,6 @@ module DatabaseConsistency
4
4
  module Checkers
5
5
  # This class checks enum types consistency
6
6
  class EnumTypeChecker < EnumChecker
7
- MissingColumn = Class.new(StandardError)
8
-
9
7
  Report = DatabaseConsistency::ReportBuilder.define(
10
8
  DatabaseConsistency::Report,
11
9
  :column_type,
@@ -15,7 +13,7 @@ module DatabaseConsistency
15
13
  private
16
14
 
17
15
  def preconditions
18
- true
16
+ column.present?
19
17
  end
20
18
 
21
19
  def check
@@ -53,10 +51,11 @@ module DatabaseConsistency
53
51
  model.defined_enums[enum].values.map(&:class).uniq
54
52
  end
55
53
 
56
- def column_type
57
- column = model.columns.find { |c| c.name.to_s == enum.to_s }
58
- raise MissingColumn unless column
54
+ def column
55
+ @column ||= model.columns.find { |c| c.name.to_s == enum.to_s }
56
+ end
59
57
 
58
+ def column_type
60
59
  column.type.to_s
61
60
  end
62
61
 
@@ -20,26 +20,13 @@ module DatabaseConsistency
20
20
 
21
21
  # We skip the check when there are no presence validators
22
22
  def preconditions
23
- validators.any? && !association?
23
+ column && validators.any? && !association?
24
24
  end
25
25
 
26
26
  def association?
27
27
  model._reflect_on_association(attribute)&.macro == :has_one
28
28
  end
29
29
 
30
- # Table of possible statuses
31
- # | allow_nil/allow_blank/if/unless | database | status |
32
- # | ------------------------------- | -------- | ------ |
33
- # | at least one provided | required | fail |
34
- # | at least one provided | optional | ok |
35
- # | all missing | required | ok |
36
- # | all missing | optional | fail |
37
- def check
38
- report_message
39
- rescue Errors::MissingField => e
40
- report_template(:fail, error_message: e.message)
41
- end
42
-
43
30
  def report_template(status, error_message: nil, error_slug: nil)
44
31
  Report.new(
45
32
  status: status,
@@ -55,7 +42,7 @@ module DatabaseConsistency
55
42
  validators.all? { |validator| validator.options.slice(*WEAK_OPTIONS).any? }
56
43
  end
57
44
 
58
- def report_message # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
45
+ def check # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
59
46
  can_be_null = column.null
60
47
  has_weak_option = weak_option?
61
48
 
@@ -84,9 +71,7 @@ module DatabaseConsistency
84
71
  end
85
72
 
86
73
  def column
87
- @column ||= regular_column ||
88
- association_reference_column ||
89
- (raise Errors::MissingField, "column (#{attribute}) is missing in table (#{model.table_name}) but used for presence validation") # rubocop:disable Layout/LineLength
74
+ @column ||= (regular_column || association_reference_column)
90
75
  end
91
76
 
92
77
  def regular_column
@@ -7,7 +7,8 @@ module DatabaseConsistency
7
7
  CHECKERS = [
8
8
  Checkers::MissingIndexChecker,
9
9
  Checkers::ForeignKeyChecker,
10
- Checkers::ForeignKeyTypeChecker
10
+ Checkers::ForeignKeyTypeChecker,
11
+ Checkers::ForeignKeyCascadeChecker
11
12
  ].freeze
12
13
 
13
14
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseConsistency
4
- VERSION = '1.4.0'
4
+ VERSION = '1.5.0'
5
5
  end
@@ -17,7 +17,9 @@ module DatabaseConsistency
17
17
  private
18
18
 
19
19
  def migration
20
- File.read(template_path) % attributes.merge(migration_configuration(migration_name))
20
+ attributes.merge(migration_configuration(migration_name)).reduce(File.read(template_path)) do |str, (k, v)|
21
+ str.gsub("%<#{k}>s", v.to_s)
22
+ end
21
23
  end
22
24
  end
23
25
  end
@@ -47,7 +47,9 @@ module DatabaseConsistency
47
47
  end
48
48
 
49
49
  def message_text
50
- template % attributes
50
+ attributes.reduce(template) do |str, (k, v)|
51
+ str.gsub("%<#{k}>s", v.to_s)
52
+ end
51
53
  end
52
54
 
53
55
  def attributes
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Writers
5
+ module Simple
6
+ class MissingForeignKeyCascade < Base # :nodoc:
7
+ private
8
+
9
+ def template
10
+ 'should have foreign key with on_delete: :%<cascade_option>s in the database'
11
+ end
12
+
13
+ def attributes
14
+ {
15
+ cascade_option: report.cascade_option
16
+ }
17
+ end
18
+
19
+ def unique_attributes
20
+ {
21
+ foreign_table: report.foreign_table,
22
+ foreign_key: report.foreign_key,
23
+ primary_table: report.primary_table,
24
+ primary_key: report.primary_key,
25
+ cascade_option: report.cascade_option
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -23,7 +23,8 @@ module DatabaseConsistency
23
23
  redundant_index: Simple::RedundantIndex,
24
24
  redundant_unique_index: Simple::RedundantUniqueIndex,
25
25
  small_primary_key: Simple::SmallPrimaryKey,
26
- inconsistent_enum_type: Simple::InconsistentEnumType
26
+ inconsistent_enum_type: Simple::InconsistentEnumType,
27
+ missing_foreign_key_cascade: Simple::MissingForeignKeyCascade
27
28
  }.freeze
28
29
 
29
30
  def write
@@ -18,6 +18,7 @@ require 'database_consistency/writers/simple/default_message'
18
18
  require 'database_consistency/writers/simple/inconsistent_types'
19
19
  require 'database_consistency/writers/simple/null_constraint_association_misses_validator'
20
20
  require 'database_consistency/writers/simple/redundant_index'
21
+ require 'database_consistency/writers/simple/missing_foreign_key_cascade'
21
22
  require 'database_consistency/writers/simple/redundant_unique_index'
22
23
  require 'database_consistency/writers/simple/association_missing_index'
23
24
  require 'database_consistency/writers/simple/association_missing_null_constraint'
@@ -59,6 +60,7 @@ require 'database_consistency/checkers/association_checkers/association_checker'
59
60
  require 'database_consistency/checkers/association_checkers/missing_index_checker'
60
61
  require 'database_consistency/checkers/association_checkers/foreign_key_checker'
61
62
  require 'database_consistency/checkers/association_checkers/foreign_key_type_checker'
63
+ require 'database_consistency/checkers/association_checkers/foreign_key_cascade_checker'
62
64
 
63
65
  require 'database_consistency/checkers/column_checkers/column_checker'
64
66
  require 'database_consistency/checkers/column_checkers/null_constraint_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.4.0
4
+ version: 1.5.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-27 00:00:00.000000000 Z
11
+ date: 2022-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -147,6 +147,7 @@ files:
147
147
  - bin/database_consistency
148
148
  - lib/database_consistency.rb
149
149
  - lib/database_consistency/checkers/association_checkers/association_checker.rb
150
+ - lib/database_consistency/checkers/association_checkers/foreign_key_cascade_checker.rb
150
151
  - lib/database_consistency/checkers/association_checkers/foreign_key_checker.rb
151
152
  - lib/database_consistency/checkers/association_checkers/foreign_key_type_checker.rb
152
153
  - lib/database_consistency/checkers/association_checkers/missing_index_checker.rb
@@ -211,6 +212,7 @@ files:
211
212
  - lib/database_consistency/writers/simple/length_validator_lower_limit.rb
212
213
  - lib/database_consistency/writers/simple/length_validator_missing.rb
213
214
  - lib/database_consistency/writers/simple/missing_foreign_key.rb
215
+ - lib/database_consistency/writers/simple/missing_foreign_key_cascade.rb
214
216
  - lib/database_consistency/writers/simple/missing_unique_index.rb
215
217
  - lib/database_consistency/writers/simple/missing_uniqueness_validation.rb
216
218
  - lib/database_consistency/writers/simple/null_constraint_association_misses_validator.rb