database_consistency 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 814513a9fa9c4a886a0181cbb09e71fb35ed3ec722404746f827fe754067df01
4
+ data.tar.gz: 1638aee8d2c92c761905e6a0b433581d6f996bb3f7c871dc2a75c84956cbb12c
5
+ SHA512:
6
+ metadata.gz: 47ef92e9038f22b807588f560e3c66f95a776e8c07b47633b8f6352935ffdb2bf8e22e6f21178b9ca1a946d84ab345fb4fdf600f2d69bed5935bdf7c5e328eea
7
+ data.tar.gz: feb38ceb958dc96139b4cca1a3dd70d884c4955203227a0f4d7402ee7d3c0128983c3205f414590829eae508a9d412cd2fd67a80c955b318a327b50cb2b6189f
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ base_dir = File.join(Dir.pwd, ARGV.first.to_s)
4
+
5
+ unless File.realpath(base_dir).start_with?(Dir.pwd)
6
+ puts "\nWarning! You are going out of current directory, ruby version may be wrong and some gems may be missing.\n"
7
+ end
8
+
9
+ begin
10
+ require File.join(base_dir, 'config', 'boot')
11
+ require File.join(base_dir, 'config', 'environment')
12
+ rescue LoadError
13
+ puts "\nUh-oh! You must be in the root directory of a Rails project.\n"
14
+ raise
15
+ end
16
+
17
+ $LOAD_PATH.unshift(File.expand_path('lib', __dir__))
18
+ require 'database_consistency'
19
+
20
+ DatabaseConsistency.run
@@ -0,0 +1,25 @@
1
+ module DatabaseConsistency
2
+ module Comparators
3
+ # The base class for comparators
4
+ class BaseComparator
5
+ attr_reader :validator, :column
6
+
7
+ delegate :result, to: :comparison
8
+
9
+ private_class_method :new
10
+
11
+ def initialize(validator, column)
12
+ @validator = validator
13
+ @column = column
14
+ end
15
+
16
+ def comparison
17
+ Comparison.for(validator, column)
18
+ end
19
+
20
+ def self.compare(validator, column)
21
+ new(validator, column).compare
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ module DatabaseConsistency
2
+ module Comparators
3
+ # The comparator class for {{ActiveModel::Validations::PresenceValidator}}
4
+ class PresenceComparator < BaseComparator
5
+ WEAK_OPTIONS = %i[allow_nil allow_blank if unless].freeze
6
+ CONSTRAINT_MISSING = 'database field should have: "null: false"'.freeze
7
+ POSSIBLE_NULL = 'possible null value insert'.freeze
8
+
9
+ # Table of possible statuses
10
+ # | allow_nil/allow_blank/if/unless | database | status |
11
+ # | ------------------------------- | -------- | ------ |
12
+ # | at least one provided | required | fail |
13
+ # | at least one provided | optional | ok |
14
+ # | all missed | required | ok |
15
+ # | all missed | optional | fail |
16
+ def compare
17
+ can_be_null = column.null
18
+ has_weak_option = validator.options.slice(*WEAK_OPTIONS).any?
19
+
20
+ if can_be_null == has_weak_option
21
+ result(:ok)
22
+ elsif can_be_null
23
+ result(:fail, CONSTRAINT_MISSING)
24
+ else
25
+ result(:fail, POSSIBLE_NULL)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,25 @@
1
+ module DatabaseConsistency
2
+ # This class outputs the comparison result
3
+ class Comparison
4
+ attr_reader :validator, :column
5
+
6
+ private_class_method :new
7
+
8
+ def initialize(validator, column)
9
+ @validator = validator
10
+ @column = column
11
+ end
12
+
13
+ def result(status, message = nil)
14
+ {
15
+ column: column,
16
+ validator: validator,
17
+ status: status
18
+ }.tap { |hash| hash[:message] = message if message }
19
+ end
20
+
21
+ def self.for(validator, column)
22
+ new(validator, column)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ module DatabaseConsistency
2
+ # The module contains helper methods
3
+ module Helper
4
+ module_function
5
+
6
+ # Returns list of models to check
7
+ def models
8
+ ActiveRecord::Base.descendants.select { |model| model.validators.any? }
9
+ end
10
+
11
+ # Loads all models
12
+ def load_environment!
13
+ Rails.application.eager_load! if defined?(Rails)
14
+ end
15
+
16
+ # Find a database field with name equals to attribute
17
+ def find_field(model, attribute)
18
+ model.columns.select.find { |field| field.name == attribute }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ module DatabaseConsistency
2
+ # The class to begin
3
+ class Processor
4
+ COMPARATORS = {
5
+ presence: DatabaseConsistency::Comparators::PresenceComparator
6
+ }.freeze
7
+
8
+ def comparisons
9
+ Helper.models.each_with_object({}) do |model, hash|
10
+ hash[model.name] = model.validators.flat_map do |validator|
11
+ next unless (comparator = COMPARATORS[validator.kind])
12
+
13
+ validator.attributes.map do |attribute|
14
+ next unless (column = Helper.find_field(model, attribute.to_s))
15
+
16
+ comparator.compare(validator, column)
17
+ end
18
+ end.compact
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module DatabaseConsistency
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,21 @@
1
+ module DatabaseConsistency
2
+ module Writers
3
+ # The base class for writers
4
+ class BaseWriter
5
+ attr_reader :results, :log_level
6
+
7
+ def initialize(results, log_level)
8
+ @results = results
9
+ @log_level = log_level
10
+ end
11
+
12
+ def write?(status)
13
+ status == :fail || log_level == 'DEBUG'
14
+ end
15
+
16
+ def self.write(results, log_level)
17
+ new(results, log_level).write
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ module DatabaseConsistency
2
+ # The module contains formatters
3
+ module Writers
4
+ # The simplest formatter
5
+ class SimpleWriter < BaseWriter
6
+ def write
7
+ puts format
8
+ end
9
+
10
+ def format
11
+ results.map do |model_name, comparisons|
12
+ comparisons.map do |comparison|
13
+ next unless write?(comparison[:status])
14
+ line(model_name, comparison)
15
+ end.tap(&:compact!).map(&:lstrip).join("\n")
16
+ end.join("\n")
17
+ end
18
+
19
+ def line(model_name, comparison)
20
+ <<-TEXT
21
+ #{comparison[:status]} #{comparison[:message]} #{model_name} #{comparison[:validator].inspect} #{comparison[:column].inspect}
22
+ TEXT
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ require 'active_record'
2
+
3
+ require 'database_consistency/version'
4
+ require 'database_consistency/writers/base_writer'
5
+ require 'database_consistency/writers/simple_writer'
6
+ require 'database_consistency/comparison'
7
+ require 'database_consistency/helper'
8
+ require 'database_consistency/comparators/base_comparator'
9
+ require 'database_consistency/comparators/presence_comparator'
10
+ require 'database_consistency/processor'
11
+
12
+ # The root module
13
+ module DatabaseConsistency
14
+ def self.run
15
+ Helper.load_environment!
16
+
17
+ Writers::SimpleWriter.write(
18
+ Processor.new.comparisons,
19
+ ENV['LOG_LEVEL'] || 'INFO'
20
+ )
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: database_consistency
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Evgeniy Demin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-10-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '6'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '3.2'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '6'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.16'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.16'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '10.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '10.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rubocop
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.55'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.55'
89
+ - !ruby/object:Gem::Dependency
90
+ name: sqlite3
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.3'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.3'
103
+ description:
104
+ email:
105
+ - lawliet.djez@gmail.com
106
+ executables:
107
+ - database_consistency
108
+ extensions: []
109
+ extra_rdoc_files: []
110
+ files:
111
+ - bin/database_consistency
112
+ - lib/database_consistency.rb
113
+ - lib/database_consistency/comparators/base_comparator.rb
114
+ - lib/database_consistency/comparators/presence_comparator.rb
115
+ - lib/database_consistency/comparison.rb
116
+ - lib/database_consistency/helper.rb
117
+ - lib/database_consistency/processor.rb
118
+ - lib/database_consistency/version.rb
119
+ - lib/database_consistency/writers/base_writer.rb
120
+ - lib/database_consistency/writers/simple_writer.rb
121
+ homepage: https://github.com/djezzzl/database_consistency
122
+ licenses:
123
+ - MIT
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.7.7
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: Provide an easy way to check the consistency of the database constraints
145
+ with the application validations.
146
+ test_files: []