Last active
January 27, 2021 18:37
-
-
Save grodowski/5f828c2ae77f401f817d2c0c3f85be9b to your computer and use it in GitHub Desktop.
Codegen script to extract explicit ActiveModel validators from SchemaValidations::ActiveRecord
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
namespace :validations do | |
desc 'List all model validations defined by schema_validations' | |
task list: :environment do | |
LIST_OUTPUT = STDOUT | |
VALIDATIONS = Hash.new | |
# monkey patch SchemaValidations to extract validation metadata instead of applying them | |
# gem source: lib/schema_validations/active_record/validations.rb | |
SchemaValidations::ActiveRecord::Base::ClassMethods.module_eval do | |
# disables real validation patching | |
alias_method :_real_validate_logged, :validate_logged | |
def validate_logged(method, arg, opts={}) | |
opts = opts.dup | |
# respect SchemaValidations filtering | |
if _filter_validation(method, arg) | |
if opts[:if] | |
opts[:if] = "#{arg}_changed?".to_sym | |
end | |
# print out validation type, column and options | |
validation = "#{method} #{arg.inspect}" | |
validation += ", #{opts.inspect[1...-1]}" if opts.any? | |
validation += "\n" | |
LIST_OUTPUT.puts(validation) | |
VALIDATIONS[self] << validation | |
end | |
end | |
end | |
Rails.application.eager_load! | |
ActiveRecord::Base.descendants.each_with_index do |cls, idx| | |
LIST_OUTPUT.puts "#{idx}: #{cls}" | |
VALIDATIONS[cls] = [] | |
cls.send(:load_schema_validations) | |
end | |
end | |
desc 'Update all models with exported validations' | |
task load: :list do | |
puts 'Applying listed validators within app/models...' | |
dir = Dir.glob('app/models/**/*.rb').reject { |path| path =~ %r{/entities/} } | |
VALIDATIONS.each do |cls, validators| | |
if validators.empty? | |
puts "Skipping #{cls}. No validators" | |
next | |
end | |
model_paths = dir.select { |path| path =~ %r{/#{cls.name.underscore}.rb$} } | |
model_path = case cls.name | |
when 'User' | |
'app/models/user.rb' | |
when 'Admin::User' | |
'app/models/admin/user.rb' | |
else | |
raise "Ambiguous path #{cls}: #{model_paths}" if model_paths.size > 1 | |
model_paths[0] | |
end | |
unless model_path | |
puts "Could not find #{cls.name} in app/models/" | |
next | |
end | |
source_lines = File.read(model_path).lines | |
if source_lines.find { |ln| ln =~ /# codegen schema validations begin/ } | |
puts "Skipping #{cls.name}. Already modified" | |
next | |
end | |
# try first validation line | |
start_line_idx = Enumerator.new(source_lines).with_index.find { |ln, _idx| ln =~ /validate/ }&.last | |
start_line_idx -= 1 if start_line_idx # prepend validations if any were found | |
# try class def line | |
unless start_line_idx | |
start_line_idx = Enumerator.new(source_lines).with_index.find { |ln, _idx| ln =~ /class[\s:\w]+/ }.last | |
end | |
validators.unshift("# codegen schema validations begin\n") | |
validators.append("# codegen schema validations end\n") | |
File.open(model_path, 'w') do |f| | |
f.write(source_lines[0..start_line_idx].join) | |
f.write(validators.map { |ln| " #{ln}" }.join) | |
f.write(source_lines[start_line_idx+1..-1].join) | |
end | |
puts "#{cls.name} #{model_path}: applied #{validators.size} validations" | |
end | |
puts 'Done!' | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment