database_consistency 1.1.5 → 1.1.10

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: 8c43909523b8cb7b8369f55dc151bf84e32752276907a249ada7606edffbbdca
4
- data.tar.gz: b2a27e403ab36224ab3f432f4bd4d8021e75306c000b8fd20c01ea2cd868d795
3
+ metadata.gz: 1b191ca6be5af749e9c3e7bd634696e5ecc648972f5b882954990bcc9e13dde2
4
+ data.tar.gz: 460e7edbac351faf21032e9d71c768d0e98b12ddafad792516bc5f4158122525
5
5
  SHA512:
6
- metadata.gz: a7dac1b50e3e621c62f5bb00c96ef130bee3a862d70c3078dc29dd2538ca5f6a66f6772aa449ffed3771354a0514ab21e2b58df4b125dd62415f163b936667ee
7
- data.tar.gz: f71af9349ed08f62b295c47cb32dc11a591a59cd44784e8f5ba00390c8011ccbc8597dc6cd63a5db4eee965f1ae0b758c8fa7487503c0f5469c27e492f7bfcd1
6
+ metadata.gz: 4b1da4a7207245abc904aa28fcbbf2d8fe7c15683f86ad55557ba10b6d7699103404c42ed33a6fda82a413e4a6ed73681579dfd9512c7480d60a5f3da7d9d335
7
+ data.tar.gz: a4a13d8527677783f144448bfbe892d282086fffbe6dd04121aa50052d435c2d370eee32ee7c378047a2cf47817fcee4b74b9e7d0f9ed4b652d80431948196cc
@@ -1,27 +1,52 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require_relative '../lib/database_consistency/configuration'
4
+
5
+ default_config = DatabaseConsistency::Configuration::DEFAULT_PATH
6
+
3
7
  if ARGV.include?('install')
4
- require_relative '../lib/database_consistency/configuration'
5
- config_filename = DatabaseConsistency::Configuration::CONFIGURATION_PATH
6
- file_exists = File.exists?(config_filename)
8
+ require 'pathname'
9
+
10
+ file_exists = File.exists?(default_config)
7
11
  rules = Pathname.new(__FILE__).dirname.join('..', 'lib', 'database_consistency', 'templates', 'rails_defaults.yml').read
8
- if file_exists && File.foreach(config_filename).grep(Regexp.new(rules.lines.first.chomp)).any?
9
- puts "#{config_filename} is already present"
12
+ if file_exists && File.foreach(default_config).grep(Regexp.new(rules.lines.first.chomp)).any?
13
+ puts "#{default_config} is already present"
10
14
  else
11
- File.open(config_filename, 'a') do |file|
15
+ File.open(default_config, 'a') do |file|
12
16
  file << "\n" * 2 if file_exists
13
17
  file << rules
14
18
  end
15
- puts "#{config_filename} #{file_exists ? 'updated' : 'added'}"
19
+ puts "#{default_config} #{file_exists ? 'updated' : 'added'}"
16
20
  end
17
21
  exit 0
18
- else
19
- base_dir = File.join(Dir.pwd, ARGV.first.to_s)
20
- unless File.realpath(base_dir).start_with?(Dir.pwd)
21
- puts "\nWarning! You are going out of current directory, ruby version may be wrong and some gems may be missing.\n"
22
+ end
23
+
24
+ require 'optparse'
25
+
26
+ config = [default_config]
27
+ opt_parser = OptionParser.new do |opts|
28
+ opts.banner = <<-DESC
29
+ Usage: database_consistency install - run installation
30
+ database_consistency [options]
31
+ DESC
32
+
33
+ opts.on('-cFILE', '--config=FILE', 'Use additional configuration file') do |f|
34
+ config << f
35
+ end
36
+
37
+ opts.on('-h', '--help', 'Prints this help') do
38
+ puts opts
39
+ exit
22
40
  end
23
41
  end
24
42
 
