database_consistency 0.3.0 → 0.4.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: 0cb421a3fbd1c94df1816ecaf52487578c2a68c2aa13f384d1f80a9c68b35613
4
- data.tar.gz: 5a38ad0d44e448e08e7474e74ecd0f934f0b9bf86e2fe43a80c2bb4540b154e8
3
+ metadata.gz: f0980b34790693d62df725d9e527a8694480d343c44c5061250abe3900802b32
4
+ data.tar.gz: 514a6e6590244f3c31b43d7c997fc90d4f3eae5e4748930e4d28eefb6945c01a
5
5
  SHA512:
6
- metadata.gz: a6195f162dac4165dd70ce10fddc1dde43527f2d3dd845f9a4f2f4b9f0def589ed125fbe5cce3a5e61eb80c91ccefb609de4bc42ea0dcbe4ff26edd556522884
7
- data.tar.gz: 47ebc91829df9bc38a2f518b0b5d4b938a51d79d441984c257c9fa7429d12cc22d3d6184029396d29c0e422ac890c9311391ae9cbc6e2d052fae33e22694f11c
6
+ metadata.gz: f79aa59df5649f0313cb78023781c9ce59e73e2add191268606353530a957a299bed6b6834352b2fbb694cdef90c072ae1c5281cdf15e3187f65da04a758e874
7
+ data.tar.gz: d1b45c15a664d2832bc3cde9a511a779fa1bd9ca39b2c7c990cdfdedeced6a6f9fcf8a50b526f07eb79b8664ea2183fab4445465d1507e9aea8770394933560b
@@ -4,14 +4,10 @@ module DatabaseConsistency
4
4
  module Checkers
5
5
  # The base class for checkers
6
6
  class BaseChecker
7
- def initialize(table_or_model, column_or_attribute, opts = {})
8
- @table_or_model = table_or_model
9
- @column_or_attribute = column_or_attribute
10
- @opts = opts
11
- end
12
-
13
- # @return [Hash]
7
+ # @return [Hash, nil]
14
8
  def report
9
+ return unless preconditions
10
+
15
11
  @report ||= check
16
12
  end
17
13
 
@@ -22,6 +18,14 @@ module DatabaseConsistency
22
18
 
23
19
  private
24
20
 
21
+ def check
22
+ raise NotImplementedError
23
+ end
24
+
25
+ def preconditions
26
+ raise NotImplementedError
27
+ end
28
+
25
29
  attr_reader :table_or_model, :column_or_attribute, :opts
26
30
 
27
31
  # @return [String]
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Checkers
5
+ # This class checks if required +belongs_to+ has foreign key constraint
6
+ class BelongsToPresenceChecker < ValidatorChecker
7
+ MISSING_FOREIGN_KEY = 'should have foreign key in the database'
8
+
9
+ private
10
+
11
+ # We skip check when:
12
+ # - validator is a not a presence validator
13
+ # - there is no belongs_to reflection with given name
14
+ # - belongs_to reflection is polymorphic
15
+ def preconditions
16
+ validator.kind == :presence && reflection && !reflection.polymorphic?
17
+ end
18
+
19
+ # Table of possible statuses
20
+ # | foreign key | status |
21
+ # | ----------- | ------ |
22
+ # | persisted | ok |
23
+ # | missed | fail |
24
+ def check
25
+ if model.connection.foreign_keys(model.table_name).find { |fk| fk.column == reflection.foreign_key.to_s }
26
+ report_template(:ok)
27
+ else
28
+ report_template(:fail, MISSING_FOREIGN_KEY)
29
+ end
30
+ end
31
+
32
+ def reflection
33
+ @reflection ||= model.reflect_on_association(attribute)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -3,7 +3,7 @@
3
3
  module DatabaseConsistency
4
4
  module Checkers
5
5
  # This class checks PresenceValidator
6
- class PresenceValidationChecker < BaseChecker
6
+ class ColumnPresenceChecker < ValidatorChecker
7
7
  WEAK_OPTIONS = %i[allow_nil allow_blank if unless].freeze
8
8
  # Message templates
9
9
  CONSTRAINT_MISSING = 'should be required in the database'
@@ -11,6 +11,13 @@ module DatabaseConsistency
11
11
 
12
12
  private
13
13
 
14
+ # We skip check when:
15
+ # - validator is not a presence validator
16
+ # - there is no column in the database with given name
17
+ def preconditions
18
+ validator.kind == :presence && column
19
+ end
20
+
14
21
  # Table of possible statuses
15
22
  # | allow_nil/allow_blank/if/unless | database | status |
16
23
  # | ------------------------------- | -------- | ------ |
