модуль ActiveModel::Validations::ClassMethods
Открытые методы экземпляра
# File activemodel/lib/active_model/validations.rb, line 284 def attribute_method?(attribute) method_defined?(attribute) end
Возвращает true, если attribute является методом атрибута, и false в противном случае.
class Person include ActiveModel::Validations attr_accessor :name end User.attribute_method?(:name) # => true User.attribute_method?(:age) # => false
# File activemodel/lib/active_model/validations.rb, line 248 def clear_validators! reset_callbacks(:validate) _validators.clear end
Удаляет все валидаторы и правила валидации.
Обратите внимание: это удалит все средства, используемые для валидации модели, как для методов validates_with, так и для validate. Удаляются валидаторы, созданные вызовом validates_with, и обратные вызовы, заданные вызовом 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}>
# ]
Если вызвать Person.clear_validators!, а затем проверить, какие валидаторы есть у этого класса, результат будет таким:
Person.validators # => []
Кроме того, обратный вызов, заданный с помощью validate :cannot_be_robot, будет удалён, поэтому:
Person._validate_callbacks.empty? # => true
# File activemodel/lib/active_model/validations.rb, line 162
def validate(*args, &block)
options = args.extract_options!
if args.all?(Symbol)
options.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 options.key?(:on)
options = options.merge(if: [predicate_for_validation_context(options[:on]), *options[:if]])
end
if options.key?(:except_on)
options = options.dup
options[:except_on] = Array(options[:except_on])
options[:unless] = [
->(o) { options[:except_on].intersect?(Array(o.validation_context)) },
*options[:unless]
]
end
set_callback(:validate, *args, options, &block)
end Добавляет в класс метод или блок валидации. Это полезно, если переопределение метода экземпляра validate становится слишком громоздким и вы хотите описывать правила валидации более выразительно.
Это можно сделать с помощью символа, указывающего на метод:
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
С помощью блока, которому передаётся текущая проверяемая запись:
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
Или с помощью блока, в котором self указывает на текущую проверяемую запись:
class Comment
include ActiveModel::Validations
validate do
errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
end
end
Обратите внимание: возвращаемое значение методов валидации не имеет значения. Остановить цепочку обратных вызовов валидации невозможно.
Параметры
-
:on— задаёт контексты, в которых активна эта валидация. По умолчанию она выполняется во всех контекстах валидацииnil. Можно передать символ или массив символов (например,on: :create,on: :custom_validation_contextилиon: [:create, :custom_validation_context]). -
:except_on— задаёт контексты, в которых эта валидация не активна. По умолчанию она выполняется во всех контекстах валидацииnil. Можно передать символ или массив символов (например,except: :create,except_on: :custom_validation_contextилиexcept_on: [:create, :custom_validation_context]). -
:if— задаёт метод или proc, который нужно вызвать, чтобы определить, следует ли выполнять валидацию (например,if: :allow_validationилиif: Proc.new { |user| user.signup_step > 2 }). Метод или proc должен возвращать или вычисляться в значениеtrueилиfalse. -
:unless— задаёт метод или proc, который нужно вызвать, чтобы определить, не следует ли выполнять валидацию (например,unless: :skip_validationилиunless: Proc.new { |user| user.signup_step <= 2 }). Метод или proc должен возвращать или вычисляться в значениеtrueилиfalse.
ПРИМЕЧАНИЕ: повторный вызов validate для того же метода перезапишет предыдущие определения.
# File activemodel/lib/active_model/validations/validates.rb, line 111
def validates(*attributes)
defaults = attributes.extract_options!.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, options|
key = "#{key.to_s.camelize}Validator"
begin
validator = const_get(key)
rescue NameError
raise ArgumentError, "Unknown validator: '#{key}'"
end
next unless options
validates_with(validator, defaults.merge(_parse_validates_options(options)))
end
end Этот метод служит сокращённой записью для всех стандартных валидаторов и любых пользовательских классов валидаторов, оканчивающихся на «Validator». Обратите внимание: стандартные валидаторы Rails можно переопределить для отдельных классов, создав вместо них пользовательские классы валидаторов, например PresenceValidator.
Примеры использования стандартных валидаторов Rails:
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
Метод validates особенно полезен, когда для заданного атрибута нужно одним вызовом применить пользовательские и стандартные валидаторы.
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add attribute, (options[:message] || "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 могут также находиться внутри проверяемого класса, что позволяет при необходимости подключать пользовательские модули валидаторов.
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
Кроме того, классы валидаторов могут находиться в другом пространстве имён и при этом использоваться в любом классе.
validates :name, :'film/title' => true
Хеш валидаторов также поддерживает регулярные выражения, диапазоны, массивы и строки в сокращённой форме.
validates :email, format: /@/ validates :role, inclusion: %w(admin contributor) validates :password, length: 6..20
При использовании сокращённой формы диапазоны и массивы передаются инициализатору валидатора как options[:in], тогда как остальные типы, включая регулярные выражения и строки, передаются как options[:with].
Вместе с валидаторами также можно использовать следующий список параметров:
-
:on— задаёт контексты, в которых активна эта валидация. По умолчанию она выполняется во всех контекстах валидацииnil. Можно передать символ или массив символов (например,on: :create,on: :custom_validation_contextилиon: [:create, :custom_validation_context]). -
:except_on— задаёт контексты, в которых эта валидация не активна. По умолчанию она выполняется во всех контекстах валидацииnil. Можно передать символ или массив символов (например,except: :create,except_on: :custom_validation_contextилиexcept_on: [:create, :custom_validation_context]). -
:if— задаёт метод, proc или строку, которые нужно вызвать, чтобы определить, следует ли выполнять валидацию (например,if: :allow_validationилиif: Proc.new { |user| user.signup_step > 2 }). Метод, proc или строка должны возвращать или вычисляться в значениеtrueилиfalse. -
:unless— задаёт метод, proc или строку, которые нужно вызвать, чтобы определить, не следует ли выполнять валидацию (например,unless: :skip_validationилиunless: Proc.new { |user| user.signup_step <= 2 }). Метод, proc или строка должны возвращать или вычисляться в значениеtrueилиfalse. -
:allow_nil— пропустить валидацию, если атрибут имеет значениеnil. -
:allow_blank— пропустить валидацию, если атрибут пуст. -
:strict— если параметр:strictимеет значение true, вместо добавления ошибки будет вызвано исключениеActiveModel::StrictValidationFailed. Параметру:strictтакже можно присвоить любое другое исключение.
Пример:
validates :password, presence: true, confirmation: true, if: :password_required?
validates :token, length: { is: 24 }, strict: TokenLengthException
Наконец, параметры :if, :unless, :on, :allow_blank, :allow_nil, :strict и :message можно передать в виде хеша одному конкретному валидатору:
validates :password, presence: { if: :password_required?, message: 'is forgotten.' }, confirmation: true
# File activemodel/lib/active_model/validations/validates.rb, line 153 def validates!(*attributes) options = attributes.extract_options! options[:strict] = true validates(*(attributes << options)) end
Этот метод используется для определения правил валидации, которые конечные пользователи не могут исправить и которые считаются исключительными. Поэтому каждый валидатор, заданный с помощью восклицательного знака или параметра :strict со значением true, всегда будет вызывать исключение ActiveModel::StrictValidationFailed вместо добавления ошибки при неудачной валидации. Подробнее о самой валидации см. в validates.
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.rb, line 89 def validates_each(*attr_names, &block) validates_with BlockValidator, _merge_attributes(attr_names), &block end
Проверяет каждый атрибут с помощью блока.
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
Параметры
-
:on— задаёт контексты, в которых активна эта валидация. По умолчанию она выполняется во всех контекстах валидацииnil. Можно передать символ или массив символов (например,on: :create,on: :custom_validation_contextилиon: [:create, :custom_validation_context]). -
:except_on— задаёт контексты, в которых эта валидация не активна. По умолчанию она выполняется во всех контекстах валидацииnil. Можно передать символ или массив символов (например,except: :create,except_on: :custom_validation_contextилиexcept_on: [:create, :custom_validation_context]). -
:allow_nil— пропустить валидацию, если атрибут имеет значениеnil. -
:allow_blank— пропустить валидацию, если атрибут пуст. -
:if— задаёт метод, proc или строку, которые нужно вызвать, чтобы определить, следует ли выполнять валидацию (например,if: :allow_validationилиif: Proc.new { |user| user.signup_step > 2 }). Метод, proc или строка должны возвращать или вычисляться в значениеtrueилиfalse. -
:unless— задаёт метод, proc или строку, которые нужно вызвать, чтобы определить, не следует ли выполнять валидацию (например,unless: :skip_validationилиunless: Proc.new { |user| user.signup_step <= 2 }). Метод, proc или строка должны возвращать или вычисляться в значениеtrueилиfalse.
# File activemodel/lib/active_model/validations/with.rb, line 88
def validates_with(*args, &block)
options = args.extract_options!
options[:class] = self
args.each do |klass|
validator = klass.new(options.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, options)
end
end Передаёт запись указанному классу или классам и позволяет им добавлять ошибки на основании более сложных условий.
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
Можно также передать несколько классов, например:
class Person include ActiveModel::Validations validates_with MyValidator, MyOtherValidator, on: :create end
Для validates_with не предусмотрено сообщение об ошибке по умолчанию. В классе валидатора необходимо вручную добавлять ошибки в коллекцию ошибок записи.
Для реализации метода validate необходимо определить параметр record, представляющий проверяемую запись.
Параметры конфигурации:
-
:on— задаёт контексты, в которых активна эта валидация. По умолчанию она выполняется во всех контекстах валидацииnil. Можно передать символ или массив символов (например,on: :create,on: :custom_validation_contextилиon: [:create, :custom_validation_context]). -
:if— задаёт метод, proc или строку, которые нужно вызвать, чтобы определить, следует ли выполнять валидацию (например,if: :allow_validationилиif: Proc.new { |user| user.signup_step > 2 }). Метод, proc или строка должны возвращать или вычисляться в значениеtrueилиfalse. -
:unless— задаёт метод, proc или строку, которые нужно вызвать, чтобы определить, не следует ли выполнять валидацию (например,unless: :skip_validationилиunless: Proc.new { |user| user.signup_step <= 2 }). Метод, proc или строка должны возвращать или вычисляться в значениеtrueилиfalse. -
:strict— указывает, должна ли валидация быть строгой. Подробнее см. вActiveModel::Validations#validates!.
Любые дополнительные параметры конфигурации будут переданы классу и доступны как options:
class Person
include ActiveModel::Validations
validates_with MyValidator, my_custom_key: 'my custom value'
end
class MyValidator < ActiveModel::Validator
def validate(record)
options[:my_custom_key] # => "my custom value"
end
end
# File activemodel/lib/active_model/validations.rb, line 206 def validators _validators.values.flatten.uniq end
Список всех валидаторов, используемых для валидации модели с помощью метода validates_with.
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 268
def validators_on(*attributes)
attributes.flat_map do |attribute|
_validators[attribute.to_sym]
end
end Список всех валидаторов, используемых для валидации указанного атрибута.
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={}>,
# ]
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.