43
+ opt_parser.parse!
44
+
45
+ base_dir = File.join(Dir.pwd, ARGV.first.to_s)
46
+ unless File.realpath(base_dir).start_with?(Dir.pwd)
47
+ puts "\nWarning! You are going out of current directory, ruby version may be wrong and some gems may be missing.\n"
48
+ end
49
+
25
50
  # Load Rails project
26
51
  begin
27
52
  require File.join(base_dir, 'config', 'boot')
@@ -41,5 +66,5 @@ $LOAD_PATH.unshift(File.expand_path('lib', __dir__))
41
66
  require 'database_consistency'
42
67
 
43
68
  # Process checks
44
- code = DatabaseConsistency.run
69
+ code = DatabaseConsistency.run(config)
45
70
  exit code
@@ -16,7 +16,7 @@ module DatabaseConsistency
16
16
  end
17
17
 
18
18
  def supported?
19
- return false if ActiveRecord::VERSION::MAJOR < 5 && ActiveRecord::Base.connection_config[:adapter] == 'sqlite3'
19
+ return false if ActiveRecord::VERSION::MAJOR < 5 && Helper.adapter == 'sqlite3'
20
20
 
21
21
  true
22
22
  end
@@ -2,18 +2,9 @@
2
2
 
3
3
  module DatabaseConsistency
4
4
  module Checkers
5
- # This class checks if association's foreign key type is the same as associated model's primary key
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
- 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
7
+ INCONSISTENT_TYPE = "foreign key (%a_f) with type (%a_t) doesn't cover primary key (%b_f) with type (%b_t)"
17
8
 
18
9
  private
19
10
 
@@ -32,16 +23,18 @@ module DatabaseConsistency
32
23
  end
33
24
 
34
25
  # Table of possible statuses
35
- # | type | status |
36
- # | ------------ | ------ |
37
- # | consistent | ok |
38
- # | inconsistent | fail |
26
+ # | type | status |
27
+ # | ------------- | ------ |
28
+ # | covers | ok |
29
+ # | doesn't cover | fail |
39
30
  def check
40
- if converted_type(base_column) == converted_type(associated_column)
31
+ if converted_type(associated_column).cover?(converted_type(primary_column))
41
32
  report_template(:ok)
42
33
  else
43
34
  report_template(:fail, render_text)
44
35
  end
36
+ rescue Errors::MissingField => e
37
+ report_template(:fail, e.message)
45
38
  end
46
39
 
47
40
  # @return [String]
@@ -49,40 +42,40 @@ module DatabaseConsistency
49
42
  INCONSISTENT_TYPE
50
43
  .gsub('%a_t', type(associated_column))
51
44
  .gsub('%a_f', associated_key)
52
- .gsub('%b_t', type(base_column))
53
- .gsub('%b_f', base_key)
45
+ .gsub('%b_t', type(primary_column))
46
+ .gsub('%b_f', primary_key)
54
47
  end
55
48
 
56
49
  # @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
50
+ def primary_key
51
+ @primary_key ||= if belongs_to_association?
52
+ association.association_primary_key
53
+ else
54
+ association.active_record_primary_key
55
+ end.to_s
65
56
  end
66
57
 
67
58
  # @return [String]
68
59
  def associated_key
69
- @associated_key ||= (
70
- if belongs_to_association?
71
- association.association_primary_key
72
- else
73
- association.foreign_key
74
- end
75
- ).to_s
60
+ @associated_key ||= association.foreign_key.to_s
76
61
  end
77
62
 
78
63
  # @return [ActiveRecord::ConnectionAdapters::Column]
79
- def base_column
80
- @base_column ||= column(association.active_record, base_key)
64
+ def primary_column
65
+ @primary_column ||= if belongs_to_association?
66
+ column(association.klass, primary_key)
67
+ else
68
+ column(association.active_record, primary_key)
69
+ end
81
70
  end
82
71
 
83
72
  # @return [ActiveRecord::ConnectionAdapters::Column]
