Make it easier to customize validations
There are CONSUL installations where the validations CONSUL offers by
default don't make sense because they're using a different business
logic. Removing these validations in a custom model was hard, and that's
why in many cases modifying the original CONSUL models was an easier
solution.
Since modifying the original CONSUL models makes the code harder to
maintain, we're now providing a way to easily skip validations in a
custom model. For example, in order to skip the price presence
validation in the Budget::Heading model, we could write a model in
`app/models/custom/budget/heading.rb`:
```
require_dependency Rails.root.join("app", "models", "budget", "heading").to_s
class Budget::Heading
skip_validation :price, :presence
end
```
In order to skip validation on translatable attributes (defined with
`validates_translation`), we have to use the
`skip_translation_validation` method; for example, to skip the proposal
title presence validation:
```
require_dependency Rails.root.join("app", "models", "proposal").to_s
class Proposal
skip_translation_validation :title, :presence
end
```
Co-Authored-By: taitus <sebastia.roig@gmail.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
class ApplicationRecord < ActiveRecord::Base
|
class ApplicationRecord < ActiveRecord::Base
|
||||||
include HumanName
|
include HumanName
|
||||||
|
include SkipValidation
|
||||||
self.abstract_class = true
|
self.abstract_class = true
|
||||||
|
|
||||||
def self.sample(count = 1)
|
def self.sample(count = 1)
|
||||||
|
|||||||
26
app/models/concerns/skip_validation.rb
Normal file
26
app/models/concerns/skip_validation.rb
Normal 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
|
||||||
@@ -9,6 +9,10 @@ module Globalize
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Translation
|
||||||
|
include SkipValidation
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
83
spec/models/skip_validation_spec.rb
Normal file
83
spec/models/skip_validation_spec.rb
Normal 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
|
||||||
Reference in New Issue
Block a user