database_consistency 0.8.3 → 0.8.8

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: 5015849cbfceb5d46a68ab0c3f9ba0e620fffbf013c16af563d3aa8fd16b140f
4
- data.tar.gz: dd379822c590457db98fd9f23fe99f19868063cf994e804eec11fe61b7311105
3
+ metadata.gz: 406fd80b86af26702bba071b8dabe3d8f238923bd7ef6d04776b014d2c5c5575
4
+ data.tar.gz: b30e6bee8c79233090fb8a94557217a6e2fc72e618b577b858083bf0cd3ba71e
5
5
  SHA512:
6
- metadata.gz: 3ec317d549f0852e6396db14f7263904448a3dda2c2515f83a174946a9d8138163f909b078a235a41c5d36daa01bbc38cd61c03f23921d3ad8704c7742e7fcec
7
- data.tar.gz: 8c41bdf53040283417a3d616e9cf9b13a34138ba17edf65b84df1506e630e758d88995a618ea2b111a8d45884c49bf8b4e5f9b2c38e16fbfdd379328260aa0f1
6
+ metadata.gz: 72147fa9f0eccd7ac75216fe673757541048b143ea31fbab435a83e7cb10f62d3e45ae19a5e0c8e609515d3033fa7d868deba863fd8d7ba152f50d254fb3f534
7
+ data.tar.gz: 6c172f7b74de5face1e235103a2821d41be08f282a840b756caf73f24285e8cf6f7e1f5ecf5e40f26160a8036c947c335edc5338ef3ca274a989d9353220f04d
@@ -6,14 +6,20 @@ require 'database_consistency/version'
6
6
  require 'database_consistency/helper'
7
7
  require 'database_consistency/configuration'
8
8
  require 'database_consistency/rescue_error'
9
+ require 'database_consistency/errors'
9
10
 
10
11
  require 'database_consistency/writers/base_writer'
11
12
  require 'database_consistency/writers/simple_writer'
12
13
 
14
+ require 'database_consistency/databases/factory'
15
+ require 'database_consistency/databases/types/base'
16
+ require 'database_consistency/databases/types/sqlite'
17
+
13
18
  require 'database_consistency/checkers/base_checker'
14
19
 
15
20
  require 'database_consistency/checkers/association_checkers/association_checker'
16
21
  require 'database_consistency/checkers/association_checkers/missing_index_checker'
22
+ require 'database_consistency/checkers/association_checkers/foreign_key_type_checker'
17
23
 
18
24
  require 'database_consistency/checkers/column_checkers/column_checker'
