1
0
Fork 0
mirror of https://github.com/thoughtbot/shoulda-matchers.git synced 2022-11-09 12:01:38 -05:00
thoughtbot--shoulda-matchers/lib/shoulda/matchers/active_model/validator.rb

120 lines
2.8 KiB
Ruby
Raw Normal View History

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 = {})
@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]
@_validation_result = nil
@captured_validation_exception = false
@captured_range_error = false
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?
end
def has_messages?
messages.any?
end
def captured_validation_exception?
@captured_validation_exception
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?
end
def all_formatted_validation_error_messages
format_validation_errors(all_validation_errors)
end
def validation_exception_message
validation_result[:validation_exception_message]
end
protected
attr_reader :attribute, :context, :record
private
def expects_strict?
@expects_strict
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
def validation_result
@_validation_result ||= perform_validation
end
def perform_validation
if context
record.valid?(context)
else
record.valid?
end
all_validation_errors = record.errors.dup
validation_error_messages = record.errors[attribute]
{
all_validation_errors: all_validation_errors,
validation_error_messages: validation_error_messages,
validation_exception_message: nil,
}
rescue ::ActiveModel::StrictValidationFailed => e
@captured_validation_exception = true
{
all_validation_errors: nil,
validation_error_messages: [],
validation_exception_message: e.message,
}
end
end
end
end
end