database_consistency 2.1.3 → 3.0.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: 67c90295e24b6b49a4e010a612729f86951d2dda7daba5ac06b091e6b435e040
4
- data.tar.gz: f1958934302e46a7b21d600a9418bbf58bef2921a5b4d5c2dd5fcccc5a4acda8
3
+ metadata.gz: 3d92e907cbb8ca8c49b6b4015614f9bc55074d37cb96130b97e62ef5266211d1
4
+ data.tar.gz: c97b5ff7fb08271812b489ea695f20b0d45c56f5109bd1ec246e1704917b33cc
5
5
  SHA512:
6
- metadata.gz: 3bb2a7b1dc68923711cad75c25c2e27a0208ac625a911f61b6e9a6459b5c71640800329f920f14373c4202f100f36b2bf05f2a683772bace47ebc5dd3b3b4ea4
7
- data.tar.gz: e6a7b06b4bd9d7b3ff002e24c8a320b952c208e864f07ec4f0c255de23b032f14610bf893cc737d3ca2713f1357ee0e0307d91d4a611c48b693e330b1145891e
6
+ metadata.gz: 004c32a1588bc7cb59299352e09a1866f20229735d56b1290d337ce2322ecd4ff20918e817e26118461356a47b6a81580ede89f695be2ec9fdf2739f44a38064
7
+ data.tar.gz: 52d25e2ee4da71b6130d442a2443c7f3e02ad84674f7d19d67270356ebe28889db57581907d5d65cc9579013fb9d3b073461e210f0320a90db14cad8bab5e024
@@ -28,6 +28,7 @@ module DatabaseConsistency
28
28
  def preconditions
29
29
  !association.polymorphic? &&
30
30
  !association.belongs_to? &&
31
+ association.association_primary_key.present? &&
31
32
  foreign_key &&
32
33
  DEPENDENT_OPTIONS.include?(dependent_option)
33
34
  rescue StandardError
@@ -68,18 +69,18 @@ module DatabaseConsistency
68
69
  association.klass
69
70
  .connection
70
71
  .foreign_keys(association.klass.table_name)
71
- .find { |fk| fk.column == association.foreign_key.to_s }
72
+ .find { |fk| (Helper.extract_columns(association.foreign_key) - Array.wrap(fk.column)).empty? }
72
73
  end
73
74
 
74
- def report_template(status, error_slug: nil)
75
+ def report_template(status, error_slug: nil) # rubocop:disable Metrics/AbcSize
75
76
  Report.new(
76
77
  status: status,
77
78
  error_message: nil,
78
79
  error_slug: error_slug,
79
80
  primary_table: association.table_name.to_s,
80
- primary_key: association.association_primary_key.to_s,
81
+ primary_key: Helper.extract_columns(association.association_primary_key).join('+'),
81
82
  foreign_table: association.active_record.table_name.to_s,
82
- foreign_key: association.foreign_key.to_s,
83
+ foreign_key: Helper.extract_columns(association.foreign_key).join('+'),
83
84
  cascade_option: required_foreign_key_cascade,
84
85
  **report_attributes
85
86
  )
@@ -15,7 +15,7 @@ module DatabaseConsistency
15
15
  private
16
16
 
17
17
  def preconditions
18
- association.belongs_to? && foreign_key
18
+ association.belongs_to? && !association.polymorphic? && foreign_key
19
19
  end
20
20
 