19
25
  require 'database_consistency/checkers/column_checkers/null_constraint_checker'
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Checkers
5
+ # This class checks if association's foreign key type is the same as associated model's primary key
6
+ class ForeignKeyTypeChecker < AssociationChecker
7
+ INCONSISTENT_TYPE = 'associated model key (%a_f) with type (%a_t) mismatches key (%b_f) with type (%b_t)'
8
+
9
+ TYPES = {
10
+ 'serial' => 'integer',
11
+ 'integer' => 'integer',
12
+ 'int' => 'integer',
13
+ 'bigserial' => 'bigint',
14
+ 'bigint' => 'bigint',
15
+ 'bigint unsigned' => 'bigint unsigned'
16
+ }.freeze
17
+
18
+ private
19
+
20
+ # We skip check when:
21
+ # - association is polymorphic association
22
+ # - association is has_and_belongs_to_many
23
+ # - association has `through` option
24
+ # - associated class doesn't exist
25
+ def preconditions
26
+ !association.polymorphic? &&
27
+ association.through_reflection.nil? &&
28
+ association.klass.present? &&
29
+ association.macro != :has_and_belongs_to_many
30
+ rescue NameError
31
+ false
32
+ end
33
+
34
+ # Table of possible statuses
35
+ # | type | status |
36
+ # | ------------ | ------ |
37
+ # | consistent | ok |
38
+ # | inconsistent | fail |
39
+ def check
40
+ if converted_type(base_column) == converted_type(associated_column)
41
+ report_template(:ok)
42
+ else
43
+ report_template(:fail, render_text)
44
+ end
45
+ end
46
+
47
+ # @return [String]
48
+ def render_text
49
+ INCONSISTENT_TYPE
50
+ .gsub('%a_t', type(associated_column))
51
+ .gsub('%a_f', associated_key)
52
+ .gsub('%b_t', type(base_column))
53
+ .gsub('%b_f', base_key)
54
+ end
55
+
56
+ # @return [String]
57
+ def base_key
58
+ @base_key ||= (
59
+ if belongs_to_association?
60
+ association.foreign_key
61
+ else
62
+ association.active_record_primary_key
63
+ end
64
+ ).to_s
65
+ end
66
+
67
+ # @return [String]
68
+ def associated_key
69
+ @associated_key ||= (
70
+ if belongs_to_association?
71
+ association.active_record_primary_key
72
+ else
73
+ association.foreign_key
74
+ end
75
+ ).to_s
76
+ end
77
+
78
+ # @return [ActiveRecord::ConnectionAdapters::Column]
79
+ def base_column
80
+ @base_column ||= column(association.active_record, base_key)
81
+ end
82
+
83
+ # @return [ActiveRecord::ConnectionAdapters::Column]
84
+ def associated_column
85
+ @associated_column ||= column(association.klass, associated_key)
86
+ end
87
+
88
+ # @return [DatabaseConsistency::Databases::Factory]
89
+ def database_factory
90
+ @database_factory ||= Databases::Factory.new(association.active_record.connection.adapter_name)
91
+ end
92
+
93
+ # @param [ActiveRecord::Base] model
94
+ # @param [String] column_name
95
+ #
96
+ # @return [ActiveRecord::ConnectionAdapters::Column]
97
+ def column(model, column_name)
98
+ model.connection.columns(model.table_name).find { |column| column.name == column_name } ||
99
+ (raise Errors::MissingField, missing_field_error(model.table_name, column_name))
100
+ end
101
+
102
+ # @return [String]
103
+ 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
+ end
107
+
108
+ # @param [ActiveRecord::ConnectionAdapters::Column] column
109
+ #
110
+ # @return [String]
111
+ def type(column)
112
+ column.sql_type
113
+ end
114
+
115
+ # @param [ActiveRecord::ConnectionAdapters::Column]
116
+ #
117
+ # @return [String]
118
+ def converted_type(column)
119
+ database_factory.type(type(column)).convert
120
+ end
121
+
122
+ # @return [Boolean]
123
+ def belongs_to_association?
124
+ association.macro == :belongs_to
125
+ end
126
+ end
127
+ end
128
+ end
@@ -16,16 +16,20 @@ module DatabaseConsistency
16
16
  @checker_name ||= name.split('::').last
17
17
  end
18
18
 
19
- # @return [Hash, nil]
20
- def report
19
+ # @param [Boolean] catch_errors
20
+ #
21
+ # @return [Hash, File, nil]
22
+ def report(catch_errors = true)
21
23
  return unless preconditions
22
24
 
23
25
  @report ||= check
24
26
  rescue StandardError => e
27
+ raise e unless catch_errors
28
+
25
29
  RescueError.call(e)
26
30
  end
27
31
 
28
- # @return [Hash, nil]
32
+ # @return [Hash, File, nil]
29
33
  def report_if_enabled?(configuration)
30
34
  report if enabled?(configuration)
31
35
  end
@@ -62,7 +66,7 @@ module DatabaseConsistency
62
66
  raise NotImplementedError
63
67
  end
64
68
 
65
- # @return [Hash]
69
+ # @return [OpenStruct]
66
70
  def report_template(status, message = nil)
