Merge pull request #4790 from consul/custom_validations

Make it easier to customize validations
This commit is contained in:
Javi Martín
2022-03-24 19:49:40 +01:00
committed by GitHub
4 changed files with 114 additions and 0 deletions

View File

@@ -1,5 +1,6 @@
class ApplicationRecord < ActiveRecord::Base
include HumanName
include SkipValidation
self.abstract_class = true
def self.sample(count = 1)

View File

@@ -0,0 +1,26 @@
module SkipValidation
extend ActiveSupport::Concern
module ClassMethods
def skip_validation(field, validator)
validator_class = if validator.is_a?(Class)
validator
else
"ActiveModel::Validations::#{validator.to_s.camelize}Validator".constantize
end
_validators[field].reject! { |existing_validator| existing_validator.is_a?(validator_class) }
_validate_callbacks.each do |callback|
if callback.raw_filter.is_a?(validator_class)
callback.raw_filter.instance_variable_set("@attributes", callback.raw_filter.attributes - [field])
end
end
end
def skip_translation_validation(field, validator)
skip_validation(field, validator)
translation_class.skip_validation(field, validator)
end
end
end

View File

@@ -9,6 +9,10 @@ module Globalize
end
end
end
class Translation
include SkipValidation
end
end
end

View File

@@ -0,0 +1,83 @@
require "rails_helper"
describe SkipValidation do
describe ".skip_validation" do
before do
dummy_model = Class.new do
include ActiveModel::Model
include SkipValidation
attr_accessor :title, :description
validates :title, presence: true, length: { in: 10..60, allow_nil: true }
validates :description, presence: true
end
stub_const("DummyModel", dummy_model)
end
it "accepts validator classes as parameters" do
DummyModel.skip_validation :title, ActiveModel::Validations::PresenceValidator
expect(DummyModel.new(title: nil, description: "Something")).to be_valid
end
it "accepts symbols as parameters" do
DummyModel.skip_validation :title, :presence
expect(DummyModel.new(title: nil, description: "Something")).to be_valid
end
it "does not affect other attributes" do
DummyModel.skip_validation :title, :presence
expect(DummyModel.new(title: nil, description: nil)).not_to be_valid
end
it "does not affect other validations" do
DummyModel.skip_validation :title, :presence
expect(DummyModel.new(title: "Short", description: "Something")).not_to be_valid
end
it "works with validators other than presence" do
DummyModel.skip_validation :title, :length
expect(DummyModel.new(title: "Short", description: "Something")).to be_valid
expect(DummyModel.new(title: nil, description: "Something")).not_to be_valid
end
end
describe ".skip_translation_validation" do
before do
dummy_banner = Class.new(Banner) do
def self.translation_class
@translation_class ||= Class.new(Banner::Translation) { clear_validators! }
end
reflect_on_association(:translations).options[:class_name] = "DummyBanner::Translation"
clear_validators!
validates_translation :title, presence: true
validates_translation :description, presence: true
end
stub_const("DummyBanner", dummy_banner)
stub_const("DummyBanner::Translation", dummy_banner.translation_class)
end
it "removes the validation from the translatable attribute" do
DummyBanner.skip_translation_validation :title, :presence
custom_banner = DummyBanner.new(build(:banner).attributes.merge(title: nil))
expect { custom_banner.save! }.not_to raise_exception
end
it "does not affect other validations" do
DummyBanner.skip_translation_validation :title, :presence
custom_banner = DummyBanner.new(build(:banner).attributes.merge(description: nil))
expect { custom_banner.save! }.to raise_exception(ActiveRecord::RecordInvalid)
end
end
end