class ActiveModel::Errors
Active Model Ошибки
Предоставляет функциональность, связанную с ошибками, которую вы можете включить в свой объект для обработки сообщений об ошибках и взаимодействия с помощниками Action View.
Минимальная реализация может быть такой:
class Person
# Required dependency for ActiveModel::Errors
extend ActiveModel::Naming
def initialize
@errors = ActiveModel::Errors.new(self)
end
attr_accessor :name
attr_reader :errors
def validate!
errors.add(:name, :blank, message: "cannot be nil") if name.nil?
end
# The following methods are needed to be minimally implemented
def read_attribute_for_validation(attr)
send(attr)
end
def self.human_attribute_name(attr, options = {})
attr
end
def self.lookup_ancestors
[self]
end
end
Последние три метода необходимы в вашем объекте для того, чтобы Errors правильно генерировать сообщения об ошибках, а также обрабатывать несколько языков. Конечно, если вы расширяете свой объект с ActiveModel::Translation, вам не нужно будет реализовывать последние два. Аналогично, использование ActiveModel::Validations обработает методы, связанные с валидацией за вас.
Это позволяет вам сделать следующее:
person = Person.new person.validate! # => ["cannot be nil"] person.errors.full_messages # => ["name cannot be nil"] # etc..
Атрибуты
Фактический массив Error объектов. Этот метод является псевдонимом для objects.
Фактический массив Error объектов. Этот метод является псевдонимом для objects.
Публичные Классовые Методы
# File activemodel/lib/active_model/errors.rb, line 85 def initialize(base) @base = base @errors = [] end
Передайте экземпляр объекта, использующего объект ошибок.
class Person
def initialize
@errors = ActiveModel::Errors.new(self)
end
end
Общедоступные методы экземпляра
# File activemodel/lib/active_model/errors.rb, line 207 def [](attribute) DeprecationHandlingMessageArray.new(messages_for(attribute), self, attribute) end
При передаче символа или имени метода возвращает массив ошибок для этого метода.
person.errors[:name] # => ["cannot be nil"] person.errors['name'] # => ["cannot be nil"]
# File activemodel/lib/active_model/errors.rb, line 404
def add(attribute, type = :invalid, **options)
attribute, type, options = normalize_arguments(attribute, type, **options)
error = Error.new(@base, attribute, type, **options)
if exception = options[:strict]
exception = ActiveModel::StrictValidationFailed if exception == true
raise exception, error.full_message
end
@errors.append(error)
error
end Добавляет новую ошибку типа type для attribute. Можно добавить несколько ошибок для одного и того же attribute. Если тип type не указан, предполагается :invalid.
person.errors.add(:name)
# Adds <#ActiveModel::Error attribute=name, type=invalid>
person.errors.add(:name, :not_implemented, message: "must be implemented")
# Adds <#ActiveModel::Error attribute=name, type=not_implemented,
options={:message=>"must be implemented"}>
person.errors.messages
# => {:name=>["is invalid", "must be implemented"]}
Если type является строкой, она будет использована в качестве сообщения об ошибке.
Если type является символом, он будет переведен с использованием соответствующего контекста (см. generate_message).
Если type является процедурой, она будет вызвана, что позволит использовать, например, Time.now внутри ошибки.
Если опция :strict установлена в значение true, будет вызвано исключение ActiveModel::StrictValidationFailed вместо добавления ошибки. Опция :strict может быть установлена в любое другое исключение.
person.errors.add(:name, :invalid, strict: true)
# => ActiveModel::StrictValidationFailed: Name is invalid
person.errors.add(:name, :invalid, strict: NameIsInvalid)
# => NameIsInvalid: Name is invalid
person.errors.messages # => {}
attribute должно быть установлено в :base, если ошибка не напрямую связана с одним атрибутом.
person.errors.add(:base, :name_or_email_blank,
message: "either name or email must be present")
person.errors.messages
# => {:base=>["either name or email must be present"]}
person.errors.details
# => {:base=>[{error: :name_or_email_blank}]}
# File activemodel/lib/active_model/errors.rb, line 434
def added?(attribute, type = :invalid, options = {})
attribute, type, options = normalize_arguments(attribute, type, **options)
if type.is_a? Symbol
@errors.any? { |error|
error.strict_match?(attribute, type, **options)
}
else
messages_for(attribute).include?(type)
end
end Возвращает true, если ошибка соответствует предоставленным attribute и type, в противном случае возвращает false. type обрабатывается так же, как и для add.
person.errors.add :name, :blank person.errors.added? :name, :blank # => true person.errors.added? :name, "can't be blank" # => true
Если для ошибки требуются параметры, возвращает true с правильными параметрами или false с неправильными или отсутствующими параметрами.
person.errors.add :name, :too_long, { count: 25 }
person.errors.added? :name, :too_long, count: 25 # => true
person.errors.added? :name, "is too long (maximum is 25 characters)" # => true
person.errors.added? :name, :too_long, count: 24 # => false
person.errors.added? :name, :too_long # => false
person.errors.added? :name, "is too long" # => false
# File activemodel/lib/active_model/errors.rb, line 310 def as_json(options = nil) to_hash(options && options[:full_messages]) end
Возвращает Hash, который может быть использован как JSON-представление этого объекта. Вы можете передать опцию :full_messages. Это определяет, должно ли JSON-представление содержать полные сообщения или нет (по умолчанию false).
person.errors.as_json # => {:name=>["cannot be nil"]}
person.errors.as_json(full_messages: true) # => {:name=>["name cannot be nil"]}
# File activemodel/lib/active_model/errors.rb, line 284 def attribute_names @errors.map(&:attribute).uniq.freeze end
Возвращает все имена атрибутов с ошибками.
person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
person.errors.attribute_names # => [:name]
# File activemodel/lib/active_model/errors.rb, line 193
def delete(attribute, type = nil, **options)
attribute, type, options = normalize_arguments(attribute, type, **options)
matches = where(attribute, type, **options)
matches.each do |error|
@errors.delete(error)
end
matches.map(&:message).presence
end Удаляет сообщения об ошибках для key. Возвращает удаленные сообщения.
person.errors[:name] # => ["cannot be nil"] person.errors.delete(:name) # => ["cannot be nil"] person.errors[:name] # => []
# File activemodel/lib/active_model/errors.rb, line 348
def details
hash = group_by_attribute.transform_values do |errors|
errors.map(&:details)
end
DeprecationHandlingDetailsHash.new(hash)
end Возвращает Hash атрибутов с массивом подробностей об ошибках.
Обновление этого хэша все еще будет обновлять состояние ошибок для обратной совместимости, но это поведение устарело.
# File activemodel/lib/active_model/errors.rb, line 235
def each(&block)
if block.arity <= 1
@errors.each(&block)
else
ActiveSupport::Deprecation.warn(<<~MSG)
Enumerating ActiveModel::Errors as a hash has been deprecated.
In Rails 6.1, `errors` is an array of Error objects,
therefore it should be accessed by a block with a single block
parameter like this:
person.errors.each do |error|
attribute = error.attribute
message = error.message
end
You are passing a block expecting two parameters,
so the old hash behavior is simulated. As this is deprecated,
this will result in an ArgumentError in Rails 6.2.
MSG
@errors.
sort { |a, b| a.attribute <=> b.attribute }.
each { |error| yield error.attribute, error.message }
end
end Перебирает каждый объект ошибки.
person.errors.add(:name, :too_short, count: 2)
person.errors.each do |error|
# Will yield <#ActiveModel::Error attribute=name, type=too_short,
options={:count=>3}>
end Для обеспечения обратной совместимости с предыдущим устаревшим поведением, если блок принимает два параметра вместо одного, он перебирает каждую пару ключ-значение в хэше сообщений об ошибках. Передает атрибут и ошибку для этого атрибута. Если у атрибута есть более одного сообщения об ошибке, вызов производится один раз для каждого сообщения об ошибке.
person.errors.add(:name, :blank, message: "can't be blank") person.errors.each do |attribute, message| # Will yield :name and "can't be blank" end person.errors.add(:name, :not_specified, message: "must be specified") person.errors.each do |attribute, message| # Will yield :name and "can't be blank" # then yield :name and "must be specified" end
# File activemodel/lib/active_model/errors.rb, line 513 def full_message(attribute, message) Error.full_message(attribute, message, @base) end
Возвращает полное сообщение для заданного атрибута.
person.errors.full_message(:name, 'is invalid') # => "Name is invalid"
# File activemodel/lib/active_model/errors.rb, line 477 def full_messages @errors.map(&:full_message) end
Возвращает все полные сообщения об ошибках в виде массива.
class Person validates_presence_of :name, :address, :email validates_length_of :name, in: 5..30 end person = Person.create(address: '123 First St.') person.errors.full_messages # => ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
# File activemodel/lib/active_model/errors.rb, line 492 def full_messages_for(attribute) where(attribute).map(&:full_message).freeze end
Возвращает все полные сообщения об ошибках для заданного атрибута в виде массива.
class Person validates_presence_of :name, :email validates_length_of :name, in: 5..30 end person = Person.create() person.errors.full_messages_for(:name) # => ["Name is too short (minimum is 5 characters)", "Name can't be blank"]
# File activemodel/lib/active_model/errors.rb, line 541
def generate_message(attribute, type = :invalid, options = {})
Error.generate_message(attribute, type, @base, options)
end Переводит сообщение об ошибке в его стандартном контексте (activemodel.errors.messages).
Error сообщения сначала ищутся в activemodel.errors.models.MODEL.attributes.ATTRIBUTE.MESSAGE, если нет, то в activemodel.errors.models.MODEL.MESSAGE, а если и там нет, то возвращается перевод стандартного сообщения (например, activemodel.errors.messages.MESSAGE). Для интерполяции доступны переведенное имя модели, переведенное имя атрибута и значение.
При использовании наследования в ваших моделях, он будет проверять все наследуемые модели тоже, но только если модель сама не найдена. Предположим, у вас есть class Admin < User; end и вы хотите перевод для ошибки :blank для атрибута title, он ищет эти переводы:
-
activemodel.errors.models.admin.attributes.title.blank -
activemodel.errors.models.admin.blank -
activemodel.errors.models.user.attributes.title.blank -
activemodel.errors.models.user.blank -
любой заданный вами по умолчанию через хэш
options(в контекстеactivemodel.errors) -
activemodel.errors.messages.blank -
errors.attributes.title.blank -
errors.messages.blank
# File activemodel/lib/active_model/errors.rb, line 118
def import(error, override_options = {})
[:attribute, :type].each do |key|
if override_options.key?(key)
override_options[key] = override_options[key].to_sym
end
end
@errors.append(NestedError.new(@base, error, override_options))
end Импортирует одну ошибку. Импортированные ошибки оборачиваются в NestedError, что обеспечивает доступ к исходному объекту ошибки. Если нужно переопределить атрибут или тип, используйте `override_options`.
override_options - Hash @option override_options [Symbol] :attribute Переопределить атрибут, к которому относится ошибка @option override_options [Symbol] :type Переопределить тип ошибки.
# File activemodel/lib/active_model/errors.rb, line 180
def include?(attribute)
@errors.any? { |error|
error.match?(attribute.to_sym)
}
end Возвращает true, если сообщения об ошибках содержат ошибку для данного ключа attribute, в противном случае возвращает false.
person.errors.messages # => {:name=>["cannot be nil"]}
person.errors.include?(:name) # => true
person.errors.include?(:age) # => false
# File activemodel/lib/active_model/errors.rb, line 273 def keys deprecation_removal_warning(:keys, "errors.attribute_names") keys = @errors.map(&:attribute) keys.uniq! keys.freeze end
Возвращает все ключи сообщений.
person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
person.errors.keys # => [:name]
# File activemodel/lib/active_model/errors.rb, line 135
def merge!(other)
other.errors.each { |error|
import(error)
}
end Объединяет ошибки из other, каждая Error обернута как NestedError.
other - Экземпляр ActiveModel::Errors.
Примеры
person.errors.merge!(other)
# File activemodel/lib/active_model/errors.rb, line 340 def messages DeprecationHandlingMessageHash.new(self) end
Возвращает Hash атрибутов с массивом их сообщений об ошибках.
Обновление этого хэша по-прежнему будет обновлять состояние ошибок для обратной совместимости, но это поведение устарело.
# File activemodel/lib/active_model/errors.rb, line 506 def messages_for(attribute) where(attribute).map(&:message) end
Возвращает все сообщения об ошибках для данного атрибута в массиве.
class Person validates_presence_of :name, :email validates_length_of :name, in: 5..30 end person = Person.create() person.errors.messages_for(:name) # => ["is too short (minimum is 5 characters)", "can't be blank"]
# File activemodel/lib/active_model/errors.rb, line 457
def of_kind?(attribute, type = :invalid)
attribute, type = normalize_arguments(attribute, type)
if type.is_a? Symbol
!where(attribute, type).empty?
else
messages_for(attribute).include?(type)
end
end Возвращает true, если ошибка на атрибуте с заданным типом присутствует, или false в противном случае. type обрабатывается так же, как и для add.
person.errors.add :age
person.errors.add :name, :too_long, { count: 25 }
person.errors.of_kind? :age # => true
person.errors.of_kind? :name # => false
person.errors.of_kind? :name, :too_long # => true
person.errors.of_kind? :name, "is too long (maximum is 25 characters)" # => true
person.errors.of_kind? :name, :not_too_long # => false
person.errors.of_kind? :name, "is too long" # => false
# File activemodel/lib/active_model/errors.rb, line 146
def slice!(*keys)
deprecation_removal_warning(:slice!)
keys = keys.map(&:to_sym)
results = messages.dup.slice!(*keys)
@errors.keep_if do |error|
keys.include?(error.attribute)
end
results
end Удаляет все ошибки, кроме указанных ключей. Возвращает хэш, содержащий удаленные ошибки.
person.errors.keys # => [:name, :age, :gender, :city]
person.errors.slice!(:age, :gender) # => { :name=>["cannot be nil"], :city=>["cannot be nil"] }
person.errors.keys # => [:age, :gender]
# File activemodel/lib/active_model/errors.rb, line 326
def to_h
ActiveSupport::Deprecation.warn(<<~EOM)
ActiveModel::Errors#to_h is deprecated and will be removed in Rails 6.2.
Please use `ActiveModel::Errors.to_hash` instead. The values in the hash
returned by `ActiveModel::Errors.to_hash` is an array of error messages.
EOM
to_hash.transform_values { |values| values.last }
end # File activemodel/lib/active_model/errors.rb, line 319
def to_hash(full_messages = false)
message_method = full_messages ? :full_message : :message
group_by_attribute.transform_values do |errors|
errors.map(&message_method)
end
end Возвращает Hash атрибутов с их сообщениями об ошибках. Если full_messages равно true, он будет содержать полные сообщения (см. full_message).
person.errors.to_hash # => {:name=>["cannot be nil"]}
person.errors.to_hash(true) # => {:name=>["name cannot be nil"]}
# File activemodel/lib/active_model/errors.rb, line 299
def to_xml(options = {})
deprecation_removal_warning(:to_xml)
to_a.to_xml({ root: "errors", skip_types: true }.merge!(options))
end Возвращает xml-представление хэша Errors.
person.errors.add(:name, :blank, message: "can't be blank") person.errors.add(:name, :not_specified, message: "must be specified") person.errors.to_xml # => # <?xml version=\"1.0\" encoding=\"UTF-8\"?> # <errors> # <error>name can't be blank</error> # <error>name must be specified</error> # </errors>
# File activemodel/lib/active_model/errors.rb, line 264
def values
deprecation_removal_warning(:values, "errors.map { |error| error.message }")
@errors.map(&:message).freeze
end Возвращает все значения сообщений.
person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
person.errors.values # => [["cannot be nil", "must be specified"]]
# File activemodel/lib/active_model/errors.rb, line 167
def where(attribute, type = nil, **options)
attribute, type, options = normalize_arguments(attribute, type, **options)
@errors.select { |error|
error.match?(attribute, type, **options)
}
end Поиск ошибок, соответствующих attribute, type или options.
Сопоставляются только предоставленные параметры.
person.errors.where(:name) # => all name errors. person.errors.where(:name, :too_short) # => all name errors being too short person.errors.where(:name, :too_short, minimum: 2) # => all name errors being too short and minimum is 2
© 2004–2020 David Heinemeier Hansson
Licensed under the MIT License.