21
21
  def check
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'prism'
5
+ rescue LoadError
6
+ # Prism is not available; this checker will be disabled on Ruby < 3.3
7
+ end
8
+
9
+ module DatabaseConsistency
10
+ module Checkers
11
+ # This class checks for columns used in find_by queries that are missing a database index.
12
+ # It uses the Prism parser (Ruby stdlib since 3.3) to traverse the AST of all project
13
+ # source files (found by iterating loaded constants and excluding gem paths) and detect
14
+ # calls such as find_by_<column>, find_by(column: ...) and find_by("column" => ...).
15
+ # The checker is automatically skipped on Ruby versions where Prism is not available.
16
+ class MissingIndexFindByChecker < ColumnChecker
17
+ Report = ReportBuilder.define(
18
+ DatabaseConsistency::Report,
19
+ :source_location,
20
+ :total_findings_count
21
+ )
22
+
23
+ private
24
+
25
+ # We skip check when:
26
+ # - Prism is not available (Ruby < 3.3)
27
+ # - column is the primary key (always indexed)
28
+ # - column name does not appear in any find_by call across project source files
29
+ def preconditions
30
+ defined?(Prism) && !primary_key_column? && find_by_used?
31
+ end
32
+
33
+ # Table of possible statuses
34
+ # | index | status |
35
+ # | -------- | ------ |
36
+ # | present | ok |
37
+ # | missing | fail |
38
+ def check
39
+ if indexed?
40
+ report_template(:ok)
41
+ else
42
+ report_template(:fail, error_slug: :missing_index_find_by)
43
+ end
44
+ end
45
+
46
+ def report_template(status, error_slug: nil)
47
+ Report.new(
48
+ status: status,
49
+ error_slug: error_slug,
50
+ error_message: nil,
51
+ source_location: (status == :fail ? @find_by_location : nil),
52
+ total_findings_count: (status == :fail ? @find_by_count : nil),
53
+ **report_attributes
54
+ )
55
+ end
56
+
57
+ def find_by_used?
58
+ entry = PrismHelper.find_by_calls_index.dig(model.name.to_s, column.name.to_s)
59
+ return false unless entry
60
+
61
+ @find_by_location = entry[:first_location]
62
+ @find_by_count = entry[:total_findings_count]
63
+ true
64
+ end
65
+
66
+ def indexed?
67
+ model.connection.indexes(model.table_name).any? do |index|
68
+ Helper.extract_index_columns(index.columns).first == column.name.to_s
69
+ end
70
+ end
71
+
72
+ def primary_key_column?
73
+ column.name.to_s == model.primary_key.to_s
74
+ end
75
+
76
+ if defined?(Prism)
77
+ # Prism AST visitor that collects ALL find_by calls from a source file into a results hash.
78
+ # Key: [model_name, column_name] -- model_name is derived from the explicit receiver or the
79
+ # lexical class/module scope for bare calls. Bare calls outside any class are ignored.
80
+ # Value: "file:line" location of the first matching call.
81
+ #
82
+ # Handles:
83
+ # - find_by_<col>(<value>) / Model.find_by_<col>! (dynamic finder)
84
+ # - find_by(col: <value>) / Model.find_by col: (symbol-key hash)
85
+ # - find_by("col" => <value>) (string-key hash)
86
+ #
87
+ # Defined only when Prism is available (Ruby 3.3+).
88
+ class FindByCollector < Prism::Visitor
89
+ # Matches the full column name from a dynamic finder method name.
90
+ # e.g. find_by_email -> "email", find_by_first_name -> "first_name"
91
+ # Multi-column patterns like find_by_name_and_email extract "name_and_email"
92
+ # which won't match any single-column name, so there are no false positives.
93
+ DYNAMIC_FINDER_RE = /\Afind_by_(.+?)!?\z/.freeze
94
+
95
+ attr_reader :results
96
+
97
+ def initialize(file)
98
+ super()
99
+ @file = file
100
+ @results = {}
101
+ @scope_stack = []
102
+ end
103
+
104
+ def visit_class_node(node)
105
+ @scope_stack.push(constant_path_name(node.constant_path))
106
+ super
107
+ ensure
108
+ @scope_stack.pop
109
+ end
110
+
111
+ def visit_module_node(node)
112
+ @scope_stack.push(constant_path_name(node.constant_path))
113
+ super
114
+ ensure
115
+ @scope_stack.pop
116
+ end
117
+
118
+ def visit_call_node(node)
119
+ name = node.name.to_s
120
+ if (match = DYNAMIC_FINDER_RE.match(name))
121
+ model_key = receiver_to_model_key(node.receiver)
122
+ store(model_key, match[1], node) unless model_key == :skip
123
+ elsif name == 'find_by' && node.arguments
124
+ col = single_hash_column(node.arguments)
125
+ model_key = receiver_to_model_key(node.receiver)
126
+ store(model_key, col, node) if col && model_key != :skip
127
+ end
128
+ super
129
+ end
130
+
131
+ private
132
+
133
+ def current_scope
134
+ @scope_stack.empty? ? nil : @scope_stack.join('::')
135
+ end
136
+
137
+ def store(model_key, col, node)
138
+ key = [model_key, col]
139
+ @results[key] ||= []
140
+ @results[key] << "#{@file}:#{node.location.start_line}"
141
+ end
142
+
143
+ def receiver_to_model_key(receiver)
144
+ case receiver
145
+ when nil then current_scope || :skip
146
+ when Prism::ConstantReadNode, Prism::ConstantPathNode
147
+ constant_path_name(receiver)
148
+ when Prism::CallNode
149
+ scoped_receiver_model(receiver)
150
+ else
151
+ :skip
152
+ end
153
+ end
154
+
155
+ def scoped_receiver_model(call_node)
156
+ return :skip unless %w[unscoped includes].include?(call_node.name.to_s)
157
+
158
+ rec = call_node.receiver
159
+ return :skip unless rec.is_a?(Prism::ConstantReadNode) || rec.is_a?(Prism::ConstantPathNode)
160
+
161
+ constant_path_name(rec)
162
+ end
163
+
164
+ def constant_path_name(node)
165
+ case node
166
+ when Prism::ConstantReadNode then node.name.to_s
167
+ when Prism::ConstantPathNode then "#{constant_path_name(node.parent)}::#{node.name}"
168
+ end
169
+ end
170
+
171
+ def single_hash_column(arguments_node)
172
+ arguments_node.arguments.each do |arg|
173
+ next unless arg.is_a?(Prism::KeywordHashNode) && arg.elements.size == 1
174
+
175
+ assoc = arg.elements.first
176
+ next unless assoc.is_a?(Prism::AssocNode)
177
+
178
+ key = assoc.key
179
+ return key.unescaped if key.is_a?(Prism::SymbolNode) || key.is_a?(Prism::StringNode)
180
+ end
181
+ nil
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -29,10 +29,13 @@ module DatabaseConsistency
29
29
  def valid?
