In my User model, I have the usual suspects of email, first_name, last_name, password, etc.
I have several cases where I need to skip all or some of the validations.
Currently, I have an unless condition that looks something like:
validates :first_name, presence: :true, etc..., unless: :skip_first_name_validation?
validates :last_name, presence: :true, etc..., unless: :skip_last_name_validation?
validates :email, presence: :true, etc..., unless: :skip_email_validation?
Of course I also have:
attr_accessor :skip_first_name_validation, :skip_last_name_validation, etc.
And then I use private methods to check the state of each:
def skip_first_name_validation?
skip_first_name_validation
end
def skip_last_name_validation?
skip_last_name_validation
end
def skip_email_validation?
skip_email_validation
end
etc..
From there, whenever I need to skip validations, I just assign each one of these guys a true
value in my controller.
So while all of this works fine, I'm wondering if there's a more elegant way?
Ideally it would be nice if I could use a simple conditional like this for each attribute in my models:
:skip_validation?
And in my controllers, just do something like:
skip_validation(:first_name, :last_name, :password) = true
Can someone offer a suggestion for how I might program this? I'd prefer not using an existing gem/library, but am trying to understand how to program this behavior in rails. Thanks.
This could help you define all the setters and checkers dynamically
private
def self.skip_validations_for(*args)
# this block dynamically defines a setter method 'set_validations'
# e.g.
# set_validations(true, :email, :last_name, :first_name)
# set_validations(false, :email, :last_name, :first_name)
define_method('set_validations') do |value, *params|
params.each do |var|
self.send("skip_#{var.to_s}_validations=", value)
end
end
# this block walks through the passed in fields and defines a checker
# method `skip_[field_name]_validation?
args.each do |arg|
if self.method_defined? arg
send :define_method, "skip_#{attr.to_s}_validation?" do
send "skip_#{attr.to_s}_validation"
end
end
end
end
Then in your model regardless of whether this is native in the class or included through a module, you can add:
Class Foo < ActiveRecord::Base
validate :bar, :presence => true
validate :baz, :length_of => 10
skip_validations_for :bar, :baz
end
at which point you'll have access to
set_validations(true, :bar, :baz)
and
skip_bar_validation?
update:
Where would this code go?
This depends on how widely you'd like to use it. If you just want to override the validations in one model then you could put it all directly in that model. If however you wish to create a Module
that allows you to do this in every class then you'll have to create a Module
put it in the lib/
directory and the make sure it's in the load path. Perhaps this could help you with that.
Why did things change so drastically?
Originally I was using the _validations method on an instance and that is an ActiveRecord method that returns a hash of validations (field name -> ActiveRecord::Validation). That method of doing things automatically turned on the ability for every field. It's more Rails'ish to allow you to opt in the specific fields you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With