database_consistency 0.1.0

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