db_validator 0.3.0 → 1.0.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 +4 -4
- data/lib/db_validator/cli.rb +2 -2
- data/lib/db_validator/config_updater.rb +40 -0
- data/lib/db_validator/formatters/json_formatter.rb +1 -1
- data/lib/db_validator/formatters/message_formatter.rb +59 -0
- data/lib/db_validator/reporter.rb +81 -61
- data/lib/db_validator/test_task.rb +59 -0
- data/lib/db_validator/validate_task.rb +60 -0
- data/lib/db_validator/validator.rb +38 -17
- data/lib/db_validator/version.rb +1 -1
- data/lib/db_validator.rb +3 -0
- data/lib/tasks/db_validator_tasks.rake +3 -73
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 130796e31968cc4d325f42092eb82f8e21a67c56fdfc751116f354f6998c93f5
|
4
|
+
data.tar.gz: e4ec35da042e41699b25e607f642d9208564bf49f88f99be191029756f1e7f96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6cdeaf577ff2b53836dca41616b8c90fb0bdce77d0e8e8d456fcb1fc3889ae02373b72cf70df4829c77d22f3ef0217f211c02a39b779d3a06fdfd2aaba66c085
|
7
|
+
data.tar.gz: b1abf3df297599475a5876aef7fed5a4e6c304f5175ed686c003ee75214c08f87755a5c5fa7825a8171d37af495723e0d357c8e56511ca358190f998f4498dad
|
data/lib/db_validator/cli.rb
CHANGED
@@ -100,7 +100,7 @@ module DbValidator
|
|
100
100
|
|
101
101
|
if available_models.empty?
|
102
102
|
Rails.logger.debug "No models found in the application."
|
103
|
-
|
103
|
+
raise "No models found in the application. Please run this command from your Rails application root."
|
104
104
|
end
|
105
105
|
|
106
106
|
selected_models = select_models(available_models)
|
@@ -116,7 +116,7 @@ module DbValidator
|
|
116
116
|
require File.expand_path("config/environment", Dir.pwd)
|
117
117
|
rescue LoadError
|
118
118
|
Rails.logger.debug "Error: Rails application not found. Please run this command from your Rails application root."
|
119
|
-
|
119
|
+
raise "Rails application not found. Please run this command from your Rails application root."
|
120
120
|
end
|
121
121
|
|
122
122
|
def configure_validator(models = nil, options = {})
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DbValidator
|
4
|
+
class ConfigUpdater
|
5
|
+
def self.update_from_env
|
6
|
+
new.update_from_env
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.update_from_options(options)
|
10
|
+
new.update_from_options(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def update_from_env
|
14
|
+
update_config(
|
15
|
+
limit: ENV["limit"]&.to_i,
|
16
|
+
report_format: ENV["format"]&.to_sym,
|
17
|
+
show_records: ENV["show_records"] != "false"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def update_from_options(options)
|
22
|
+
update_config(
|
23
|
+
limit: options[:limit],
|
24
|
+
batch_size: options[:batch_size],
|
25
|
+
report_format: options[:format]&.to_sym,
|
26
|
+
show_records: options[:show_records]
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def update_config(settings)
|
33
|
+
settings.each do |key, value|
|
34
|
+
next unless value
|
35
|
+
|
36
|
+
DbValidator.configuration.public_send("#{key}=", value)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -33,7 +33,7 @@ module DbValidator
|
|
33
33
|
|
34
34
|
def save_to_file(data)
|
35
35
|
FileUtils.mkdir_p("db_validator_reports")
|
36
|
-
timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
|
36
|
+
timestamp = Time.zone.now.strftime("%Y%m%d_%H%M%S")
|
37
37
|
filename = "db_validator_reports/validation_report_#{timestamp}.json"
|
38
38
|
|
39
39
|
File.write(filename, JSON.pretty_generate(data))
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DbValidator
|
4
|
+
module Formatters
|
5
|
+
class MessageFormatter
|
6
|
+
def initialize(record)
|
7
|
+
@record = record
|
8
|
+
end
|
9
|
+
|
10
|
+
def format_error_message(error, field_value, message)
|
11
|
+
return enum_validation_message(error, field_value, message) if error.options[:in].present?
|
12
|
+
return enum_field_message(error, field_value, message) if enum_field?(error)
|
13
|
+
|
14
|
+
basic_validation_message(error, field_value, message)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :record
|
20
|
+
|
21
|
+
def enum_validation_message(error, field_value, message)
|
22
|
+
allowed = error.options[:in].join(", ")
|
23
|
+
error_message = "#{error.attribute} #{message}"
|
24
|
+
details = " (allowed values: #{allowed}, actual value: #{field_value.inspect})"
|
25
|
+
|
26
|
+
"#{error_message} #{details}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def enum_field_message(error, field_value, message)
|
30
|
+
enum_values = record.class.defined_enums[error.attribute.to_s].keys
|
31
|
+
error_message = "#{error.attribute} #{message}"
|
32
|
+
details = " (allowed values: #{enum_values.join(', ')}, actual value: #{field_value.inspect})"
|
33
|
+
|
34
|
+
"#{error_message} #{details}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def enum_field?(error)
|
38
|
+
record.class.defined_enums[error.attribute.to_s].present?
|
39
|
+
end
|
40
|
+
|
41
|
+
def basic_validation_message(error, field_value, message)
|
42
|
+
"#{error.attribute} #{message} (actual value: #{format_value(field_value)})"
|
43
|
+
end
|
44
|
+
|
45
|
+
def format_value(value)
|
46
|
+
case value
|
47
|
+
when true, false, Symbol
|
48
|
+
value.to_s
|
49
|
+
when String
|
50
|
+
"\"#{value}\""
|
51
|
+
when nil
|
52
|
+
"nil"
|
53
|
+
else
|
54
|
+
value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require "tty-box"
|
4
4
|
require "tty-spinner"
|
5
5
|
require "db_validator/formatters/json_formatter"
|
6
|
+
require "db_validator/formatters/message_formatter"
|
6
7
|
|
7
8
|
module DbValidator
|
8
9
|
class Reporter
|
@@ -11,15 +12,11 @@ module DbValidator
|
|
11
12
|
end
|
12
13
|
|
13
14
|
def add_invalid_record(record)
|
15
|
+
formatter = Formatters::MessageFormatter.new(record)
|
14
16
|
enhanced_errors = record.errors.map do |error|
|
15
17
|
field_value = record.send(error.attribute)
|
16
18
|
message = error.message
|
17
|
-
|
18
|
-
if error.options[:in].present?
|
19
|
-
"#{error.attribute} #{message} (allowed values: #{error.options[:in].join(', ')}, actual value: #{field_value.inspect})"
|
20
|
-
else
|
21
|
-
"#{error.attribute} #{message} (actual value: #{format_value(field_value)})"
|
22
|
-
end
|
19
|
+
formatter.format_error_message(error, field_value, message)
|
23
20
|
end
|
24
21
|
|
25
22
|
@invalid_records << {
|
@@ -29,6 +26,11 @@ module DbValidator
|
|
29
26
|
}
|
30
27
|
end
|
31
28
|
|
29
|
+
def generate_report_message(error, field_value, message)
|
30
|
+
formatter = Formatters::MessageFormatter.new(record)
|
31
|
+
formatter.format_error_message(error, field_value, message)
|
32
|
+
end
|
33
|
+
|
32
34
|
def generate_report
|
33
35
|
case DbValidator.configuration.report_format
|
34
36
|
when :json
|
@@ -40,22 +42,81 @@ module DbValidator
|
|
40
42
|
|
41
43
|
private
|
42
44
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
else
|
52
|
-
value
|
45
|
+
def generate_text_report
|
46
|
+
print_title
|
47
|
+
|
48
|
+
report = StringIO.new
|
49
|
+
|
50
|
+
if @invalid_records.empty?
|
51
|
+
report.puts "No invalid records found."
|
52
|
+
return report.string
|
53
53
|
end
|
54
|
+
|
55
|
+
report.puts print_summary
|
56
|
+
report.puts
|
57
|
+
|
58
|
+
@invalid_records.group_by { |r| r[:model] }.each do |model, records|
|
59
|
+
report.puts generate_model_report(model, records)
|
60
|
+
end
|
61
|
+
|
62
|
+
report.string
|
54
63
|
end
|
55
64
|
|
56
|
-
def
|
65
|
+
def print_summary
|
66
|
+
report = StringIO.new
|
67
|
+
is_plural = @invalid_records.count > 1
|
68
|
+
record_word = is_plural ? "records" : "record"
|
69
|
+
model_word = is_plural ? "models" : "model"
|
70
|
+
|
71
|
+
report.puts "Found #{@invalid_records.count} invalid #{record_word} across #{@invalid_records.group_by do |r|
|
72
|
+
r[:model]
|
73
|
+
end.keys.count} #{model_word}"
|
74
|
+
|
75
|
+
report.string
|
76
|
+
end
|
77
|
+
|
78
|
+
def generate_model_report(model, records)
|
79
|
+
report = StringIO.new
|
80
|
+
report.puts
|
81
|
+
report.puts "#{model}: #{records.count} invalid #{records.count == 1 ? 'record' : 'records'}"
|
82
|
+
report.puts
|
83
|
+
|
84
|
+
records.each_with_index do |record, index|
|
85
|
+
report.puts generate_record_report(record, index)
|
86
|
+
end
|
87
|
+
|
88
|
+
report.string
|
89
|
+
end
|
90
|
+
|
91
|
+
def generate_record_report(record, index) # rubocop:disable Metrics/AbcSize
|
57
92
|
report = StringIO.new
|
58
93
|
|
94
|
+
record_obj = record[:model].constantize.find_by(id: record[:id])
|
95
|
+
info = []
|
96
|
+
info << "Record ##{index + 1}"
|
97
|
+
info << "ID: #{record[:id]}"
|
98
|
+
|
99
|
+
# Add timestamps if available
|
100
|
+
if record_obj.respond_to?(:created_at)
|
101
|
+
info << "Created: #{record_obj.created_at.strftime('%b %d, %Y at %I:%M %p')}"
|
102
|
+
end
|
103
|
+
if record_obj.respond_to?(:updated_at)
|
104
|
+
info << "Updated: #{record_obj.updated_at.strftime('%b %d, %Y at %I:%M %p')}"
|
105
|
+
end
|
106
|
+
|
107
|
+
# Add identifying fields if available
|
108
|
+
info << "Name: #{record_obj.name}" if record_obj.respond_to?(:name) && record_obj.name.present?
|
109
|
+
info << "Title: #{record_obj.title}" if record_obj.respond_to?(:title) && record_obj.title.present?
|
110
|
+
|
111
|
+
report.puts " #{info.join(', ')}"
|
112
|
+
record[:errors].each do |error|
|
113
|
+
report.puts " \e[31m- #{error}\e[0m"
|
114
|
+
end
|
115
|
+
|
116
|
+
report.string
|
117
|
+
end
|
118
|
+
|
119
|
+
def print_title
|
59
120
|
title_box = TTY::Box.frame(
|
60
121
|
width: 50,
|
61
122
|
align: :center,
|
@@ -71,50 +132,9 @@ module DbValidator
|
|
71
132
|
"Database Validation Report"
|
72
133
|
end
|
73
134
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
if @invalid_records.empty?
|
78
|
-
report.puts "No invalid records found."
|
79
|
-
else
|
80
|
-
is_plural = @invalid_records.count > 1
|
81
|
-
report.puts "Found #{@invalid_records.count} invalid #{is_plural ? 'records' : 'record'} across #{@invalid_records.group_by do |r|
|
82
|
-
r[:model]
|
83
|
-
end.keys.count} #{is_plural ? 'models' : 'model'}"
|
84
|
-
report.puts
|
85
|
-
|
86
|
-
@invalid_records.group_by { |r| r[:model] }.each do |model, records|
|
87
|
-
report.puts "#{model}: #{records.count} invalid #{records.count == 1 ? 'record' : 'records'}"
|
88
|
-
|
89
|
-
next if DbValidator.configuration.show_records == false
|
90
|
-
|
91
|
-
report.puts
|
92
|
-
|
93
|
-
records.each do |record|
|
94
|
-
record_obj = record[:model].constantize.find_by(id: record[:id])
|
95
|
-
|
96
|
-
info = ["ID: #{record[:id]}"]
|
97
|
-
if record_obj.respond_to?(:created_at)
|
98
|
-
info << "Created: #{record_obj.created_at.strftime('%Y-%m-%d %H:%M:%S')}"
|
99
|
-
end
|
100
|
-
if record_obj.respond_to?(:updated_at)
|
101
|
-
info << "Updated: #{record_obj.updated_at.strftime('%Y-%m-%d %H:%M:%S')}"
|
102
|
-
end
|
103
|
-
info << "Name: #{record_obj.name}" if record_obj.respond_to?(:name)
|
104
|
-
info << "Title: #{record_obj.title}" if record_obj.respond_to?(:title)
|
105
|
-
|
106
|
-
report.puts " #{info.join(' | ')}"
|
107
|
-
record[:errors].each do |error|
|
108
|
-
report.puts " ⚠️ #{error}"
|
109
|
-
end
|
110
|
-
report.puts
|
111
|
-
end
|
112
|
-
|
113
|
-
report.puts
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
report.string
|
135
|
+
puts
|
136
|
+
puts title_box
|
137
|
+
puts
|
118
138
|
end
|
119
139
|
end
|
120
140
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DbValidator
|
4
|
+
class TestTask
|
5
|
+
def initialize(model_name, validation_rule)
|
6
|
+
@model_name = model_name
|
7
|
+
@validation_rule = validation_rule
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
validate_and_test_model
|
12
|
+
rescue NameError
|
13
|
+
puts "Model '#{@model_name}' not found"
|
14
|
+
raise "Model '#{@model_name}' not found"
|
15
|
+
rescue SyntaxError
|
16
|
+
puts "Invalid validation rule syntax"
|
17
|
+
raise "Invalid validation rule syntax"
|
18
|
+
ensure
|
19
|
+
cleanup_temporary_model
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def validate_and_test_model
|
25
|
+
base_model = @model_name.constantize
|
26
|
+
validate_attribute(base_model)
|
27
|
+
|
28
|
+
temp_model = create_temporary_model(base_model)
|
29
|
+
Object.const_set("Temporary#{@model_name}", temp_model)
|
30
|
+
|
31
|
+
validator = DbValidator::Validator.new
|
32
|
+
report = validator.validate_test_model("Temporary#{@model_name}")
|
33
|
+
puts report
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate_attribute(base_model)
|
37
|
+
attribute_match = @validation_rule.match(/validates\s+:(\w+)/)
|
38
|
+
return unless attribute_match
|
39
|
+
|
40
|
+
attribute_name = attribute_match[1]
|
41
|
+
return if base_model.column_names.include?(attribute_name) || base_model.method_defined?(attribute_name)
|
42
|
+
|
43
|
+
puts "Attribute '#{attribute_name}' does not exist for model '#{@model_name}'"
|
44
|
+
raise "Attribute '#{attribute_name}' does not exist for model '#{@model_name}'"
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_temporary_model(base_model)
|
48
|
+
Class.new(base_model) do
|
49
|
+
self.table_name = base_model.table_name
|
50
|
+
class_eval(@validation_rule)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def cleanup_temporary_model
|
55
|
+
temp_const_name = "Temporary#{@model_name}"
|
56
|
+
Object.send(:remove_const, temp_const_name) if Object.const_defined?(temp_const_name)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DbValidator
|
4
|
+
class ValidateTask
|
5
|
+
def initialize(cli = DbValidator::CLI.new)
|
6
|
+
@cli = cli
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute
|
10
|
+
configure_from_env_or_cli
|
11
|
+
run_validation
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def configure_from_env_or_cli
|
17
|
+
if env_args_present?
|
18
|
+
configure_from_env
|
19
|
+
else
|
20
|
+
configure_from_cli
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def env_args_present?
|
25
|
+
ENV["models"].present? || ENV["limit"].present? ||
|
26
|
+
ENV["format"].present? || ENV["show_records"].present?
|
27
|
+
end
|
28
|
+
|
29
|
+
def configure_from_env
|
30
|
+
if ENV["models"].present?
|
31
|
+
models = ENV["models"].split(",").map(&:strip).map(&:classify)
|
32
|
+
DbValidator.configuration.only_models = models
|
33
|
+
end
|
34
|
+
|
35
|
+
ConfigUpdater.update_from_env
|
36
|
+
end
|
37
|
+
|
38
|
+
def configure_from_cli
|
39
|
+
@cli.display_progress("Loading models") { Rails.application.eager_load! }
|
40
|
+
|
41
|
+
available_models = ActiveRecord::Base.descendants
|
42
|
+
.reject(&:abstract_class?)
|
43
|
+
.select(&:table_exists?)
|
44
|
+
.map(&:name)
|
45
|
+
.sort
|
46
|
+
|
47
|
+
selected_models = @cli.select_models(available_models)
|
48
|
+
options = @cli.configure_options
|
49
|
+
|
50
|
+
DbValidator.configuration.only_models = selected_models
|
51
|
+
ConfigUpdater.update_from_options(options)
|
52
|
+
end
|
53
|
+
|
54
|
+
def run_validation
|
55
|
+
validator = DbValidator::Validator.new
|
56
|
+
report = validator.validate_all
|
57
|
+
puts report
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -12,7 +12,7 @@ module DbValidator
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def validate_all
|
15
|
-
models =
|
15
|
+
models = models_to_validate
|
16
16
|
invalid_count = 0
|
17
17
|
|
18
18
|
models.each do |model|
|
@@ -24,15 +24,21 @@ module DbValidator
|
|
24
24
|
Rails.logger.debug "\nValidation passed! All records are valid."
|
25
25
|
else
|
26
26
|
total_records = models.sum(&:count)
|
27
|
-
|
28
|
-
Rails.logger.debug do
|
29
|
-
"\nFound #{invalid_count} invalid #{is_plural ? 'records' : 'record'} out of #{total_records} total #{is_plural ? 'records' : 'record'}."
|
30
|
-
end
|
27
|
+
Rails.logger.debug get_summary(total_records, invalid_count)
|
31
28
|
end
|
32
29
|
|
33
30
|
@reporter.generate_report
|
34
31
|
end
|
35
32
|
|
33
|
+
def get_summary(records_count, invalid_count)
|
34
|
+
is_plural = invalid_count > 1
|
35
|
+
records_word = is_plural ? "records" : "record"
|
36
|
+
first_part = "\nFound #{invalid_count} invalid #{records_word} out of #{records_count} total #{records_word}."
|
37
|
+
second_part = "\nValidation failed! Some records are invalid." if invalid_count.positive?
|
38
|
+
|
39
|
+
"#{first_part} #{second_part}"
|
40
|
+
end
|
41
|
+
|
36
42
|
def validate_test_model(model_name)
|
37
43
|
model = model_name.constantize
|
38
44
|
scope = model.all
|
@@ -97,25 +103,31 @@ module DbValidator
|
|
97
103
|
end
|
98
104
|
|
99
105
|
def validate_model(model)
|
100
|
-
|
101
|
-
batch_size = config.batch_size || 100
|
102
|
-
limit = config.limit
|
103
|
-
|
104
|
-
scope = model.all
|
105
|
-
scope = scope.limit(limit) if limit
|
106
|
-
|
106
|
+
scope = build_scope(model)
|
107
107
|
total_count = scope.count
|
108
108
|
return 0 if total_count.zero?
|
109
109
|
|
110
|
+
process_records(scope, model, total_count)
|
111
|
+
end
|
112
|
+
|
113
|
+
def build_scope(model)
|
114
|
+
scope = model.all
|
115
|
+
scope = scope.limit(DbValidator.configuration.limit) if DbValidator.configuration.limit
|
116
|
+
scope
|
117
|
+
end
|
118
|
+
|
119
|
+
def process_records(scope, model, total_count)
|
110
120
|
progress_bar = create_progress_bar(model.name, total_count)
|
121
|
+
process_batches(scope, progress_bar, model)
|
122
|
+
end
|
123
|
+
|
124
|
+
def process_batches(scope, progress_bar, model)
|
111
125
|
invalid_count = 0
|
126
|
+
batch_size = DbValidator.configuration.batch_size || 100
|
112
127
|
|
113
128
|
begin
|
114
129
|
scope.find_in_batches(batch_size: batch_size) do |batch|
|
115
|
-
batch
|
116
|
-
invalid_count += 1 unless validate_record(record)
|
117
|
-
progress_bar.increment
|
118
|
-
end
|
130
|
+
invalid_count += process_batch(batch, progress_bar)
|
119
131
|
end
|
120
132
|
rescue StandardError => e
|
121
133
|
Rails.logger.debug { "Error validating #{model.name}: #{e.message}" }
|
@@ -124,6 +136,15 @@ module DbValidator
|
|
124
136
|
invalid_count
|
125
137
|
end
|
126
138
|
|
139
|
+
def process_batch(batch, progress_bar)
|
140
|
+
invalid_count = 0
|
141
|
+
batch.each do |record|
|
142
|
+
invalid_count += 1 unless validate_record(record)
|
143
|
+
progress_bar.increment
|
144
|
+
end
|
145
|
+
invalid_count
|
146
|
+
end
|
147
|
+
|
127
148
|
def create_progress_bar(model_name, total)
|
128
149
|
ProgressBar.create(
|
129
150
|
title: "Validating #{model_name}",
|
@@ -140,7 +161,7 @@ module DbValidator
|
|
140
161
|
false
|
141
162
|
end
|
142
163
|
|
143
|
-
def
|
164
|
+
def models_to_validate
|
144
165
|
models = find_all_models
|
145
166
|
models.select { |model| should_validate_model?(model) }
|
146
167
|
end
|
data/lib/db_validator/version.rb
CHANGED
data/lib/db_validator.rb
CHANGED
@@ -6,6 +6,9 @@ require "db_validator/configuration"
|
|
6
6
|
require "db_validator/validator"
|
7
7
|
require "db_validator/reporter"
|
8
8
|
require "db_validator/cli"
|
9
|
+
require "db_validator/config_updater"
|
10
|
+
require "db_validator/test_task"
|
11
|
+
require "db_validator/validate_task"
|
9
12
|
|
10
13
|
module DbValidator
|
11
14
|
class Error < StandardError; end
|
@@ -3,93 +3,23 @@
|
|
3
3
|
namespace :db_validator do
|
4
4
|
desc "Validate records in the database"
|
5
5
|
task validate: :environment do
|
6
|
-
|
7
|
-
|
8
|
-
has_any_args = ENV["models"].present? || ENV["limit"].present? || ENV["format"].present? || ENV["show_records"].present?
|
9
|
-
|
10
|
-
if has_any_args
|
11
|
-
if ENV["models"].present?
|
12
|
-
models = ENV["models"].split(",").map(&:strip).map(&:classify)
|
13
|
-
DbValidator.configuration.only_models = models
|
14
|
-
end
|
15
|
-
|
16
|
-
DbValidator.configuration.limit = ENV["limit"].to_i if ENV["limit"].present?
|
17
|
-
DbValidator.configuration.report_format = ENV["format"].to_sym if ENV["format"].present?
|
18
|
-
DbValidator.configuration.show_records = ENV["show_records"] != "false" if ENV["show_records"].present?
|
19
|
-
else
|
20
|
-
cli.display_progress("Loading models") do
|
21
|
-
Rails.application.eager_load!
|
22
|
-
end
|
23
|
-
|
24
|
-
available_models = ActiveRecord::Base.descendants
|
25
|
-
.reject(&:abstract_class?)
|
26
|
-
.select(&:table_exists?)
|
27
|
-
.map(&:name)
|
28
|
-
.sort
|
29
|
-
|
30
|
-
selected_models = cli.select_models(available_models)
|
31
|
-
options = cli.configure_options
|
32
|
-
|
33
|
-
DbValidator.configuration.only_models = selected_models
|
34
|
-
DbValidator.configuration.limit = options[:limit] if options[:limit].present?
|
35
|
-
DbValidator.configuration.batch_size = options[:batch_size] if options[:batch_size].present?
|
36
|
-
DbValidator.configuration.report_format = options[:format].to_sym if options[:format].present?
|
37
|
-
DbValidator.configuration.show_records = options[:show_records] if options[:show_records].present?
|
38
|
-
end
|
39
|
-
|
40
|
-
validator = DbValidator::Validator.new
|
41
|
-
report = validator.validate_all
|
42
|
-
puts "\n#{report}"
|
6
|
+
DbValidator::ValidateTask.new.execute
|
43
7
|
end
|
44
8
|
|
45
9
|
desc "Test validation rules on existing records"
|
46
10
|
task test: :environment do
|
47
11
|
unless ENV["model"] && ENV["rule"]
|
48
12
|
puts "Usage: rake db_validator:test model=user rule='validates :field, presence: true' [show_records=false] [limit=1000] [format=json]"
|
49
|
-
|
13
|
+
raise "No models found in the application. Please run this command from your Rails application root."
|
50
14
|
end
|
51
15
|
|
52
16
|
model_name = ENV.fetch("model").classify
|
53
17
|
validation_rule = ENV.fetch("rule", nil)
|
54
18
|
|
55
|
-
# Configure options
|
56
19
|
DbValidator.configuration.show_records = ENV["show_records"] != "false" if ENV["show_records"].present?
|
57
20
|
DbValidator.configuration.limit = ENV["limit"].to_i if ENV["limit"].present?
|
58
21
|
DbValidator.configuration.report_format = ENV["format"].to_sym if ENV["format"].present?
|
59
22
|
|
60
|
-
|
61
|
-
base_model = model_name.constantize
|
62
|
-
# Extract attribute name from validation rule
|
63
|
-
attribute_match = validation_rule.match(/validates\s+:(\w+)/)
|
64
|
-
if attribute_match
|
65
|
-
attribute_name = attribute_match[1]
|
66
|
-
unless base_model.column_names.include?(attribute_name) || base_model.method_defined?(attribute_name)
|
67
|
-
puts "\n❌ Error: Attribute '#{attribute_name}' does not exist for model '#{model_name}'"
|
68
|
-
puts "Available columns: #{base_model.column_names.join(', ')}"
|
69
|
-
exit 1
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# Create temporary subclass with new validation
|
74
|
-
temp_model = Class.new(base_model) do
|
75
|
-
self.table_name = base_model.table_name
|
76
|
-
class_eval(validation_rule)
|
77
|
-
end
|
78
|
-
|
79
|
-
Object.const_set("Temporary#{model_name}", temp_model)
|
80
|
-
|
81
|
-
validator = DbValidator::Validator.new
|
82
|
-
report = validator.validate_test_model("Temporary#{model_name}")
|
83
|
-
puts "\n#{report}"
|
84
|
-
rescue NameError
|
85
|
-
puts "\n❌ Error: Model '#{model_name}' not found"
|
86
|
-
exit 1
|
87
|
-
rescue SyntaxError => e
|
88
|
-
puts "\n❌ Error: Invalid validation rule syntax"
|
89
|
-
puts e.message
|
90
|
-
exit 1
|
91
|
-
ensure
|
92
|
-
Object.send(:remove_const, "Temporary#{model_name}") if Object.const_defined?("Temporary#{model_name}")
|
93
|
-
end
|
23
|
+
DbValidator::TestTask.new(model_name, validation_rule).execute
|
94
24
|
end
|
95
25
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: db_validator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Krzysztof Duda
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -136,10 +136,14 @@ files:
|
|
136
136
|
- config/initializers/db_validator.rb
|
137
137
|
- lib/db_validator.rb
|
138
138
|
- lib/db_validator/cli.rb
|
139
|
+
- lib/db_validator/config_updater.rb
|
139
140
|
- lib/db_validator/configuration.rb
|
140
141
|
- lib/db_validator/formatters/json_formatter.rb
|
142
|
+
- lib/db_validator/formatters/message_formatter.rb
|
141
143
|
- lib/db_validator/railtie.rb
|
142
144
|
- lib/db_validator/reporter.rb
|
145
|
+
- lib/db_validator/test_task.rb
|
146
|
+
- lib/db_validator/validate_task.rb
|
143
147
|
- lib/db_validator/validator.rb
|
144
148
|
- lib/db_validator/version.rb
|
145
149
|
- lib/generators/db_validator/install_generator.rb
|