30
30
  uniqueness_validators = model.validators.select { |validator| validator.kind == :uniqueness }
31
31
 
32
- uniqueness_validators.any? do |validator|
33
- validator.attributes.any? do |attribute|
34
- sorted_index_columns == Helper.sorted_uniqueness_validator_columns(attribute, validator, model)
35
- end
32
+ uniqueness_validators.any? { |validator| validator_matches?(validator) }
33
+ end
34
+
35
+ def validator_matches?(validator)
36
+ validator.attributes.any? do |attribute|
37
+ sorted_index_columns == Helper.sorted_uniqueness_validator_columns(attribute, validator, model) &&
38
+ Helper.conditions_match_index?(model, validator.options[:conditions], index.where)
36
39
  end
37
40
  end
38
41
 
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Checkers
5
+ # This class checks that a model pointing to a view has a primary_key set and that column exists
6
+ class ViewPrimaryKeyChecker < ModelChecker
7
+ private
8
+
9
+ def preconditions
10
+ ActiveRecord::VERSION::MAJOR >= 5 &&
11
+ !model.abstract_class? &&
12
+ model.connection.view_exists?(model.table_name)
13
+ end
14
+
15
+ # Table of possible statuses
16
+ # | primary_key set | column exists | status |
17
+ # | --------------- | ------------- | ------ |
18
+ # | no | - | fail |
19
+ # | yes | no | fail |
20
+ # | yes | yes | ok |
21
+ def check
22
+ if model.primary_key.blank?
23
+ report_template(:fail, error_slug: :view_missing_primary_key)
24
+ elsif !primary_key_column_exists?
25
+ report_template(:fail, error_slug: :view_primary_key_column_missing)
26
+ else
27
+ report_template(:ok)
28
+ end
29
+ end
30
+
31
+ def primary_key_column_exists?
32
+ Array(model.primary_key).all? { |key| model.column_names.include?(key.to_s) }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -47,9 +47,13 @@ module DatabaseConsistency
47
47
  end
48
48
 
49
49
  def unique_index
50
- @unique_index ||= model.connection.indexes(model.table_name).find do |index|
51
- index.unique && Helper.extract_index_columns(index.columns).sort == sorted_uniqueness_validator_columns
52
- end
50
+ @unique_index ||= model.connection.indexes(model.table_name).find { |index| index_matches?(index) }
51
+ end
52
+
53
+ def index_matches?(index)
54
+ index.unique &&
55
+ Helper.extract_index_columns(index.columns).sort == sorted_uniqueness_validator_columns &&
56
+ Helper.conditions_match_index?(model, validator.options[:conditions], index.where)
53
57
  end
54
58
 
