database_consistency 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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