2010-01-17 03:57:16 -05:00
|
|
|
require 'active_support/core_ext/hash/slice'
|
|
|
|
|
2010-01-07 12:44:35 -05:00
|
|
|
module ActiveModel
|
|
|
|
module Validations
|
|
|
|
module ClassMethods
|
|
|
|
# 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.
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-01-07 12:44:35 -05:00
|
|
|
# Examples of using the default rails validators:
|
2010-01-08 02:37:58 -05:00
|
|
|
#
|
2012-07-29 21:08:42 -04:00
|
|
|
# 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
|
|
|
|
# validates :username, uniqueness: true
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-06-11 06:15:34 -04:00
|
|
|
# The power of the +validates+ method comes when using custom validators
|
2012-07-29 21:08:42 -04:00
|
|
|
# and default validators in one call for a given attribute.
|
2010-01-08 02:37:58 -05:00
|
|
|
#
|
2010-01-07 12:44:35 -05:00
|
|
|
# class EmailValidator < ActiveModel::EachValidator
|
|
|
|
# def validate_each(record, attribute, value)
|
2011-10-11 09:13:08 -04:00
|
|
|
# record.errors.add attribute, (options[:message] || "is not an email") unless
|
2010-08-17 18:15:01 -04:00
|
|
|
# value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
|
2010-01-07 12:44:35 -05:00
|
|
|
# end
|
|
|
|
# end
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-01-07 12:44:35 -05:00
|
|
|
# class Person
|
|
|
|
# include ActiveModel::Validations
|
|
|
|
# attr_accessor :name, :email
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2012-07-29 21:08:42 -04:00
|
|
|
# validates :name, presence: true, uniqueness: true, length: { maximum: 100 }
|
|
|
|
# validates :email, presence: true, email: true
|
2010-01-07 12:44:35 -05:00
|
|
|
# end
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-08-10 09:34:54 -04:00
|
|
|
# Validator classes may also exist within the class being validated
|
2012-07-29 21:08:42 -04:00
|
|
|
# allowing custom modules of validators to be included as needed.
|
2010-01-08 02:37:58 -05:00
|
|
|
#
|
|
|
|
# class Film
|
|
|
|
# include ActiveModel::Validations
|
|
|
|
#
|
2010-01-07 12:44:35 -05:00
|
|
|
# class TitleValidator < ActiveModel::EachValidator
|
|
|
|
# def validate_each(record, attribute, value)
|
2011-10-11 09:13:08 -04:00
|
|
|
# record.errors.add attribute, "must start with 'the'" unless value =~ /\Athe/i
|
2010-01-07 12:44:35 -05:00
|
|
|
# end
|
|
|
|
# end
|
2010-01-07 13:22:32 -05:00
|
|
|
#
|
2012-07-29 21:08:42 -04:00
|
|
|
# validates :name, title: true
|
2010-01-08 02:37:58 -05:00
|
|
|
# end
|
|
|
|
#
|
2012-07-29 21:08:42 -04:00
|
|
|
# Additionally validator classes may be in another namespace and still
|
|
|
|
# used within any class.
|
2010-12-09 13:30:02 -05:00
|
|
|
#
|
2011-11-10 12:49:37 -05:00
|
|
|
# validates :name, :'film/title' => true
|
2010-12-09 13:30:02 -05:00
|
|
|
#
|
2012-07-29 21:08:42 -04:00
|
|
|
# The validators hash can also handle regular expressions, ranges, arrays
|
|
|
|
# and strings in shortcut form.
|
2010-01-08 02:37:58 -05:00
|
|
|
#
|
2012-07-29 21:08:42 -04:00
|
|
|
# validates :email, format: /@/
|
|
|
|
# validates :gender, inclusion: %w(male female)
|
|
|
|
# validates :password, length: 6..20
|
2010-01-07 13:22:32 -05:00
|
|
|
#
|
2010-09-20 21:35:41 -04:00
|
|
|
# When using shortcut form, ranges and arrays are passed to your
|
2012-07-29 21:08:42 -04:00
|
|
|
# validator's initializer as <tt>options[:in]</tt> while other types
|
|
|
|
# including regular expressions and strings are passed as <tt>options[:with]</tt>.
|
2010-09-20 21:35:41 -04:00
|
|
|
#
|
2012-07-10 03:07:38 -04:00
|
|
|
# There is also a list of options that could be used along with validators:
|
2012-07-29 21:08:42 -04:00
|
|
|
#
|
2012-07-10 03:07:38 -04:00
|
|
|
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
|
|
|
|
# validation contexts by default (+nil+), other options are <tt>:create</tt>
|
|
|
|
# and <tt>:update</tt>.
|
|
|
|
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
|
|
|
|
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
|
|
|
|
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
|
|
|
|
# proc or string should return or evaluate to a +true+ or +false+ value.
|
|
|
|
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
|
|
|
|
# if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
|
|
|
|
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
|
|
|
# method, proc or string should return or evaluate to a +true+ or
|
|
|
|
# +false+ value.
|
2012-08-06 06:45:27 -04:00
|
|
|
# * <tt>:strict</tt> - if the <tt>:strict</tt> option is set to true
|
|
|
|
# will raise ActiveModel::StrictValidationFailed instead of adding the error.
|
|
|
|
# <tt>:strict</tt> option can also be set to any other exception.
|
2012-07-10 03:07:38 -04:00
|
|
|
#
|
|
|
|
# Example:
|
|
|
|
#
|
2012-07-29 21:08:42 -04:00
|
|
|
# validates :password, presence: true, confirmation: true, if: :password_required?
|
2012-08-06 06:45:27 -04:00
|
|
|
# validates :token, uniqueness: true, strict: TokenGenerationException
|
|
|
|
#
|
2010-01-07 13:22:32 -05:00
|
|
|
#
|
2012-09-07 06:56:23 -04:00
|
|
|
# Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+, +:strict+
|
|
|
|
# and +:message+ can be given to one specific validator, as a hash:
|
2010-01-07 13:22:32 -05:00
|
|
|
#
|
2012-09-07 06:56:23 -04:00
|
|
|
# validates :password, presence: { if: :password_required?, message: 'is forgotten.' }, confirmation: true
|
2010-01-07 12:44:35 -05:00
|
|
|
def validates(*attributes)
|
2012-01-17 05:18:58 -05:00
|
|
|
defaults = attributes.extract_options!.dup
|
2011-02-05 19:00:57 -05:00
|
|
|
validations = defaults.slice!(*_validates_default_keys)
|
2010-01-07 12:44:35 -05:00
|
|
|
|
|
|
|
raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
|
|
|
|
raise ArgumentError, "You need to supply at least one validation" if validations.empty?
|
2010-01-07 13:22:32 -05:00
|
|
|
|
2012-11-04 08:35:01 -05:00
|
|
|
defaults[:attributes] = attributes
|
2010-01-07 13:22:32 -05:00
|
|
|
|
2010-01-07 12:44:35 -05:00
|
|
|
validations.each do |key, options|
|
Don't enable validations when passing false hash values to ActiveModel.validates
Passing a falsey option value for a validator currently causes that validator to
be enabled, just like "true":
ActiveModel.validates :foo, :presence => false
This is rather counterintuitive, and makes it inconvenient to wrap `validates` in
methods which may conditionally enable different validators.
As an example, one is currently forced to write:
def has_slug(source_field, options={:unique => true})
slugger = Proc.new { |r| r[:slug] = self.class.sluggify(r[source_field]) if r[:slug].blank? }
before_validation slugger
validations = { :presence => true, :slug => true }
if options[:unique]
validations[:uniqueness] = true
end
validates :slug, validations
end
because the following reasonable-looking alternative fails to work as expected:
def has_slug(source_field, options={:unique => true})
slugger = Proc.new { |r| r[:slug] = self.class.sluggify(r[source_field]) if r[:slug].blank? }
before_validation slugger
validates :slug, :presence => true, :slug => true, :uniqueness => options[:unique]
end
(This commit includes a test, and all activemodel and activerecord tests pass as before.)
2012-05-28 09:39:09 -04:00
|
|
|
next unless options
|
2010-12-09 13:30:02 -05:00
|
|
|
key = "#{key.to_s.camelize}Validator"
|
|
|
|
|
2010-01-07 12:44:35 -05:00
|
|
|
begin
|
2010-12-09 13:30:02 -05:00
|
|
|
validator = key.include?('::') ? key.constantize : const_get(key)
|
2010-01-07 12:44:35 -05:00
|
|
|
rescue NameError
|
|
|
|
raise ArgumentError, "Unknown validator: '#{key}'"
|
|
|
|
end
|
|
|
|
|
2010-01-08 02:37:58 -05:00
|
|
|
validates_with(validator, defaults.merge(_parse_validates_options(options)))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-02-01 12:50:24 -05:00
|
|
|
# This method is used to define validations that cannot be corrected by end
|
|
|
|
# users and are considered exceptional. So each validator defined with bang
|
2012-02-01 08:28:31 -05:00
|
|
|
# or <tt>:strict</tt> option set to <tt>true</tt> will always raise
|
|
|
|
# <tt>ActiveModel::StrictValidationFailed</tt> instead of adding error
|
2012-07-29 21:08:42 -04:00
|
|
|
# when validation fails. See <tt>validates</tt> for more information about
|
|
|
|
# the validation itself.
|
|
|
|
#
|
|
|
|
# class Person
|
2012-10-25 21:41:35 -04:00
|
|
|
# include ActiveModel::Validations
|
2012-07-29 21:08:42 -04:00
|
|
|
#
|
|
|
|
# attr_accessor :name
|
|
|
|
# validates! :name, presence: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# person = Person.new
|
2012-10-25 21:41:35 -04:00
|
|
|
# person.name = ''
|
|
|
|
# person.valid?
|
2012-07-29 21:08:42 -04:00
|
|
|
# # => ActiveModel::StrictValidationFailed: Name can't be blank
|
2011-08-17 10:26:00 -04:00
|
|
|
def validates!(*attributes)
|
|
|
|
options = attributes.extract_options!
|
|
|
|
options[:strict] = true
|
|
|
|
validates(*(attributes << options))
|
|
|
|
end
|
|
|
|
|
2010-01-08 02:37:58 -05:00
|
|
|
protected
|
|
|
|
|
2011-02-05 19:00:57 -05:00
|
|
|
# When creating custom validators, it might be useful to be able to specify
|
|
|
|
# additional default keys. This can be done by overwriting this method.
|
2012-10-25 21:41:35 -04:00
|
|
|
def _validates_default_keys # :nodoc:
|
2012-02-01 08:28:31 -05:00
|
|
|
[:if, :unless, :on, :allow_blank, :allow_nil , :strict]
|
2011-02-05 19:00:57 -05:00
|
|
|
end
|
|
|
|
|
2012-10-25 21:41:35 -04:00
|
|
|
def _parse_validates_options(options) # :nodoc:
|
2010-01-08 02:37:58 -05:00
|
|
|
case options
|
|
|
|
when TrueClass
|
|
|
|
{}
|
|
|
|
when Hash
|
|
|
|
options
|
|
|
|
when Range, Array
|
|
|
|
{ :in => options }
|
2010-09-20 21:35:41 -04:00
|
|
|
else
|
|
|
|
{ :with => options }
|
2010-01-07 12:44:35 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2011-02-05 18:37:38 -05:00
|
|
|
end
|