55
59
  def primary_key_covers_validation?
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ # The module contains file system helper methods for locating project source files.
5
+ module FilesHelper
6
+ module_function
7
+
8
+ # Returns all unique project source file paths (non-gem Ruby files from loaded constants).
9
+ # Memoized so the file system walk happens once per database_consistency run.
10
+ def project_source_files
11
+ @project_source_files ||=
12
+ if Module.respond_to?(:const_source_location)
13
+ collect_source_files
14
+ else
15
+ []
16
+ end
17
+ end
18
+
19
+ def collect_source_files
20
+ files = []
21
+ ObjectSpace.each_object(Module) { |mod| files << source_file_path(mod) }
22
+ files.compact.uniq
23
+ end
24
+
25
+ def source_file_path(mod)
26
+ return unless (name = mod.name)
27
+
28
+ file, = Module.const_source_location(name)
29
+ return unless file && File.exist?(file)
30
+ return if excluded_source_file?(file)
31
+
32
+ file
33
+ rescue NameError, ArgumentError
34
+ nil
35
+ end
36
+
37
+ def excluded_source_file?(file)
38
+ return true if defined?(Bundler) && file.include?(Bundler.bundle_path.to_s)
39
+ return true if defined?(Gem) && file.include?(Gem::RUBYGEMS_DIR)
40
+
41
+ excluded_by_ruby_stdlib?(file)
42
+ end
43
+
44
+ def excluded_by_ruby_stdlib?(file)
45
+ return false unless defined?(RbConfig)
46
+
47
+ file.include?(RbConfig::CONFIG['rubylibdir']) ||
48
+ file.include?(RbConfig::CONFIG['bindir']) ||
49
+ file.include?(RbConfig::CONFIG['sbindir'])
50
+ end
51
+ end
52
+ end
@@ -144,6 +144,38 @@ module DatabaseConsistency
144
144
  model._reflect_on_association(attribute)&.foreign_key || attribute
145
145
  end
146
146
 
147
+ # Returns the normalized WHERE SQL produced by a conditions proc, or nil if
148
+ # it cannot be determined (complex proc, unsupported AR version, etc.).
149
+ def conditions_where_sql(model, conditions)
150
+ sql = model.unscoped.instance_exec(&conditions).to_sql
151
+ where_part = sql.split(/\bWHERE\b/i, 2).last
152
+ return nil unless where_part
153
+
154
+ normalize_sql(where_part.gsub("#{model.quoted_table_name}.", '').gsub('"', ''))
155
+ rescue StandardError
156
+ nil
157
+ end
158
+
159
+ # Returns true when validator conditions and index WHERE clause are a valid
160
+ # pairing: both absent means a match; exactly one present means no match;
161
+ # when both present the normalized SQL is compared.
162
+ def conditions_match_index?(model, conditions, index_where)
163
+ return true if conditions.nil? && index_where.blank?
164
+ return false if conditions.nil? || index_where.blank?
165
+
166
+ conditions_sql = conditions_where_sql(model, conditions)
167
+ # Strip one level of outer parentheses that some databases (e.g. PostgreSQL)
168
+ # add when storing/returning the index WHERE clause.
169
+ normalized_where = normalize_sql(index_where.sub(/\A\s*\((.+)\)\s*\z/m, '\1'))
170
+ conditions_sql&.casecmp?(normalized_where)
171
+ end
172
+
173
+ def normalize_sql(sql)
174
+ sql.gsub(/\bTRUE\b/i, '1').gsub(/\bFALSE\b/i, '0')
175
+ .gsub(/ = 't'/, ' = 1').gsub(/ = 'f'/, ' = 0')
176
+ .strip
177
+ end
178
+
147
179
  # @return [String]
148
180
  def wrapped_attribute_name(attribute, validator, model)