67
71
  OpenStruct.new(
68
72
  checker_name: checker_name,
@@ -21,8 +21,14 @@ module DatabaseConsistency
21
21
  # We skip check when:
22
22
  # - column hasn't limit constraint
23
23
  # - column insn't string nor text
24
+ # - column is array (PostgreSQL only)
24
25
  def preconditions
25
- !column.limit.nil? && %i[string text].include?(column.type)
26
+ !column.limit.nil? && %i[string text].include?(column.type) && !postgresql_array?
27
+ end
28
+
29
+ # @return [Boolean] true if it is an array (PostgreSQL only)
30
+ def postgresql_array?
31
+ column.respond_to?(:array) && column.array
26
32
  end
27
33
 
28
34
  # Table of possible statuses
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Databases
5
+ # Factory for database adapters
6
+ class Factory
7
+ attr_reader :adapter
8
+
9
+ # @param [String] adapter
10
+ def initialize(adapter)
11
+ @adapter = adapter
12
+ end
13
+
14
+ # @return [DatabaseConsistency::Databases::Types::Base]
15
+ def type(type)
16
+ sqlite? ? Types::Sqlite.new(type) : Types::Base.new(type)
17
+ end
18
+
19
+ private
20
+
21
+ # @return [Boolean]
22
+ def sqlite?
23
+ adapter == 'SQLite'
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Databases
5
+ module Types
6
+ # Base wrapper for database types
7
+ class Base
8
+ attr_reader :type
9
+
10
+ # @param [String] type
11
+ def initialize(type)
12
+ @type = type
13
+ end
14
+
15
+ # @return [String]
16
+ def convert
17
+ type
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Databases
5
+ module Types
6
+ # Wraps types for SQLite database
7
+ class Sqlite < Base
8
+ TYPES = {
9
+ 'bigserial' => 'bigint',
10
+ 'bigint' => 'bigint',
11
+ 'serial' => 'integer',
12
+ 'integer' => 'integer'
13
+ }.freeze
14
+
15
+ # @return [String]
16
+ def convert
17
+ TYPES[type] || type
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Errors
5
+ # The base error class
6
+ class Base < StandardError; end
7
+
8
+ # The error class for missing field
9
+ class MissingField < Base; end
10
+ end
11
+ end
@@ -8,7 +8,7 @@ module DatabaseConsistency
8
8
  # Returns list of models to check
9
9
  def models
10
10
  ActiveRecord::Base.descendants.delete_if(&:abstract_class?).delete_if do |klass|
11
- !klass.connection.table_exists?(klass.table_name)
11
+ !klass.connection.table_exists?(klass.table_name) || klass.name.include?('HABTM_')
12
12
  end
13
13
  end
14
14
 
@@ -5,7 +5,8 @@ module DatabaseConsistency
5
5
  # The class to process associations
6
6
  class AssociationsProcessor < BaseProcessor
7
7
  CHECKERS = [
8
- Checkers::MissingIndexChecker
8
+ Checkers::MissingIndexChecker,
9
+ Checkers::ForeignKeyTypeChecker
9
10
  ].freeze
10
11
 
11
12
  private
@@ -33,7 +33,7 @@ module DatabaseConsistency
33
33
  private
34
34
 
35
35
  def filename
36
- "database_consistency_#{Time.now.strftime('%Y_%m_%d_%H_%M_%S')}"
36
+ @filename ||= "database_consistency_#{Time.now.strftime('%Y_%m_%d_%H_%M_%S')}"
37
37
  end
38
38
  end
39
39
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseConsistency
4
- VERSION = '0.8.3'
4
+ VERSION = '0.8.8'
5
5
  end
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: 0.8.3
4
+ version: 0.8.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgeniy Demin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-24 00:00:00.000000000 Z
11
+ date: 2020-09-24 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_type_checker.rb
136
137
  - lib/database_consistency/checkers/association_checkers/missing_index_checker.rb
137
138
  - lib/database_consistency/checkers/base_checker.rb
138
139
  - lib/database_consistency/checkers/column_checkers/column_checker.rb
@@ -145,6 +146,10 @@ files:
145
146
  - lib/database_consistency/checkers/validators_fraction_checkers/column_presence_checker.rb
146
147
  - lib/database_consistency/checkers/validators_fraction_checkers/validators_fraction_checker.rb
147
148
  - lib/database_consistency/configuration.rb
149
+ - lib/database_consistency/databases/factory.rb
150
+ - lib/database_consistency/databases/types/base.rb
151
+ - lib/database_consistency/databases/types/sqlite.rb
152
+ - lib/database_consistency/errors.rb
148
153
  - lib/database_consistency/helper.rb
149
154
  - lib/database_consistency/processors/associations_processor.rb
150
155
  - lib/database_consistency/processors/base_processor.rb
@@ -175,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
175
180
  - !ruby/object:Gem::Version
176
181
  version: '0'
177
182
  requirements: []
178
- rubygems_version: 3.1.2
183
+ rubygems_version: 3.0.8
179
184
  signing_key:
180
185
  specification_version: 4
181
186
  summary: Provide an easy way to check the consistency of the database constraints