mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
06756290d5
- When a ActiveRecord record get saved and validated as part of a
collection association, the errors attribute are changed to reflect
the children names. You end up with an error attribute that will
look like this:
`author.errors # {:'books.title' => [:blank]}`
2fe20cb55c/activerecord/lib/active_record/autosave_association.rb (L331-L340)
We then can't check if the `books.title` errors was added using
`ActiveModel::Errors#added?` because it tries to generate a message
to make the match and end up calling the "books.title" method
on the Author.
```
author.errors.added?(:'books.title', :blank) => NoMethodError: undefined method `books.title'
```
This patch modify the behaviour of `strict_match?` to not generate
a message to make the comparison but instead make a strict
comparison with the `options` from the error.
80 lines
1.8 KiB
Ruby
80 lines
1.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module ActiveModel
|
|
# == Active \Model \Error
|
|
#
|
|
# Represents one single error
|
|
class Error
|
|
CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
|
|
MESSAGE_OPTIONS = [:message]
|
|
|
|
def initialize(base, attribute, type = :invalid, **options)
|
|
@base = base
|
|
@attribute = attribute
|
|
@raw_type = type
|
|
@type = type || :invalid
|
|
@options = options
|
|
end
|
|
|
|
def initialize_dup(other)
|
|
@attribute = @attribute.dup
|
|
@raw_type = @raw_type.dup
|
|
@type = @type.dup
|
|
@options = @options.deep_dup
|
|
end
|
|
|
|
attr_reader :base, :attribute, :type, :raw_type, :options
|
|
|
|
def message
|
|
case raw_type
|
|
when Symbol
|
|
base.errors.generate_message(attribute, raw_type, options.except(*CALLBACKS_OPTIONS))
|
|
else
|
|
raw_type
|
|
end
|
|
end
|
|
|
|
def detail
|
|
{ error: raw_type }.merge(options.except(*CALLBACKS_OPTIONS + MESSAGE_OPTIONS))
|
|
end
|
|
|
|
def full_message
|
|
base.errors.full_message(attribute, message)
|
|
end
|
|
|
|
# See if error matches provided +attribute+, +type+ and +options+.
|
|
def match?(attribute, type = nil, **options)
|
|
if @attribute != attribute || (type && @type != type)
|
|
return false
|
|
end
|
|
|
|
options.each do |key, value|
|
|
if @options[key] != value
|
|
return false
|
|
end
|
|
end
|
|
|
|
true
|
|
end
|
|
|
|
def strict_match?(attribute, type, **options)
|
|
return false unless match?(attribute, type)
|
|
|
|
options == @options.except(*CALLBACKS_OPTIONS + MESSAGE_OPTIONS)
|
|
end
|
|
|
|
def ==(other)
|
|
other.is_a?(self.class) && attributes_for_hash == other.attributes_for_hash
|
|
end
|
|
alias eql? ==
|
|
|
|
def hash
|
|
attributes_for_hash.hash
|
|
end
|
|
|
|
protected
|
|
def attributes_for_hash
|
|
[@base, @attribute, @raw_type, @options]
|
|
end
|
|
end
|
|
end
|