149
181
  attribute = foreign_key_or_attribute(model, attribute)
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ # The module contains Prism AST helper methods for scanning project source files.
5
+ module PrismHelper
6
+ module_function
7
+
8
+ # Returns a memoized index: {model_name => {column_name => "file:line"}}.
9
+ # Built once per run by scanning all project source files with Prism (Ruby 3.3+).
10
+ # Bare find_by calls are resolved to their lexical class/module scope.
11
+ def find_by_calls_index
12
+ return {} unless defined?(Prism)
13
+
14
+ @find_by_calls_index ||= build_find_by_calls_index
15
+ end
16
+
17
+ def build_find_by_calls_index
18
+ FilesHelper.project_source_files.each_with_object({}) do |file, index|
19
+ collector = Checkers::MissingIndexFindByChecker::FindByCollector.new(file)
20
+ collector.visit(Prism.parse_file(file).value)
21
+ merge_collector_results(collector.results, index)
22
+ rescue StandardError
23
+ nil
24
+ end
25
+ end
26
+
27
+ def merge_collector_results(results, index)
28
+ results.each do |(model_key, col), locations|
29
+ index[model_key] ||= {}
30
+ if (entry = index[model_key][col])
31
+ entry[:total_findings_count] += locations.size
32
+ else
33
+ index[model_key][col] = { first_location: locations.first, total_findings_count: locations.size }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseConsistency
4
- VERSION = '2.1.3'
4
+ VERSION = '3.0.0'
5
5
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Writers
5
+ module Simple
6
+ class MissingIndexFindBy < Base # :nodoc:
7
+ private
8
+
9
+ def template
10
+ 'column is used in find_by but is missing an index%<source_location>s'
11
+ end
12
+
13
+ def attributes
14
+ if report.source_location
15
+ count = report.total_findings_count || 1
16
+ count_str = count > 1 ? ", and #{count - 1} more occurrences" : ''
17
+ { source_location: " (found at #{report.source_location}#{count_str})" }
18
+ else
19
+ { source_location: '' }
20
+ end
21
+ end
22
+
23
+ def unique_attributes
24
+ {
25
+ table_or_model_name: report.table_or_model_name,
26
+ column_or_attribute_name: report.column_or_attribute_name
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Writers
5
+ module Simple
6
+ class ViewMissingPrimaryKey < Base # :nodoc:
7
+ private
8
+
9
+ def template
10
+ 'model pointing to a view should have primary_key set'
11
+ end
12
+
13
+ def unique_attributes
14
+ {
15
+ table_or_model_name: report.table_or_model_name
16
+ }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Writers
5
+ module Simple
6
+ class ViewPrimaryKeyColumnMissing < Base # :nodoc:
7
+ private
8
+
9
+ def template
10
+ 'model pointing to a view has a non-existent primary_key column set'
11
+ end
12
+
13
+ def unique_attributes
14
+ {
15
+ table_or_model_name: report.table_or_model_name
16
+ }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -4,6 +4,8 @@ require 'active_record'
4
4
 
5
5
  require 'database_consistency/version'
6
6
  require 'database_consistency/helper'
7
+ require 'database_consistency/files_helper'
8
+ require 'database_consistency/prism_helper'
7
9
  require 'database_consistency/configuration'
8
10
  require 'database_consistency/rescue_error'
9
11
  require 'database_consistency/errors'
@@ -44,6 +46,9 @@ require 'database_consistency/writers/simple/missing_association_class'
44
46
  require 'database_consistency/writers/simple/missing_table'
45
47
  require 'database_consistency/writers/simple/implicit_order_column_missing'
46
48
  require 'database_consistency/writers/simple/missing_dependent_destroy'
49
+ require 'database_consistency/writers/simple/missing_index_find_by'
50
+ require 'database_consistency/writers/simple/view_missing_primary_key'
51
+ require 'database_consistency/writers/simple/view_primary_key_column_missing'
47
52
  require 'database_consistency/writers/simple_writer'
48
53
 
49
54
  require 'database_consistency/writers/autofix/helpers/migration'
@@ -77,6 +82,7 @@ require 'database_consistency/checkers/enum_checkers/enum_type_checker'
77
82
 
78
83
  require 'database_consistency/checkers/model_checkers/model_checker'
79
84
  require 'database_consistency/checkers/model_checkers/missing_table_checker'
85
+ require 'database_consistency/checkers/model_checkers/view_primary_key_checker'
80
86
 
81
87
  require 'database_consistency/checkers/association_checkers/association_checker'
82
88
  require 'database_consistency/checkers/association_checkers/missing_index_checker'
@@ -93,6 +99,7 @@ require 'database_consistency/checkers/column_checkers/primary_key_type_checker'
93
99
  require 'database_consistency/checkers/column_checkers/enum_value_checker'
94
100
  require 'database_consistency/checkers/column_checkers/three_state_boolean_checker'
95
101
  require 'database_consistency/checkers/column_checkers/implicit_ordering_checker'
102
+ require 'database_consistency/checkers/column_checkers/missing_index_find_by_checker'
96
103
 
97
104
  require 'database_consistency/checkers/validator_checkers/validator_checker'
98
105
  require 'database_consistency/checkers/validator_checkers/missing_unique_index_checker'
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: database_consistency
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.3
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgeniy Demin
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-03-02 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: activerecord
@@ -135,6 +136,7 @@ dependencies:
135
136
  - - ">"
136
137
  - !ruby/object:Gem::Version
137
138
  version: '1.3'
139
+ description:
138
140
  email:
139
141
  - lawliet.djez@gmail.com
140
142
  executables:
@@ -156,6 +158,7 @@ files:
156
158
  - lib/database_consistency/checkers/column_checkers/enum_value_checker.rb
157
159
  - lib/database_consistency/checkers/column_checkers/implicit_ordering_checker.rb
158
160
  - lib/database_consistency/checkers/column_checkers/length_constraint_checker.rb
161
+ - lib/database_consistency/checkers/column_checkers/missing_index_find_by_checker.rb
159
162
  - lib/database_consistency/checkers/column_checkers/null_constraint_checker.rb
160
163
  - lib/database_consistency/checkers/column_checkers/primary_key_type_checker.rb
161
164
  - lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb
@@ -167,6 +170,7 @@ files:
167
170
  - lib/database_consistency/checkers/index_checkers/unique_index_checker.rb
168
171
  - lib/database_consistency/checkers/model_checkers/missing_table_checker.rb
169
172
  - lib/database_consistency/checkers/model_checkers/model_checker.rb
173
+ - lib/database_consistency/checkers/model_checkers/view_primary_key_checker.rb
170
174
  - lib/database_consistency/checkers/validator_checkers/case_sensitive_unique_validation_checker.rb
171
175
  - lib/database_consistency/checkers/validator_checkers/missing_unique_index_checker.rb
172
176
  - lib/database_consistency/checkers/validator_checkers/validator_checker.rb
@@ -178,7 +182,9 @@ files:
178
182
  - lib/database_consistency/databases/types/sqlite.rb
179
183
  - lib/database_consistency/debug_context.rb
180
184
  - lib/database_consistency/errors.rb
185
+ - lib/database_consistency/files_helper.rb
181
186
  - lib/database_consistency/helper.rb
187
+ - lib/database_consistency/prism_helper.rb
182
188
  - lib/database_consistency/processors/associations_processor.rb
183
189
  - lib/database_consistency/processors/base_processor.rb
184
190
  - lib/database_consistency/processors/columns_processor.rb
@@ -227,6 +233,7 @@ files:
227
233
  - lib/database_consistency/writers/simple/missing_dependent_destroy.rb
228
234
  - lib/database_consistency/writers/simple/missing_foreign_key.rb
229
235
  - lib/database_consistency/writers/simple/missing_foreign_key_cascade.rb
236
+ - lib/database_consistency/writers/simple/missing_index_find_by.rb
230
237
  - lib/database_consistency/writers/simple/missing_table.rb
231
238
  - lib/database_consistency/writers/simple/missing_unique_index.rb
232
239
  - lib/database_consistency/writers/simple/missing_uniqueness_validation.rb
@@ -239,6 +246,8 @@ files:
239
246
  - lib/database_consistency/writers/simple/redundant_unique_index.rb
240
247
  - lib/database_consistency/writers/simple/small_primary_key.rb
241
248
  - lib/database_consistency/writers/simple/three_state_boolean.rb
249
+ - lib/database_consistency/writers/simple/view_missing_primary_key.rb
250
+ - lib/database_consistency/writers/simple/view_primary_key_column_missing.rb
242
251
  - lib/database_consistency/writers/simple_writer.rb
243
252
  - lib/database_consistency/writers/todo_writer.rb
244
253
  homepage: https://github.com/djezzzl/database_consistency
@@ -273,7 +282,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
273
282
  - !ruby/object:Gem::Version
274
283
  version: '0'
275
284
  requirements: []
276
- rubygems_version: 3.7.2
285
+ rubygems_version: 3.4.19
286
+ signing_key:
277
287
  specification_version: 4
278
288
  summary: Provide an easy way to check the consistency of the database constraints
279
289
  with the application validations.