84
73
  def associated_column
85
- @associated_column ||= column(association.klass, associated_key)
74
+ @associated_column ||= if belongs_to_association?
75
+ column(association.active_record, associated_key)
76
+ else
77
+ column(association.klass, associated_key)
78
+ end
86
79
  end
87
80
 
88
81
  # @return [DatabaseConsistency::Databases::Factory]
@@ -101,8 +94,8 @@ module DatabaseConsistency
101
94
 
102
95
  # @return [String]
103
96
  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."
97
+ "association (#{association.name}) of class (#{association.active_record}) relies on "\
98
+ "field (#{column_name}) of table (#{table_name}) but it is missing"
106
99
  end
107
100
 
108
101
  # @param [ActiveRecord::ConnectionAdapters::Column] column
@@ -114,9 +107,9 @@ module DatabaseConsistency
114
107
 
115
108
  # @param [ActiveRecord::ConnectionAdapters::Column]
116
109
  #
117
- # @return [String]
110
+ # @return [DatabaseConsistency::Databases::Types::Base]
118
111
  def converted_type(column)
119
- database_factory.type(type(column)).convert
112
+ database_factory.type(type(column))
120
113
  end
121
114
 
122
115
  # @return [Boolean]
@@ -29,8 +29,18 @@ module DatabaseConsistency
29
29
  # | all missing | required | ok |
30
30
  # | all missing | optional | fail |
31
31
  def check
32
+ report_message
33
+ rescue Errors::MissingField => e
34
+ report_template(:fail, e.message)
35
+ end
36
+
37
+ def weak_option?
38
+ validators.all? { |validator| validator.options.slice(*WEAK_OPTIONS).any? }
39
+ end
40
+
41
+ def report_message
32
42
  can_be_null = column.null
33
- has_weak_option = validators.all? { |validator| validator.options.slice(*WEAK_OPTIONS).any? }
43
+ has_weak_option = weak_option?
34
44
 
35
45
  return report_template(:ok) if can_be_null == has_weak_option
36
46
  return report_template(:fail, POSSIBLE_NULL) unless can_be_null
@@ -43,8 +53,9 @@ module DatabaseConsistency
43
53
  end
44
54
 
45
55
  def column
46
- @column ||= regular_column || association_reference_column ||
47
- (raise Errors::MissingField, "Missing column in #{model.table_name} for #{attribute}")
56
+ @column ||= regular_column ||
57
+ association_reference_column ||
58
+ (raise Errors::MissingField, "column (#{attribute}) is missing in table (#{model.table_name}) but used for presence validation") # rubocop:disable Layout/LineLength
48
59
  end
49
60
 
50
61
  def regular_column
@@ -5,15 +5,20 @@ require 'yaml'
5
5
  module DatabaseConsistency
6
6
  # The class to access configuration options
7
7
  class Configuration
8
- CONFIGURATION_PATH = '.database_consistency.yml'
9
-
10
- def initialize(filepath = CONFIGURATION_PATH)
11
- @configuration = if filepath && File.exist?(filepath)
12
- data = YAML.load_file(filepath)
13
- data.is_a?(Hash) ? data : {}
14
- else
15
- {}
16
- end
8
+ DEFAULT_PATH = '.database_consistency.yml'
9
+
10
+ def initialize(filepaths = DEFAULT_PATH)
11
+ @configuration = Array(filepaths).each_with_object({}) do |filepath, result|
12
+ content =
13
+ if filepath && File.exist?(filepath)
14
+ data = load_yaml_config_file(filepath)
15
+ data.is_a?(Hash) ? data : {}
16
+ else
17
+ {}
18
+ end
19
+
20
+ combine_configs!(result, content)
21
+ end
17
22
  end
18
23
 
19
24
  def debug?
@@ -48,6 +53,24 @@ module DatabaseConsistency
48
53
 
49
54
  attr_reader :configuration
50
55
 
