Module: ActiveModel::Validations::ClassMethods
| Relationships & Source Files | |
| Defined in: | activemodel/lib/active_model/validations.rb, activemodel/lib/active_model/validations/validates.rb, activemodel/lib/active_model/validations/with.rb |
Constant Summary
-
VALID_OPTIONS_FOR_VALIDATE =
Internal use only
# File 'activemodel/lib/active_model/validations.rb', line 110[:on, :if, :unless, :prepend, :except_on].freeze
Instance Method Summary
-
#attribute_method?(attribute) ⇒ Boolean
Returns
trueifattributeis an attribute method,falseotherwise. -
#clear_validators!
Clears all of the validators and validations.
-
#validate(*args, &block)
Adds a validation method or block to the class.
-
#validates(*attributes)
This method is a shortcut to all default validators and any custom validator classes ending in 'Validator'.
-
#validates!(*attributes)
This method is used to define validations that cannot be corrected by end users and are considered exceptional.
-
#validates_each(*attr_names, &block)
Validates each attribute against a block.
-
#validates_with(*args, &block)
Passes the record off to the class or classes specified and allows them to add errors based on more complex conditions.
-
#validators
List all validators that are being used to validate the model using #validates_with method.
-
#validators_on(*attributes)
List all validators that are being used to validate a specific attribute.
- #_merge_validates_options(defaults, validator_options) private
- #_parse_validates_options(options) private
-
#_validates_default_keys
private
When creating custom validators, it might be useful to be able to specify additional default keys.
- #predicate_for_validation_context(context) private
-
#inherited(base)
Internal use only
Copy validators on inheritance.
Instance Method Details
#_merge_validates_options(defaults, validator_options) (private)
[ GitHub ]#_parse_validates_options(options) (private)
[ GitHub ]#_validates_default_keys (private)
When creating custom validators, it might be useful to be able to specify additional default keys. This can be done by overwriting this method.
# File 'activemodel/lib/active_model/validations/validates.rb', line 196
def _validates_default_keys [:if, :unless, :on, :allow_blank, :allow_nil, :strict, :except_on] end
#attribute_method?(attribute) ⇒ Boolean
Returns true if attribute is an attribute method, false otherwise.
class Person
include ActiveModel::Validations
attr_accessor :name
end
{Person.attribute_method?}(:name) # => true
{Person.attribute_method?}(:age) # => false
# File 'activemodel/lib/active_model/validations.rb', line 324
def attribute_method?(attribute) method_defined?(attribute) end
#clear_validators!
Clears all of the validators and validations.
Note that this will clear anything that is being used to validate the model for both the #validates_with and #validate methods. It clears the validators that are created with an invocation of #validates_with and the callbacks that are set by an invocation of #validate.
class Person
include ActiveModel::Validations
validates_with MyValidator
validates_with OtherValidator, on: :create
validates_with StrictValidator, strict: true
validate :cannot_be_robot
def cannot_be_robot
errors.add(:base, 'A person cannot be a robot') if person_is_robot
end
end
{Person.validators}
#### => [
#### {#<}MyValidator:0x007fbff403e808 @options={}>,
#### {#<}OtherValidator:0x007fbff403d930 @options={on: :create}>,
#### {#<}StrictValidator:0x007fbff3204a30 @options={strict:true}>
#### ]
If one runs Person.clear_validators! and then checks to see what
validators this class has, you would obtain:
{Person.validators} # => []
Also, the callback set by validate :cannot_be_robot will be erased
so that:
{Person._validate_callbacks}.empty? # => true
# File 'activemodel/lib/active_model/validations.rb', line 284
def clear_validators! reset_callbacks(:validate) _validators.clear end
#inherited(base)
Copy validators on inheritance.
# File 'activemodel/lib/active_model/validations.rb', line 329
def inherited(base) # :nodoc: dup = _validators.dup base._validators = dup.each { |k, v| dup[k] = v.dup } super end
#predicate_for_validation_context(context) (private)
[ GitHub ]# File 'activemodel/lib/active_model/validations.rb', line 338
def predicate_for_validation_context(context) context = context.is_a?(Array) ? context.sort : Array(context) @@predicates_for_validation_contexts[context] ||= -> (model) do if model.validation_context.is_a?(Array) model.validation_context.any? { |model_context| context.include?(model_context) } else context.include?(model.validation_context) end end end
#validate(*args, &block)
Adds a validation method or block to the class. This is useful when
overriding the validate instance method becomes too unwieldy and
you're looking for more descriptive declaration of your validations.
This can be done with a symbol pointing to a method:
class Comment
include ActiveModel::Validations
validate :must_be_friends
def must_be_friends
errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
end
end
With a block which is passed with the current record to be validated:
class Comment
include ActiveModel::Validations
validate do |comment|
comment.must_be_friends
end
def must_be_friends
errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
end
end
Or with a block where self points to the current record to be validated:
class Comment
include ActiveModel::Validations
validate do
errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
end
end
Note that the return value of validation methods is not relevant. It's not possible to halt the validate callback chain.
Options
:on- Specifies the contexts where this validation is active. Runs in all validation contexts by defaultnil. You can pass a symbol or an array of symbols. (e.g.on: :createoron: :custom_validation_contextoron: [:create, :custom_validation_context]):except_on- Specifies the contexts where this validation is not active. Runs in all validation contexts by defaultnil. You can pass a symbol or an array of symbols. (e.g.except_on: :createorexcept_on: :custom_validation_contextorexcept_on: [:create, :custom_validation_context]):if- Specifies a method or proc to call to determine if the validation should occur (e.g.if: :allow_validation, orif: Proc.new { |user| user.signup_step > 2 }). The method or proc should return or evaluate to atrueorfalsevalue. Multiple methods or procs can be passed as an array to check multiple conditions, all of which must pass for validation to occur (e.g.if: [:allow_validation, Proc.new { |user| user.signup_step > 2 }]):unless- Specifies a method or proc to call to determine if the validation should not occur (e.g.unless: :skip_validation, orunless: Proc.new { |user| user.signup_step <= 2 }). The method or proc should return or evaluate to atrueorfalsevalue. Multiple methods or procs can be passed as an array to check multiple conditions, all of which must pass for validation not to occur (e.g.unless: [:skip_validation, Proc.new { |user| user.signup_step <= 2 }])
NOTE: Calling validate multiple times on the same method will overwrite previous definitions.
# File 'activemodel/lib/active_model/validations.rb', line 190
def validate(*args, &block) = args. if args.all?(Symbol) .each_key do |k| unless VALID_OPTIONS_FOR_VALIDATE.include?(k) raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{VALID_OPTIONS_FOR_VALIDATE.map(&:inspect).join(', ')}. Perhaps you meant to call `validates` instead of `validate`?") end end end if .key?(:on) = .merge(if: [predicate_for_validation_context([:on]), *[:if]]) end if .key?(:except_on) = .dup [:except_on] = Array([:except_on]) [:unless] = [ ->(o) { [:except_on].intersect?(Array(o.validation_context)) }, *[:unless] ] end set_callback(:validate, *args, , &block) end
#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 :username, absence: true
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, ([:] || "is not an email") unless
/\A([^@\s])@((?:[-a-z0-9]\.)+[a-z]{2,})\z/i.match?(value)
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 /\Athe/i.match?(value)
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: %w(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 defaultnil. You can pass a symbol or an array of symbols. (e.g.on: :createoron: :custom_validation_contextoron: [:create, :custom_validation_context]):except_on- Specifies the contexts where this validation is not active. Runs in all validation contexts by defaultnil. You can pass a symbol or an array of symbols. (e.g.except_on: :createorexcept_on: :custom_validation_contextorexcept_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, orif: Proc.new { |user| user.signup_step > 2 }). The method, proc or string should return or evaluate to atrueorfalsevalue. Multiple methods or procs can be passed as an array to check multiple conditions, all of which must pass for validation to occur (e.g.if: [:allow_validation, Proc.new { |user| user.signup_step > 2 }]):unless- Specifies a method, proc, or string to call to determine if the validation should not occur (e.g.unless: :skip_validation, orunless: Proc.new { |user| user.signup_step <= 2 }). The method, proc, or string should return or evaluate to atrueorfalsevalue. Multiple methods or procs can be passed as an array to check multiple conditions, all of which must pass for validation not to occur (e.g.unless: [:skip_validation, Proc.new { |user| user.signup_step <= 2 }]):allow_nil- Skip validation if the attribute isnil.:allow_blank- Skip validation if the attribute is blank.:strict- If the:strictoption is set to true will raise ActiveModel::StrictValidationFailed instead of adding the error.:strictoption can also be set to any other exception.
Example:
validates :password, presence: true, confirmation: true, if: :password_required?
validates :token, length: { is: 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
When :if, :unless, or :on appear at both the validates level and
inside a specific validator's options, they are combined rather than the
inner option replacing the outer one. All :if conditions must pass,
any :unless condition will skip validation, and :on contexts are merged:
validates :password, presence: { if: :local_check? }, if: :global_check?
#### Equivalent to: validates_presence_of :password, if: [:global_check?, :local_check?]
Other per-validator options (:allow_blank, :allow_nil, :strict,
:message) override the same option given at the validates level.
# File 'activemodel/lib/active_model/validations/validates.rb', line 143
def validates(*attributes) defaults = attributes..dup validations = defaults.slice!(*_validates_default_keys) 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? defaults[:attributes] = attributes validations.each do |key, | key = "#{key.to_s.camelize}Validator" begin validator = const_get(key) rescue NameError raise ArgumentError, "Unknown validator: '#{key}'" end next unless validates_with(validator, (defaults, ())) end end
#validates!(*attributes)
This method is used to define validations that cannot be corrected by end
users and are considered exceptional. So each validator defined with bang
or :strict option set to true will always raise
::ActiveModel::StrictValidationFailed instead of adding error
when validation fails. See #validates for more information about
the validation itself.
class Person
include ActiveModel::Validations
attr_accessor :name
validates! :name, presence: true
end
person = {Person.new}
person.name = ''
person.valid?
#### => {ActiveModel::StrictValidationFailed}: Name can't be blank
# File 'activemodel/lib/active_model/validations/validates.rb', line 187
def validates!(*attributes) = attributes. [:strict] = true validates(*(attributes << )) end
#validates_each(*attr_names, &block)
Validates each attribute against a block.
class Person
include ActiveModel::Validations
attr_accessor :first_name, :last_name
validates_each :first_name, :last_name, allow_blank: true do |record, attr, value|
record.errors.add attr, "starts with z." if value.start_with?("z")
end
end
Options
:on- Specifies the contexts where this validation is active. Runs in all validation contexts by defaultnil. You can pass a symbol or an array of symbols. (e.g.on: :createoron: :custom_validation_contextoron: [:create, :custom_validation_context]):except_on- Specifies the contexts where this validation is not active. Runs in all validation contexts by defaultnil. You can pass a symbol or an array of symbols. (e.g.except_on: :createorexcept_on: :custom_validation_contextorexcept_on: [:create, :custom_validation_context]):allow_nil- Specify a method, proc, or boolean, to skip validation if attribute isnil(e.g.allow_nil: true, orallow_nil: Proc.new { |user| user.signup_step > 2 }).:allow_blank- Specify a method, proc, or boolean, to skip validation if attribute isblank(e.g.allow_blank: true, orallow_blank: Proc.new { |user| user.signup_step > 2 }).:if- Specifies a method, proc, or string to call to determine if the validation should occur (e.g.if: :allow_validation, orif: Proc.new { |user| user.signup_step > 2 }). The method, proc or string should return or evaluate to atrueorfalsevalue. Multiple methods or procs can be passed as an array to check multiple conditions, all of which must pass for validation to occur (e.g.if: [:allow_validation, Proc.new { |user| user.signup_step > 2 }]):unless- Specifies a method, proc, or string to call to determine if the validation should not occur (e.g.unless: :skip_validation, orunless: Proc.new { |user| user.signup_step <= 2 }). The method, proc, or string should return or evaluate to atrueorfalsevalue. Multiple methods or procs can be passed as an array to check multiple conditions, all of which must pass for validation not to occur (e.g.unless: [:skip_validation, Proc.new { |user| user.signup_step <= 2 }])
# File 'activemodel/lib/active_model/validations.rb', line 106
def validates_each(*attr_names, &block) validates_with BlockValidator, _merge_attributes(attr_names), &block end
#validates_with(*args, &block)
Passes the record off to the class or classes specified and allows them to add errors based on more complex conditions.
class Person
include ActiveModel::Validations
validates_with MyValidator
end
class MyValidator < {ActiveModel::Validator}
def validate(record)
if some_complex_logic
record.errors.add :base, 'This record is invalid'
end
end
private
def some_complex_logic
# ...
end
end
You may also pass it multiple classes, like so:
class Person
include ActiveModel::Validations
validates_with MyValidator, MyOtherValidator, on: :create
end
There is no default error message for validates_with. You must
manually add errors to the record's errors collection in the validator
class.
To implement the validate method, you must have a record parameter
defined, which is the record to be validated.
Configuration options:
:on- Specifies the contexts where this validation is active. Runs in all validation contexts by defaultnil. You can pass a symbol or an array of symbols. (e.g.on: :createoron: :custom_validation_contextoron: [: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, orif: Proc.new { |user| user.signup_step > 2 }). The method, proc, or string should return or evaluate to atrueorfalsevalue. Multiple methods or procs can be passed as an array to check multiple conditions, all of which must pass for validation to occur (e.g.if: [:allow_validation, Proc.new { |user| user.signup_step > 2 }]):unless- Specifies a method, proc, or string to call to determine if the validation should not occur (e.g.unless: :skip_validation, orunless: Proc.new { |user| user.signup_step <= 2 }). The method, proc, or string should return or evaluate to atrueorfalsevalue. Multiple methods or procs can be passed as an array to check multiple conditions, all of which must pass for validation not to occur (e.g.unless: [:skip_validation, Proc.new { |user| user.signup_step <= 2 }]):strict- Specifies whether validation should be strict. SeeActiveModel::Validations#validates!for more information.
If you pass any additional configuration options, they will be passed
to the class and available as options:
class Person
include ActiveModel::Validations
validates_with MyValidator, my_custom_key: 'my custom value'
end
class MyValidator < {ActiveModel::Validator}
def validate(record)
[:my_custom_key] # => "my custom value"
end
end
# File 'activemodel/lib/active_model/validations/with.rb', line 100
def validates_with(*args, &block) = args. [:class] = self args.each do |klass| validator = klass.new(.dup, &block) if validator.respond_to?(:attributes) && !validator.attributes.empty? validator.attributes.each do |attribute| _validators[attribute.to_sym] << validator end else _validators[nil] << validator end validate(validator, ) end end
#validators
List all validators that are being used to validate the model using #validates_with method.
class Person
include ActiveModel::Validations
validates_with MyValidator
validates_with OtherValidator, on: :create
validates_with StrictValidator, strict: true
end
{Person.validators}
#### => [
#### {#<}MyValidator:0x007fbff403e808 @options={}>,
#### {#<}OtherValidator:0x007fbff403d930 @options={on: :create}>,
#### {#<}StrictValidator:0x007fbff3204a30 @options={strict:true}>
#### ]
# File 'activemodel/lib/active_model/validations.rb', line 236
def validators _validators.values.flatten.uniq end
#validators_on(*attributes)
List all validators that are being used to validate a specific attribute.
class Person
include ActiveModel::Validations
attr_accessor :name, :age
validates_presence_of :name
validates_inclusion_of :age, in: 0..99
end
{Person.validators_on}(:name)
#### => [
#### {#<}ActiveModel::Validations::PresenceValidator:0x007fe604914e60 @attributes=[:name], @options={}>,
#### ]
# File 'activemodel/lib/active_model/validations.rb', line 306
def validators_on(*attributes) attributes.flat_map do |attribute| _validators[attribute.to_sym] end end