2015-01-21 15:46:26 -07:00
|
|
|
module Shoulda
|
|
|
|
module Matchers
|
|
|
|
module ActiveModel
|
|
|
|
# @private
|
|
|
|
class Validator
|
|
|
|
include Helpers
|
|
|
|
|
allow_value: pre-set attributes before validation
While attempting to add support for `ignoring_interference_by_writer` to
the confirmation matcher, I was noticing that there are two attributes
we are concerned with: the attribute under test, and the confirmation
attribute -- for instance, `password` and `password_confirmation`. The
way that the matcher works, `password_confirmation` is set first on the
record before `password` is set, and then the whole record is validated.
This is fine, but I also noticed that `allow_value` has a specific way
of setting attributes -- not only does it check whether the attribute
being set exists and fail properly if it is does not, but it also
raises a CouldNotSetAttribute error if the attribute changes incoming
values. This logic needs to be performed on both `password_confirmation`
as well as `password`.
With that in mind, `allow_value` now supports a `values_to_preset=`
writer method which allows one to assign additional attributes unrelated
to the one being tested prior to validation. This will be used by the
confirmation matcher in a future commit.
This means that `allow_value` now operates in two steps:
1. Set attributes unrelated to the test, raising an error if any of the
attributes do not exist on the model.
2. Set the attribute under test to one or more values, raising an error
if the attribute does not exist, then running validations on the
record, failing with an appropriate error message if the validations
fail.
Note that the second step is similar to the first, although there are
more things involved. To that end, `allow_value` has been completely
refactored so that the logic for setting and validating attributes
happens in other places. Specifically, the core logic to set an
attribute (and capture the results) is located in a new AttributeSetter
class.
Also, the CouldNotSetAttributeError class has been moved to a namespace
and renamed to AttributeChangedValueError.
Finally, this commit fixes DisallowValueMatcher so that it is the true
opposite of AllowValueMatcher: DVM#matches? calls AVM#does_not_match?
and DVM#does_not_match? calls AVM#matches?.
2015-12-19 10:11:01 -07:00
|
|
|
def initialize(record, attribute, options = {})
|
2015-12-13 16:55:34 -07:00
|
|
|
@record = record
|
|
|
|
@attribute = attribute
|
allow_value: pre-set attributes before validation
While attempting to add support for `ignoring_interference_by_writer` to
the confirmation matcher, I was noticing that there are two attributes
we are concerned with: the attribute under test, and the confirmation
attribute -- for instance, `password` and `password_confirmation`. The
way that the matcher works, `password_confirmation` is set first on the
record before `password` is set, and then the whole record is validated.
This is fine, but I also noticed that `allow_value` has a specific way
of setting attributes -- not only does it check whether the attribute
being set exists and fail properly if it is does not, but it also
raises a CouldNotSetAttribute error if the attribute changes incoming
values. This logic needs to be performed on both `password_confirmation`
as well as `password`.
With that in mind, `allow_value` now supports a `values_to_preset=`
writer method which allows one to assign additional attributes unrelated
to the one being tested prior to validation. This will be used by the
confirmation matcher in a future commit.
This means that `allow_value` now operates in two steps:
1. Set attributes unrelated to the test, raising an error if any of the
attributes do not exist on the model.
2. Set the attribute under test to one or more values, raising an error
if the attribute does not exist, then running validations on the
record, failing with an appropriate error message if the validations
fail.
Note that the second step is similar to the first, although there are
more things involved. To that end, `allow_value` has been completely
refactored so that the logic for setting and validating attributes
happens in other places. Specifically, the core logic to set an
attribute (and capture the results) is located in a new AttributeSetter
class.
Also, the CouldNotSetAttributeError class has been moved to a namespace
and renamed to AttributeChangedValueError.
Finally, this commit fixes DisallowValueMatcher so that it is the true
opposite of AllowValueMatcher: DVM#matches? calls AVM#does_not_match?
and DVM#does_not_match? calls AVM#matches?.
2015-12-19 10:11:01 -07:00
|
|
|
@context = options[:context]
|
|
|
|
@expects_strict = options[:expects_strict]
|
|
|
|
@expected_message = options[:expected_message]
|
2015-01-21 15:46:26 -07:00
|
|
|
|
2015-12-13 16:55:34 -07:00
|
|
|
@_validation_result = nil
|
|
|
|
@captured_validation_exception = false
|
|
|
|
@captured_range_error = false
|
2015-01-21 15:46:26 -07:00
|
|
|
end
|
|
|
|
|
allow_value: pre-set attributes before validation
While attempting to add support for `ignoring_interference_by_writer` to
the confirmation matcher, I was noticing that there are two attributes
we are concerned with: the attribute under test, and the confirmation
attribute -- for instance, `password` and `password_confirmation`. The
way that the matcher works, `password_confirmation` is set first on the
record before `password` is set, and then the whole record is validated.
This is fine, but I also noticed that `allow_value` has a specific way
of setting attributes -- not only does it check whether the attribute
being set exists and fail properly if it is does not, but it also
raises a CouldNotSetAttribute error if the attribute changes incoming
values. This logic needs to be performed on both `password_confirmation`
as well as `password`.
With that in mind, `allow_value` now supports a `values_to_preset=`
writer method which allows one to assign additional attributes unrelated
to the one being tested prior to validation. This will be used by the
confirmation matcher in a future commit.
This means that `allow_value` now operates in two steps:
1. Set attributes unrelated to the test, raising an error if any of the
attributes do not exist on the model.
2. Set the attribute under test to one or more values, raising an error
if the attribute does not exist, then running validations on the
record, failing with an appropriate error message if the validations
fail.
Note that the second step is similar to the first, although there are
more things involved. To that end, `allow_value` has been completely
refactored so that the logic for setting and validating attributes
happens in other places. Specifically, the core logic to set an
attribute (and capture the results) is located in a new AttributeSetter
class.
Also, the CouldNotSetAttributeError class has been moved to a namespace
and renamed to AttributeChangedValueError.
Finally, this commit fixes DisallowValueMatcher so that it is the true
opposite of AllowValueMatcher: DVM#matches? calls AVM#does_not_match?
and DVM#does_not_match? calls AVM#matches?.
2015-12-19 10:11:01 -07:00
|
|
|
def call
|
|
|
|
!messages_match? && !captured_range_error?
|
2015-01-21 15:46:26 -07:00
|
|
|
end
|
|
|
|
|
2015-12-13 16:55:34 -07:00
|
|
|
def has_messages?
|
|
|
|
messages.any?
|
2015-01-21 15:46:26 -07:00
|
|
|
end
|
|
|
|
|
2015-12-13 16:55:34 -07:00
|
|
|
def captured_validation_exception?
|
|
|
|
@captured_validation_exception
|
2015-01-21 15:46:26 -07:00
|
|
|
end
|
|
|
|
|
allow_value: pre-set attributes before validation
While attempting to add support for `ignoring_interference_by_writer` to
the confirmation matcher, I was noticing that there are two attributes
we are concerned with: the attribute under test, and the confirmation
attribute -- for instance, `password` and `password_confirmation`. The
way that the matcher works, `password_confirmation` is set first on the
record before `password` is set, and then the whole record is validated.
This is fine, but I also noticed that `allow_value` has a specific way
of setting attributes -- not only does it check whether the attribute
being set exists and fail properly if it is does not, but it also
raises a CouldNotSetAttribute error if the attribute changes incoming
values. This logic needs to be performed on both `password_confirmation`
as well as `password`.
With that in mind, `allow_value` now supports a `values_to_preset=`
writer method which allows one to assign additional attributes unrelated
to the one being tested prior to validation. This will be used by the
confirmation matcher in a future commit.
This means that `allow_value` now operates in two steps:
1. Set attributes unrelated to the test, raising an error if any of the
attributes do not exist on the model.
2. Set the attribute under test to one or more values, raising an error
if the attribute does not exist, then running validations on the
record, failing with an appropriate error message if the validations
fail.
Note that the second step is similar to the first, although there are
more things involved. To that end, `allow_value` has been completely
refactored so that the logic for setting and validating attributes
happens in other places. Specifically, the core logic to set an
attribute (and capture the results) is located in a new AttributeSetter
class.
Also, the CouldNotSetAttributeError class has been moved to a namespace
and renamed to AttributeChangedValueError.
Finally, this commit fixes DisallowValueMatcher so that it is the true
opposite of AllowValueMatcher: DVM#matches? calls AVM#does_not_match?
and DVM#does_not_match? calls AVM#matches?.
2015-12-19 10:11:01 -07:00
|
|
|
def type_of_message_matched?
|
|
|
|
expects_strict? == captured_validation_exception?
|
2015-01-21 15:46:26 -07:00
|
|
|
end
|
|
|
|
|
2015-12-13 16:55:34 -07:00
|
|
|
def all_formatted_validation_error_messages
|
|
|
|
format_validation_errors(all_validation_errors)
|
2015-01-21 15:46:26 -07:00
|
|
|
end
|
|
|
|
|
2015-12-13 16:55:34 -07:00
|
|
|
def validation_exception_message
|
|
|
|
validation_result[:validation_exception_message]
|
2015-01-21 15:46:26 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
2015-12-13 16:55:34 -07:00
|
|
|
attr_reader :attribute, :context, :record
|
2015-01-21 15:46:26 -07:00
|
|
|
|
|
|
|
private
|
|
|
|
|
2015-12-13 16:55:34 -07:00
|
|
|
def expects_strict?
|
|
|
|
@expects_strict
|
2015-01-21 15:46:26 -07:00
|
|
|
end
|
|
|
|
|
allow_value: pre-set attributes before validation
While attempting to add support for `ignoring_interference_by_writer` to
the confirmation matcher, I was noticing that there are two attributes
we are concerned with: the attribute under test, and the confirmation
attribute -- for instance, `password` and `password_confirmation`. The
way that the matcher works, `password_confirmation` is set first on the
record before `password` is set, and then the whole record is validated.
This is fine, but I also noticed that `allow_value` has a specific way
of setting attributes -- not only does it check whether the attribute
being set exists and fail properly if it is does not, but it also
raises a CouldNotSetAttribute error if the attribute changes incoming
values. This logic needs to be performed on both `password_confirmation`
as well as `password`.
With that in mind, `allow_value` now supports a `values_to_preset=`
writer method which allows one to assign additional attributes unrelated
to the one being tested prior to validation. This will be used by the
confirmation matcher in a future commit.
This means that `allow_value` now operates in two steps:
1. Set attributes unrelated to the test, raising an error if any of the
attributes do not exist on the model.
2. Set the attribute under test to one or more values, raising an error
if the attribute does not exist, then running validations on the
record, failing with an appropriate error message if the validations
fail.
Note that the second step is similar to the first, although there are
more things involved. To that end, `allow_value` has been completely
refactored so that the logic for setting and validating attributes
happens in other places. Specifically, the core logic to set an
attribute (and capture the results) is located in a new AttributeSetter
class.
Also, the CouldNotSetAttributeError class has been moved to a namespace
and renamed to AttributeChangedValueError.
Finally, this commit fixes DisallowValueMatcher so that it is the true
opposite of AllowValueMatcher: DVM#matches? calls AVM#does_not_match?
and DVM#does_not_match? calls AVM#matches?.
2015-12-19 10:11:01 -07:00
|
|
|
def messages_match?
|
|
|
|
has_messages? &&
|
|
|
|
type_of_message_matched? &&
|
|
|
|
matched_messages.compact.any?
|
|
|
|
end
|
|
|
|
|
|
|
|
def messages
|
|
|
|
if expects_strict?
|
|
|
|
[validation_exception_message]
|
|
|
|
else
|
|
|
|
validation_error_messages
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def matched_messages
|
|
|
|
if @expected_message
|
|
|
|
messages.grep(@expected_message)
|
|
|
|
else
|
|
|
|
messages
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def captured_range_error?
|
|
|
|
!!@captured_range_error
|
|
|
|
end
|
|
|
|
|
|
|
|
def all_validation_errors
|
|
|
|
validation_result[:all_validation_errors]
|
|
|
|
end
|
|
|
|
|
|
|
|
def validation_error_messages
|
|
|
|
validation_result[:validation_error_messages]
|
|
|
|
end
|
|
|
|
|
2015-12-13 16:55:34 -07:00
|
|
|
def validation_result
|
|
|
|
@_validation_result ||= perform_validation
|
2015-01-21 15:46:26 -07:00
|
|
|
end
|
|
|
|
|
2015-12-13 16:55:34 -07:00
|
|
|
def perform_validation
|
2015-01-21 15:46:26 -07:00
|
|
|
if context
|
|
|
|
record.valid?(context)
|
|
|
|
else
|
|
|
|
record.valid?
|
|
|
|
end
|
|
|
|
|
2015-12-13 16:55:34 -07:00
|
|
|
all_validation_errors = record.errors.dup
|
|
|
|
|
2021-06-03 17:41:27 -03:00
|
|
|
validation_error_messages = record.errors[attribute]
|
2015-12-13 16:55:34 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
all_validation_errors: all_validation_errors,
|
|
|
|
validation_error_messages: validation_error_messages,
|
2020-09-04 20:43:05 +05:30
|
|
|
validation_exception_message: nil,
|
2015-12-13 16:55:34 -07:00
|
|
|
}
|
2020-09-06 18:39:31 +05:30
|
|
|
rescue ::ActiveModel::StrictValidationFailed => e
|
2015-12-13 16:55:34 -07:00
|
|
|
@captured_validation_exception = true
|
|
|
|
{
|
|
|
|
all_validation_errors: nil,
|
|
|
|
validation_error_messages: [],
|
2020-09-06 18:39:31 +05:30
|
|
|
validation_exception_message: e.message,
|
2015-12-13 16:55:34 -07:00
|
|
|
}
|
2015-01-21 15:46:26 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|