56
+ def load_yaml_config_file(filepath)
57
+ if YAML.respond_to?(:safe_load_file)
58
+ YAML.safe_load_file(filepath, aliases: true)
59
+ else
60
+ YAML.load_file(filepath)
61
+ end
62
+ end
63
+
64
+ def combine_configs!(config, new_config)
65
+ config.merge!(new_config) do |_key, val, new_val|
66
+ if val.is_a?(Hash) && new_val.is_a?(Hash)
67
+ combine_configs!(val, new_val)
68
+ else
69
+ new_val
70
+ end
71
+ end
72
+ end
73
+
51
74
  def settings
52
75
  @settings ||= configuration['DatabaseConsistencySettings']
53
76
  end
@@ -7,6 +7,11 @@ module DatabaseConsistency
7
7
  class Base
8
8
  attr_reader :type
9
9
 
10
+ COVERED_TYPES = {
11
+ 'bigint' => %w[integer bigint],
12
+ 'integer' => %w[integer smallint]
13
+ }.freeze
14
+
10
15
  # @param [String] type
11
16
  def initialize(type)
12
17
  @type = type
@@ -16,6 +21,13 @@ module DatabaseConsistency
16
21
  def convert
17
22
  type
18
23
  end
24
+
25
+ # @param [DatabaseConsistency::Databases::Types::Base]
26
+ #
27
+ # @return [Boolean]
28
+ def cover?(other_type)
29
+ (COVERED_TYPES[convert] || [convert]).include?(other_type.convert)
30
+ end
19
31
  end
20
32
  end
21
33
  end
@@ -7,9 +7,10 @@ module DatabaseConsistency
7
7
  class Sqlite < Base
8
8
  TYPES = {
9
9
  'bigserial' => 'bigint',
10
- 'bigint' => 'bigint',
11
10
  'serial' => 'integer',
12
- 'integer' => 'integer'
11
+ 'integer(8)' => 'bigint',
12
+ 'integer(4)' => 'integer',
13
+ 'integer(2)' => 'smallint'
13
14
  }.freeze
14
15
 
15
16
  # @return [String]
@@ -5,6 +5,14 @@ module DatabaseConsistency
5
5
  module Helper
6
6
  module_function
7
7
 
8
+ def adapter
9
+ if ActiveRecord::Base.respond_to?(:connection_config)
10
+ ActiveRecord::Base.connection_config[:adapter]
11
+ else
12
+ ActiveRecord::Base.connection_db_config.configuration_hash[:adapter]
13
+ end
14
+ end
15
+
8
16
  # Returns list of models to check
9
17
  def models
10
18
  ActiveRecord::Base.descendants.delete_if(&:abstract_class?).delete_if do |klass|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseConsistency
4
- VERSION = '1.1.5'
4
+ VERSION = '1.1.10'
5
5
  end
@@ -48,8 +48,8 @@ require 'database_consistency/processors/indexes_processor'
48
48
  # The root module
49
49
  module DatabaseConsistency
50
50
  class << self
51
- def run
52
- configuration = Configuration.new
51
+ def run(*args)
52
+ configuration = Configuration.new(*args)
53
53
  reports = Processors.reports(configuration)
54
54
 
55
55
  Writers::SimpleWriter.write(
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.1.5
4
+ version: 1.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgeniy Demin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-31 00:00:00.000000000 Z
11
+ date: 2022-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -56,14 +56,14 @@ dependencies:
56
56
  name: pg
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0.2'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.2'
69
69
  - !ruby/object:Gem::Dependency
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec_junit_formatter
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.4'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.4'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: rubocop
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -185,7 +199,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
185
199
  - !ruby/object:Gem::Version
186
200
  version: '0'
187
201
  requirements: []
188
- rubygems_version: 3.0.8
202
+ rubygems_version: 3.2.22
189
203
  signing_key:
190
204
  specification_version: 4
191
205
  summary: Provide an easy way to check the consistency of the database constraints