@@ -18,12 +25,7 @@ module DatabaseConsistency
18
25
  # | at least one provided | optional | ok |
19
26
  # | all missed | required | ok |
20
27
  # | all missed | optional | fail |
21
- #
22
- # We skip check when:
23
- # - there is no column in the database with given name
24
28
  def check
25
- return unless column
26
-
27
29
  can_be_null = column.null
28
30
  has_weak_option = validator.options.slice(*WEAK_OPTIONS).any?
29
31
 
@@ -37,19 +39,7 @@ module DatabaseConsistency
37
39
  end
38
40
 
39
41
  def column
40
- @column ||= Helper.find_field(table_or_model, column_or_attribute.to_s)
41
- end
42
-
43
- def column_or_attribute_name
44
- column_or_attribute.to_s
45
- end
46
-
47
- def table_or_model_name
48
- table_or_model.name.to_s
49
- end
50
-
51
- def validator
52
- opts[:validator]
42
+ @column ||= model.columns.select.find { |field| field.name == attribute.to_s }
53
43
  end
54
44
  end
55
45
  end
@@ -3,62 +3,59 @@
3
3
  module DatabaseConsistency
4
4
  module Checkers
5
5
  # This class checks missing presence validator
6
- class NullConstraintChecker < BaseChecker
6
+ class NullConstraintChecker < TableChecker
7
7
  # Message templates
8
8
  VALIDATOR_MISSING = 'is required but do not have presence validator'
9
9
 
10
10
  private
11
11
 
12
- # Table of possible statuses
13
- # | validation | database | status |
14
- # | ---------- | -------- | ------ |
15
- # | missed | required | fail |
16
- #
17
12
  # We skip check when:
18
13
  # - column hasn't null constraint
19
14
  # - column has default value
20
15
  # - column is a primary key
21
16
  # - column is a timestamp
22
- # - presence validation exists
23
- # - inclusion validation exists
24
- # - belongs_to reflection exists with given column as foreign key or foreign type
25
- def check
26
- return if skip? ||
27
- validator?(ActiveModel::Validations::PresenceValidator) ||
28
- validator?(ActiveModel::Validations::InclusionValidator) ||
29
- belongs_to_reflection?
30
-
31
- report_template(:fail, VALIDATOR_MISSING)
17
+ def preconditions
18
+ !column.null && column.default.nil? && !primary_field? && !timestamp_field?
32
19
  end
33
20
 
34
- def column_or_attribute_name
35
- column_or_attribute.name.to_s
21
+ # Table of possible statuses
22
+ # | validation | status |
23
+ # | ---------- | ------ |
24
+ # | provided | ok |
25
+ # | missed | fail |
26
+ #
27
+ # We consider PresenceValidation, InclusionValidation or BelongsTo reflection using this column
28
+ def check
29
+ if valid?
30
+ report_template(:ok)
31
+ else
32
+ report_template(:fail, VALIDATOR_MISSING)
33
+ end
36
34
  end
37
35
 
38
- def table_or_model_name
39
- table_or_model.name.to_s
36
+ def valid?
37
+ validator?(ActiveModel::Validations::PresenceValidator) ||
38
+ validator?(ActiveModel::Validations::InclusionValidator) ||
39
+ belongs_to_reflection?
40
40
  end
41
41
 
42
- def skip?
43
- column_or_attribute.null ||
44
- !column_or_attribute.default.nil? ||
45
- column_or_attribute.name == table_or_model.primary_key ||
46
- timestamp_field?
42
+ def primary_field?
43
+ column.name.to_s == model.primary_key.to_s
47
44
  end
48
45
 
49
46
  def timestamp_field?
50
- table_or_model.record_timestamps? && %w[created_at updated_at].include?(column_or_attribute.name)
47
+ model.record_timestamps? && %w[created_at updated_at].include?(column.name)
51
48
  end
52
49
 
53
50
  def validator?(validator_class)
54
- table_or_model.validators.grep(validator_class).any? do |validator|
55
- Helper.check_inclusion?(validator.attributes, column_or_attribute.name)
51
+ model.validators.grep(validator_class).any? do |validator|
52
+ Helper.check_inclusion?(validator.attributes, column.name)
56
53
  end
57
54
  end
58
55
 
59
56
  def belongs_to_reflection?
60
- table_or_model.reflect_on_all_associations.grep(ActiveRecord::Reflection::BelongsToReflection).any? do |r|
61
- Helper.check_inclusion?([r.foreign_key, r.foreign_type], column_or_attribute.name)
57
+ model.reflect_on_all_associations.grep(ActiveRecord::Reflection::BelongsToReflection).any? do |r|
58
+ Helper.check_inclusion?([r.foreign_key, r.foreign_type], column.name)
62
59
  end
