validates
validates(*attributes)This method is a shortcut to all default validators and any custom validator classes ending in ‘Validator’. Note that Rails default validators can be overridden inside specific classes by creating custom validator classes in their place such as PresenceValidator.
Examples of using the default rails validators:
validates :terms, acceptance: true validates :password, confirmation: true validates :username, exclusion: { in: %w(admin superuser) } validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create } validates :age, inclusion: { in: 0..9 } validates :first_name, length: { maximum: 30 } validates :age, numericality: true validates :username, presence: true
The power of the validates method comes when using custom validators and default validators in one call for a given attribute.
class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) record.errors.add attribute, (options[:message] || "is not an email") unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i end end class Person include ActiveModel::Validations attr_accessor :name, :email validates :name, presence: true, length: { maximum: 100 } validates :email, presence: true, email: true end
Validator classes may also exist within the class being validated allowing custom modules of validators to be included as needed.
class Film include ActiveModel::Validations class TitleValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) record.errors.add attribute, "must start with 'the'" unless value =~ /\Athe/i end end validates :name, title: true end
Additionally validator classes may be in another namespace and still used within any class.
validates :name, :'film/title' => true
The validators hash can also handle regular expressions, ranges, arrays and strings in shortcut form.
validates :email, format: /@/ validates :role, inclusion: %(admin contributor) validates :password, length: 6..20
When using shortcut form, ranges and arrays are passed to your validator’s initializer as options[:in] while other types including regular expressions and strings are passed as options[:with].
There is also a list of options that could be used along with validators:
-
:on - Specifies the contexts where this validation is active. Runs in all validation contexts by default nil. You can pass a symbol or an array of symbols. (e.g. on: :create or on: :custom_validation_context or on: [:create, :custom_validation_context])
-
:if - Specifies a method, proc or string to call to determine if the validation should occur (e.g. if: :allow_validation, or if: Proc.new { |user| user.signup_step > 2 }). The method, proc or string should return or evaluate to a true or false value.
-
:unless - Specifies a method, proc or string to call to determine if the validation should not occur (e.g. unless: :skip_validation, or unless: Proc.new { |user| user.signup_step <= 2 }). The method, proc or string should return or evaluate to a true or false value.
-
:allow_nil - Skip validation if the attribute is nil.
-
:allow_blank - Skip validation if the attribute is blank.
-
:strict - If the :strict option is set to true will raise ActiveModel::StrictValidationFailed instead of adding the error. :strict option can also be set to any other exception.
Example:
validates :password, presence: true, confirmation: true, if: :password_required? validates :token, length: 24, strict: TokenLengthException
Finally, the options :if, :unless, :on, :allow_blank, :allow_nil, :strict and :message can be given to one specific validator, as a hash:
validates :password, presence: { if: :password_required?, message: 'is forgotten.' }, confirmation: true
9Notes
multiple attributes with the same validations
You can list multiple attributes if they share the same validations
validates :title, :body, :presence => true
sending the attributes as an array will return an error
validates [:title, :body], :presence => true
#=> ArgumentError: Attribute names must be symbols
case-insensitive uniqueness
For case-insensitive uniqueness:
validate :username, :uniqueness => {:case_sensitive => false}
uniqueness
You can scope uniqueness as well
validates :user_name, :presence => true, :uniqueness => {:scope => :account_id}
# the old way
validates_uniqueness_of :user_name, :scope => :account_id
Custom validator with i18n support
Here is modified EmailValidator from the example above:
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add(attribute, options[:message] || :email) unless
value =~ /\\A([^@\\s]+)@((?:[-a-z0-9]+\\.)+[a-z]{2,})\\z/i
end
end
And locale:
en:
activerecord:
errors:
messages:
email: "is not an email"
Changing the Message
For Change the default message:
==== Code example
validates :invoice_number, :presence => {:message => 'The invoice number must be informed.'}
validating a database column acceptance
+accept+ option should be set to +true+ if you are validating a database column, since the attribute is typecast from "1" to +true+ before validation
validates :terms,
acceptance: {
allow_nil: false,
accept: true
}
Common Validator options
Most validators will support all of the following common options: (Through ActiveModel::Errors::CALLBACK_OPTIONS (http://apidock.com/rails/ActiveModel/Errors))
- :if
- :unless
- :allow_blank
- :allow_nil
Example of conditions using
==== f.e. validates :number, :presence => { :if => :quota_file? }
def self.quota_file?
quota_file?
end
attributes that have the same names as options
For reasons that are beyond my comprehension, this piece of code
class Working
include ActiveModel::Validations
attr_accessor :format
validates :format, :presence => true, :format => { :with => /\\AWorking/ }
end
works (NOTE: it has an attribute that has the same name of an option), while this
class NotWorking < ActiveRecord::Base
validates :format, :presence => true, :format => { :with => /\\ANot Working/ }
end
does not (assuming that you have a legacy db in which you can't change the names of the columns). It throws an ArgumentError at you. However, a crude hack is to add an explicit accessor to the :format method, like this
class WorkingAgain < ActiveRecord::Base
validates :format, :presence => true, :format => { :with => /\\AWorking again/ }
def format
read_attribute(:format)
end
end
Any explanation is welcome.