63
60
  end
64
61
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Checkers
5
+ # The base class for table checkers
6
+ class TableChecker < BaseChecker
7
+ attr_reader :model, :column
8
+
9
+ def initialize(model, column)
10
+ @model = model
11
+ @column = column
12
+ end
13
+
14
+ def column_or_attribute_name
15
+ @column_or_attribute_name ||= column.name.to_s
16
+ end
17
+
18
+ def table_or_model_name
19
+ @table_or_model_name ||= model.name.to_s
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Checkers
5
+ # The base class for validator checker
6
+ class ValidatorChecker < BaseChecker
7
+ attr_reader :model, :attribute, :validator
8
+
9
+ def initialize(model, attribute, validator)
10
+ @model = model
11
+ @attribute = attribute
12
+ @validator = validator
13
+ end
14
+
15
+ def column_or_attribute_name
16
+ @column_or_attribute_name ||= attribute.to_s
17
+ end
18
+
19
+ def table_or_model_name
20
+ @table_or_model_name ||= model.name.to_s
21
+ end
22
+ end
23
+ end
24
+ end
@@ -22,11 +22,6 @@ module DatabaseConsistency
22
22
  Rails.application.eager_load! if defined?(Rails)
23
23
  end
24
24
 
25
- # Find a database field with name equals to attribute
26
- def find_field(model, attribute)
27
- model.columns.select.find { |field| field.name == attribute }
28
- end
29
-
30
25
  # @return [Boolean]
31
26
  def check_inclusion?(array, element)
32
27
  array.include?(element.to_s) || array.include?(element.to_sym)
@@ -4,9 +4,10 @@ module DatabaseConsistency
4
4
  module Processors
5
5
  # The class to process all comparators
6
6
  class ModelsProcessor < BaseProcessor
7
- CHECKERS = {
8
- presence: Checkers::PresenceValidationChecker
9
- }.freeze
7
+ CHECKERS = [
8
+ Checkers::ColumnPresenceChecker,
9
+ Checkers::BelongsToPresenceChecker
10
+ ].freeze
10
11
 
11
12
  private
12
13
 
@@ -14,11 +15,11 @@ module DatabaseConsistency
14
15
  def check
15
16
  Helper.parent_models.flat_map do |model|
16
17
  model.validators.flat_map do |validator|
17
- next unless (checker_class = CHECKERS[validator.kind])
18
-
19
- validator.attributes.map do |attribute|
20
- checker = checker_class.new(model, attribute, validator: validator)
21
- checker.report if checker.enabled?(configuration)
18
+ validator.attributes.flat_map do |attribute|
19
+ CHECKERS.map do |checker_class|
20
+ checker = checker_class.new(model, attribute, validator)
21
+ checker.report if checker.enabled?(configuration)
22
+ end
22
23
  end
23
24
  end
24
25
  end.compact
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseConsistency
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
@@ -10,8 +10,11 @@ require 'database_consistency/writers/base_writer'
10
10
  require 'database_consistency/writers/simple_writer'
11
11
 
12
12
  require 'database_consistency/checkers/base_checker'
13
- require 'database_consistency/checkers/presence_validation_checker'
13
+ require 'database_consistency/checkers/table_checker'
14
+ require 'database_consistency/checkers/validator_checker'
15
+ require 'database_consistency/checkers/column_presence_checker'
14
16
  require 'database_consistency/checkers/null_constraint_checker'
17
+ require 'database_consistency/checkers/belongs_to_presence_checker'
15
18
 
16
19
  require 'database_consistency/processors/base_processor'
17
20
  require 'database_consistency/processors/models_processor'
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.3.0
4
+ version: 0.4.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: 2019-01-04 00:00:00.000000000 Z
11
+ date: 2019-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -111,8 +111,11 @@ files:
111
111
  - bin/database_consistency
112
112
  - lib/database_consistency.rb
113
113
  - lib/database_consistency/checkers/base_checker.rb
114
+ - lib/database_consistency/checkers/belongs_to_presence_checker.rb
115
+ - lib/database_consistency/checkers/column_presence_checker.rb
114
116
  - lib/database_consistency/checkers/null_constraint_checker.rb
115
- - lib/database_consistency/checkers/presence_validation_checker.rb
117
+ - lib/database_consistency/checkers/table_checker.rb
118
+ - lib/database_consistency/checkers/validator_checker.rb
116
119
  - lib/database_consistency/configuration.rb
117
120
  - lib/database_consistency/helper.rb
118
121
  - lib/database_consistency/processors/base_processor.rb