WIP
This commit is contained in:
parent
e5ce548600
commit
e45a70dffa
|
@ -3,15 +3,16 @@ require 'shoulda/matchers/active_model/qualifiers'
|
|||
require 'shoulda/matchers/active_model/validation_matcher'
|
||||
require 'shoulda/matchers/active_model/validation_matcher/build_description'
|
||||
require 'shoulda/matchers/active_model/validator'
|
||||
require 'shoulda/matchers/active_model/allow_or_disallow_value_matcher/attribute_changed_value_error'
|
||||
require 'shoulda/matchers/active_model/allow_or_disallow_value_matcher/attribute_does_not_exist_error'
|
||||
require 'shoulda/matchers/active_model/allow_or_disallow_value_matcher/attribute_setter'
|
||||
require 'shoulda/matchers/active_model/allow_or_disallow_value_matcher/attribute_setter_and_validator'
|
||||
require 'shoulda/matchers/active_model/allow_or_disallow_value_matcher/attribute_setters'
|
||||
require 'shoulda/matchers/active_model/allow_or_disallow_value_matcher/attribute_setters_and_validators'
|
||||
require 'shoulda/matchers/active_model/allow_or_disallow_value_matcher/successful_check'
|
||||
require 'shoulda/matchers/active_model/allow_or_disallow_value_matcher/successful_setting'
|
||||
require 'shoulda/matchers/active_model/allow_or_disallow_value_matcher'
|
||||
require 'shoulda/matchers/active_model/allow_value_matcher'
|
||||
require 'shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error'
|
||||
require 'shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error'
|
||||
require 'shoulda/matchers/active_model/allow_value_matcher/attribute_setter'
|
||||
require 'shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator'
|
||||
require 'shoulda/matchers/active_model/allow_value_matcher/attribute_setters'
|
||||
require 'shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators'
|
||||
require 'shoulda/matchers/active_model/allow_value_matcher/successful_check'
|
||||
require 'shoulda/matchers/active_model/allow_value_matcher/successful_setting'
|
||||
require 'shoulda/matchers/active_model/disallow_value_matcher'
|
||||
require 'shoulda/matchers/active_model/validate_length_of_matcher'
|
||||
require 'shoulda/matchers/active_model/validate_inclusion_of_matcher'
|
||||
|
|
|
@ -0,0 +1,358 @@
|
|||
module Shoulda
|
||||
module Matchers
|
||||
module ActiveModel
|
||||
# @private
|
||||
class AllowOrDisallowValueMatcher
|
||||
include Helpers
|
||||
include Qualifiers::IgnoringInterferenceByWriter
|
||||
|
||||
attr_reader(
|
||||
:after_setting_value_callback,
|
||||
:attribute_to_check_message_against,
|
||||
:attribute_to_set,
|
||||
:context,
|
||||
:subject,
|
||||
)
|
||||
|
||||
attr_writer(
|
||||
:attribute_changed_value_message,
|
||||
:failure_message_preface,
|
||||
:values_to_preset,
|
||||
)
|
||||
|
||||
def initialize(*values)
|
||||
super
|
||||
@values_to_set = values
|
||||
@options = {}
|
||||
@after_setting_value_callback = -> {}
|
||||
@expects_strict = false
|
||||
@expects_custom_validation_message = false
|
||||
@context = nil
|
||||
@values_to_preset = {}
|
||||
@failure_message_preface = nil
|
||||
@attribute_changed_value_message = nil
|
||||
@was_negated = nil
|
||||
end
|
||||
|
||||
def for(attribute_name)
|
||||
@attribute_to_set = attribute_name
|
||||
@attribute_to_check_message_against = attribute_name
|
||||
self
|
||||
end
|
||||
|
||||
def on(context)
|
||||
if context.present?
|
||||
@context = context
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def with_message(message, given_options = {})
|
||||
if message.present?
|
||||
@expects_custom_validation_message = true
|
||||
options[:expected_message] = message
|
||||
options[:expected_message_values] = given_options.fetch(:values, {})
|
||||
|
||||
if given_options.key?(:against)
|
||||
@attribute_to_check_message_against = given_options[:against]
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def expected_message
|
||||
if options.key?(:expected_message)
|
||||
if Symbol === options[:expected_message]
|
||||
default_expected_message
|
||||
else
|
||||
options[:expected_message]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def expects_custom_validation_message?
|
||||
@expects_custom_validation_message
|
||||
end
|
||||
|
||||
def strict(expects_strict = true)
|
||||
@expects_strict = expects_strict
|
||||
self
|
||||
end
|
||||
|
||||
def expects_strict?
|
||||
@expects_strict
|
||||
end
|
||||
|
||||
def _after_setting_value(&callback)
|
||||
@after_setting_value_callback = callback
|
||||
end
|
||||
|
||||
def was_negated?
|
||||
@was_negated
|
||||
end
|
||||
|
||||
def description
|
||||
ValidationMatcher::BuildDescription.call(self, simple_description)
|
||||
end
|
||||
|
||||
def model
|
||||
subject.class
|
||||
end
|
||||
|
||||
def last_attribute_setter_used
|
||||
result.attribute_setter
|
||||
end
|
||||
|
||||
def last_value_set
|
||||
last_attribute_setter_used.value_written
|
||||
end
|
||||
|
||||
def pretty_print(pp)
|
||||
Shoulda::Matchers::Util.pretty_print(self, pp, {
|
||||
was_negated: was_negated?,
|
||||
attribute_to_set: attribute_to_set,
|
||||
attribute_to_check_message_against: attribute_to_check_message_against,
|
||||
values_to_set: values_to_set,
|
||||
expected_message: expected_message,
|
||||
expects_strict: expects_strict?,
|
||||
subject: subject,
|
||||
attribute_setters_and_validators_for_values_to_set: attribute_setters_and_validators_for_values_to_set,
|
||||
})
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader(
|
||||
:options,
|
||||
:result,
|
||||
:values_to_preset,
|
||||
:values_to_set,
|
||||
)
|
||||
|
||||
def matches?(subject)
|
||||
@subject = subject
|
||||
@was_negated = false
|
||||
false
|
||||
end
|
||||
|
||||
def does_not_match?(subject)
|
||||
@subject = subject
|
||||
@was_negated = true
|
||||
false
|
||||
end
|
||||
|
||||
def positive_failure_message
|
||||
attribute_setter = result.attribute_setter
|
||||
|
||||
if result.attribute_setter.successfully_checked?
|
||||
validator = result.validator
|
||||
message = failure_message_preface.call
|
||||
message << ' valid, but it was invalid instead,'
|
||||
|
||||
if validator.captured_validation_exception?
|
||||
message << ' raising a validation exception with the message '
|
||||
message << validator.validation_exception_message.inspect
|
||||
message << '.'
|
||||
else
|
||||
message << " producing these validation errors:\n\n"
|
||||
message << validator.all_formatted_validation_error_messages
|
||||
end
|
||||
else
|
||||
message = attribute_setter.failure_message
|
||||
end
|
||||
|
||||
if include_attribute_changed_value_message?
|
||||
message << "\n\n" + attribute_changed_value_message.call
|
||||
end
|
||||
|
||||
Shoulda::Matchers.word_wrap(message)
|
||||
end
|
||||
|
||||
def negative_failure_message
|
||||
attribute_setter = result.attribute_setter
|
||||
|
||||
if attribute_setter.successfully_checked?
|
||||
validator = result.validator
|
||||
message = failure_message_preface.call + ' invalid'
|
||||
|
||||
if validator.validation_message_type_matches?
|
||||
if validator.has_matching_validation_messages?
|
||||
message << ' and to'
|
||||
|
||||
if validator.captured_validation_exception?
|
||||
message << ' raise a validation exception with message'
|
||||
else
|
||||
message << ' produce'
|
||||
|
||||
if expected_message.is_a?(Regexp)
|
||||
message << ' a'
|
||||
else
|
||||
message << ' the'
|
||||
end
|
||||
|
||||
message << ' validation error'
|
||||
end
|
||||
|
||||
if expected_message.is_a?(Regexp)
|
||||
message << ' matching '
|
||||
message << Shoulda::Matchers::Util.inspect_value(
|
||||
expected_message,
|
||||
)
|
||||
else
|
||||
message << " #{expected_message.inspect}"
|
||||
end
|
||||
|
||||
if !validator.captured_validation_exception?
|
||||
message << " on :#{attribute_to_check_message_against}"
|
||||
end
|
||||
|
||||
message << '. The record was indeed invalid, but'
|
||||
|
||||
if validator.captured_validation_exception?
|
||||
message << ' the exception message was '
|
||||
message << validator.validation_exception_message.inspect
|
||||
message << ' instead.'
|
||||
else
|
||||
message << " it produced these validation errors instead:\n\n"
|
||||
message << validator.all_formatted_validation_error_messages
|
||||
end
|
||||
else
|
||||
message << ', but it was valid instead.'
|
||||
end
|
||||
elsif validator.captured_validation_exception?
|
||||
message << ' and to produce validation errors, but the record'
|
||||
message << ' raised a validation exception instead.'
|
||||
else
|
||||
message << ' and to raise a validation exception, but the record'
|
||||
message << ' produced validation errors instead.'
|
||||
end
|
||||
else
|
||||
message = attribute_setter.failure_message
|
||||
end
|
||||
|
||||
if include_attribute_changed_value_message?
|
||||
message << "\n\n" + attribute_changed_value_message.call
|
||||
end
|
||||
|
||||
Shoulda::Matchers.word_wrap(message)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def run(strategy)
|
||||
attribute_setters_for_values_to_preset.first_to_unexpectedly_not_pass ||
|
||||
attribute_setters_and_validators_for_values_to_set.public_send(strategy)
|
||||
end
|
||||
|
||||
def failure_message_preface
|
||||
@failure_message_preface || method(:default_failure_message_preface)
|
||||
end
|
||||
|
||||
def default_failure_message_preface
|
||||
''.tap do |preface|
|
||||
if descriptions_for_preset_values.any?
|
||||
preface << 'After setting '
|
||||
preface << descriptions_for_preset_values.to_sentence
|
||||
preface << ', then '
|
||||
else
|
||||
preface << 'After '
|
||||
end
|
||||
|
||||
preface << 'setting '
|
||||
preface << description_for_resulting_attribute_setter
|
||||
|
||||
unless preface.end_with?('--')
|
||||
preface << ','
|
||||
end
|
||||
|
||||
preface << " the matcher expected the #{model.name} to be"
|
||||
end
|
||||
end
|
||||
|
||||
def include_attribute_changed_value_message?
|
||||
!ignore_interference_by_writer.never? &&
|
||||
result.attribute_setter.attribute_changed_value?
|
||||
end
|
||||
|
||||
def attribute_changed_value_message
|
||||
@attribute_changed_value_message ||
|
||||
method(:default_attribute_changed_value_message)
|
||||
end
|
||||
|
||||
def default_attribute_changed_value_message
|
||||
<<-MESSAGE.strip
|
||||
As indicated in the message above, :#{result.attribute_setter.attribute_name}
|
||||
seems to be changing certain values as they are set, and this could have
|
||||
something to do with why this test is failing. If you've overridden the writer
|
||||
method for this attribute, then you may need to change it to make this test
|
||||
pass, or do something else entirely.
|
||||
MESSAGE
|
||||
end
|
||||
|
||||
def descriptions_for_preset_values
|
||||
attribute_setters_for_values_to_preset.
|
||||
map(&:attribute_setter_description)
|
||||
end
|
||||
|
||||
def description_for_resulting_attribute_setter
|
||||
result.attribute_setter_description
|
||||
end
|
||||
|
||||
def attribute_setters_for_values_to_preset
|
||||
@_attribute_setters_for_values_to_preset ||=
|
||||
AttributeSetters.new(self, values_to_preset)
|
||||
end
|
||||
|
||||
def attribute_setters_and_validators_for_values_to_set
|
||||
@_attribute_setters_and_validators_for_values_to_set ||=
|
||||
AttributeSettersAndValidators.new(
|
||||
self,
|
||||
values_to_set.map { |value| [attribute_to_set, value] },
|
||||
)
|
||||
end
|
||||
|
||||
def inspected_values_to_set
|
||||
Shoulda::Matchers::Util.inspect_values(values_to_set).to_sentence(
|
||||
two_words_connector: ' or ',
|
||||
last_word_connector: ', or ',
|
||||
)
|
||||
end
|
||||
|
||||
def default_expected_message
|
||||
if expects_strict?
|
||||
"#{human_attribute_name} #{default_attribute_message}"
|
||||
else
|
||||
default_attribute_message
|
||||
end
|
||||
end
|
||||
|
||||
def default_attribute_message
|
||||
default_error_message(
|
||||
options[:expected_message],
|
||||
default_attribute_message_values,
|
||||
)
|
||||
end
|
||||
|
||||
def default_attribute_message_values
|
||||
defaults = {
|
||||
model_name: model_name,
|
||||
model: subject,
|
||||
attribute: attribute_to_check_message_against,
|
||||
}
|
||||
|
||||
defaults.merge(options[:expected_message_values])
|
||||
end
|
||||
|
||||
def model_name
|
||||
subject.class.to_s.underscore
|
||||
end
|
||||
|
||||
def human_attribute_name
|
||||
subject.class.human_attribute_name(attribute_to_check_message_against)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
module Shoulda
|
||||
module Matchers
|
||||
module ActiveModel
|
||||
class AllowValueMatcher
|
||||
class AllowOrDisallowValueMatcher
|
||||
# @private
|
||||
class AttributeChangedValueError < Shoulda::Matchers::Error
|
||||
attr_accessor :matcher_name, :model, :attribute_name, :value_written,
|
|
@ -1,7 +1,7 @@
|
|||
module Shoulda
|
||||
module Matchers
|
||||
module ActiveModel
|
||||
class AllowValueMatcher
|
||||
class AllowOrDisallowValueMatcher
|
||||
# @private
|
||||
class AttributeDoesNotExistError < Shoulda::Matchers::Error
|
||||
attr_accessor :model, :attribute_name, :value
|
|
@ -1,7 +1,7 @@
|
|||
module Shoulda
|
||||
module Matchers
|
||||
module ActiveModel
|
||||
class AllowValueMatcher
|
||||
class AllowOrDisallowValueMatcher
|
||||
# @private
|
||||
class AttributeSetter
|
||||
def self.set(args)
|
|
@ -3,7 +3,7 @@ require 'forwardable'
|
|||
module Shoulda
|
||||
module Matchers
|
||||
module ActiveModel
|
||||
class AllowValueMatcher
|
||||
class AllowOrDisallowValueMatcher
|
||||
# @private
|
||||
class AttributeSetterAndValidator
|
||||
extend Forwardable
|
||||
|
@ -16,7 +16,7 @@ module Shoulda
|
|||
:expected_message,
|
||||
:expects_strict?,
|
||||
:ignore_interference_by_writer,
|
||||
:instance,
|
||||
:subject,
|
||||
)
|
||||
|
||||
def initialize(allow_value_matcher, attribute_name, value)
|
||||
|
@ -30,7 +30,7 @@ module Shoulda
|
|||
def attribute_setter
|
||||
@_attribute_setter ||= AttributeSetter.new(
|
||||
matcher_name: :allow_value,
|
||||
object: instance,
|
||||
object: subject,
|
||||
attribute_name: attribute_name,
|
||||
value: value,
|
||||
ignore_interference_by_writer: ignore_interference_by_writer,
|
||||
|
@ -44,7 +44,7 @@ module Shoulda
|
|||
|
||||
def validator
|
||||
@_validator ||= Validator.new(
|
||||
instance,
|
||||
subject,
|
||||
attribute_to_check_message_against,
|
||||
context: context,
|
||||
expects_strict: expects_strict?,
|
|
@ -1,7 +1,7 @@
|
|||
module Shoulda
|
||||
module Matchers
|
||||
module ActiveModel
|
||||
class AllowValueMatcher
|
||||
class AllowOrDisallowValueMatcher
|
||||
# @private
|
||||
class AttributeSetters
|
||||
include Enumerable
|
|
@ -1,7 +1,7 @@
|
|||
module Shoulda
|
||||
module Matchers
|
||||
module ActiveModel
|
||||
class AllowValueMatcher
|
||||
class AllowOrDisallowValueMatcher
|
||||
# @private
|
||||
class AttributeSettersAndValidators
|
||||
include Enumerable
|
||||
|
@ -51,7 +51,7 @@ module Shoulda
|
|||
# binding.pry
|
||||
tuple.attribute_setter.set!
|
||||
# -- BEFORE: -- !tuple.validator.does_not_pass?
|
||||
# tuple.validator.passes?
|
||||
# -- ALSO: -- tuple.validator.passes?
|
||||
!tuple.validator.fails?
|
||||
end
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
module Shoulda
|
||||
module Matchers
|
||||
module ActiveModel
|
||||
class AllowValueMatcher
|
||||
class AllowOrDisallowValueMatcher
|
||||
# @private
|
||||
class SuccessfulCheck
|
||||
def successful?
|
|
@ -1,7 +1,7 @@
|
|||
module Shoulda
|
||||
module Matchers
|
||||
module ActiveModel
|
||||
class AllowValueMatcher
|
||||
class AllowOrDisallowValueMatcher
|
||||
# @private
|
||||
class SuccessfulSetting
|
||||
def successful?
|
|
@ -304,369 +304,32 @@ module Shoulda
|
|||
alias_method :allow_values, :allow_value
|
||||
|
||||
# @private
|
||||
class AllowValueMatcher
|
||||
include Helpers
|
||||
include Qualifiers::IgnoringInterferenceByWriter
|
||||
|
||||
attr_reader(
|
||||
:after_setting_value_callback,
|
||||
:attribute_to_check_message_against,
|
||||
:attribute_to_set,
|
||||
:context,
|
||||
:instance
|
||||
)
|
||||
|
||||
attr_writer(
|
||||
:attribute_changed_value_message,
|
||||
:failure_message_preface,
|
||||
:values_to_preset,
|
||||
)
|
||||
|
||||
def initialize(*values, will_be_negated: nil)
|
||||
super
|
||||
@values_to_set = values
|
||||
@options = {}
|
||||
@after_setting_value_callback = -> {}
|
||||
@expects_strict = false
|
||||
@expects_custom_validation_message = false
|
||||
@context = nil
|
||||
@values_to_preset = {}
|
||||
@failure_message_preface = nil
|
||||
@attribute_changed_value_message = nil
|
||||
@will_be_negated = will_be_negated
|
||||
@was_negated = nil
|
||||
class AllowValueMatcher < AllowOrDisallowValueMatcher
|
||||
def simple_description
|
||||
"pass validation when :#{attribute_to_set} is set to " +
|
||||
"#{inspected_values_to_set}"
|
||||
end
|
||||
|
||||
def for(attribute_name)
|
||||
@attribute_to_set = attribute_name
|
||||
@attribute_to_check_message_against = attribute_name
|
||||
self
|
||||
end
|
||||
def matches?(subject)
|
||||
super(subject)
|
||||
|
||||
def on(context)
|
||||
if context.present?
|
||||
@context = context
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def with_message(message, given_options = {})
|
||||
if message.present?
|
||||
@expects_custom_validation_message = true
|
||||
options[:expected_message] = message
|
||||
options[:expected_message_values] = given_options.fetch(:values, {})
|
||||
|
||||
if given_options.key?(:against)
|
||||
@attribute_to_check_message_against = given_options[:against]
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def expected_message
|
||||
if options.key?(:expected_message)
|
||||
if Symbol === options[:expected_message]
|
||||
default_expected_message
|
||||
else
|
||||
options[:expected_message]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def expects_custom_validation_message?
|
||||
@expects_custom_validation_message
|
||||
end
|
||||
|
||||
def strict(expects_strict = true)
|
||||
@expects_strict = expects_strict
|
||||
self
|
||||
end
|
||||
|
||||
def expects_strict?
|
||||
@expects_strict
|
||||
end
|
||||
|
||||
def _after_setting_value(&callback)
|
||||
@after_setting_value_callback = callback
|
||||
end
|
||||
|
||||
def matches?(instance)
|
||||
@instance = instance
|
||||
@was_negated = false
|
||||
@result = run(:first_to_unexpectedly_not_pass)
|
||||
@result.nil?
|
||||
end
|
||||
|
||||
def does_not_match?(instance)
|
||||
@instance = instance
|
||||
@was_negated = true
|
||||
@result = run(:first_to_unexpectedly_not_fail)
|
||||
@result.nil?
|
||||
end
|
||||
# def does_not_match?(subject)
|
||||
# super(subject)
|
||||
|
||||
# @result = run(:first_to_unexpectedly_not_fail)
|
||||
# @result.nil?
|
||||
# end
|
||||
|
||||
def failure_message
|
||||
attribute_setter = result.attribute_setter
|
||||
|
||||
if result.attribute_setter.successfully_checked?
|
||||
validator = result.validator
|
||||
message = failure_message_preface.call
|
||||
message << ' valid, but it was invalid instead,'
|
||||
|
||||
if validator.captured_validation_exception?
|
||||
message << ' raising a validation exception with the message '
|
||||
message << validator.validation_exception_message.inspect
|
||||
message << '.'
|
||||
else
|
||||
message << " producing these validation errors:\n\n"
|
||||
message << validator.all_formatted_validation_error_messages
|
||||
end
|
||||
else
|
||||
message = attribute_setter.failure_message
|
||||
end
|
||||
|
||||
if include_attribute_changed_value_message?
|
||||
message << "\n\n" + attribute_changed_value_message.call
|
||||
end
|
||||
|
||||
Shoulda::Matchers.word_wrap(message)
|
||||
positive_failure_message
|
||||
end
|
||||
|
||||
def failure_message_when_negated
|
||||
attribute_setter = result.attribute_setter
|
||||
|
||||
if attribute_setter.successfully_checked?
|
||||
validator = result.validator
|
||||
message = failure_message_preface.call + ' invalid'
|
||||
|
||||
if validator.type_of_message_matched?
|
||||
if validator.has_messages?
|
||||
message << ' and to'
|
||||
|
||||
if validator.captured_validation_exception?
|
||||
message << ' raise a validation exception with message'
|
||||
else
|
||||
message << ' produce'
|
||||
|
||||
if expected_message.is_a?(Regexp)
|
||||
message << ' a'
|
||||
else
|
||||
message << ' the'
|
||||
end
|
||||
|
||||
message << ' validation error'
|
||||
end
|
||||
|
||||
if expected_message.is_a?(Regexp)
|
||||
message << ' matching '
|
||||
message << Shoulda::Matchers::Util.inspect_value(
|
||||
expected_message
|
||||
)
|
||||
else
|
||||
message << " #{expected_message.inspect}"
|
||||
end
|
||||
|
||||
unless validator.captured_validation_exception?
|
||||
message << " on :#{attribute_to_check_message_against}"
|
||||
end
|
||||
|
||||
message << '. The record was indeed invalid, but'
|
||||
|
||||
if validator.captured_validation_exception?
|
||||
message << ' the exception message was '
|
||||
message << validator.validation_exception_message.inspect
|
||||
message << ' instead.'
|
||||
else
|
||||
message << " it produced these validation errors instead:\n\n"
|
||||
message << validator.all_formatted_validation_error_messages
|
||||
end
|
||||
else
|
||||
message << ', but it was valid instead.'
|
||||
end
|
||||
elsif validator.captured_validation_exception?
|
||||
message << ' and to produce validation errors, but the record'
|
||||
message << ' raised a validation exception instead.'
|
||||
else
|
||||
message << ' and to raise a validation exception, but the record'
|
||||
message << ' produced validation errors instead.'
|
||||
end
|
||||
else
|
||||
message = attribute_setter.failure_message
|
||||
end
|
||||
|
||||
if include_attribute_changed_value_message?
|
||||
message << "\n\n" + attribute_changed_value_message.call
|
||||
end
|
||||
|
||||
Shoulda::Matchers.word_wrap(message)
|
||||
end
|
||||
|
||||
def will_be_negated?
|
||||
@will_be_negated
|
||||
end
|
||||
|
||||
def was_negated?
|
||||
@was_negated
|
||||
end
|
||||
|
||||
def description
|
||||
ValidationMatcher::BuildDescription.call(self, simple_description)
|
||||
end
|
||||
|
||||
def simple_description
|
||||
"allow :#{attribute_to_set} to be #{inspected_values_to_set}"
|
||||
end
|
||||
|
||||
def model
|
||||
instance.class
|
||||
end
|
||||
|
||||
def last_attribute_setter_used
|
||||
result.attribute_setter
|
||||
end
|
||||
|
||||
def last_value_set
|
||||
last_attribute_setter_used.value_written
|
||||
end
|
||||
|
||||
def pretty_print(pp)
|
||||
Shoulda::Matchers::Util.pretty_print(self, pp, {
|
||||
will_be_negated: will_be_negated?,
|
||||
was_negated: was_negated?,
|
||||
attribute_to_set: attribute_to_set,
|
||||
attribute_to_check_message_against: attribute_to_check_message_against,
|
||||
values_to_set: values_to_set,
|
||||
expected_message: expected_message,
|
||||
expects_strict: expects_strict?,
|
||||
instance: instance,
|
||||
attribute_setters_and_validators_for_values_to_set: attribute_setters_and_validators_for_values_to_set,
|
||||
})
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader(
|
||||
:options,
|
||||
:result,
|
||||
:values_to_preset,
|
||||
:values_to_set,
|
||||
)
|
||||
|
||||
private
|
||||
|
||||
def run(strategy)
|
||||
attribute_setters_for_values_to_preset.first_to_unexpectedly_not_pass ||
|
||||
attribute_setters_and_validators_for_values_to_set.public_send(strategy)
|
||||
end
|
||||
|
||||
def failure_message_preface
|
||||
@failure_message_preface || method(:default_failure_message_preface)
|
||||
end
|
||||
|
||||
def default_failure_message_preface
|
||||
''.tap do |preface|
|
||||
if descriptions_for_preset_values.any?
|
||||
preface << 'After setting '
|
||||
preface << descriptions_for_preset_values.to_sentence
|
||||
preface << ', then '
|
||||
else
|
||||
preface << 'After '
|
||||
end
|
||||
|
||||
preface << 'setting '
|
||||
preface << description_for_resulting_attribute_setter
|
||||
|
||||
unless preface.end_with?('--')
|
||||
preface << ','
|
||||
end
|
||||
|
||||
preface << " the matcher expected the #{model.name} to be"
|
||||
end
|
||||
end
|
||||
|
||||
def include_attribute_changed_value_message?
|
||||
!ignore_interference_by_writer.never? &&
|
||||
result.attribute_setter.attribute_changed_value?
|
||||
end
|
||||
|
||||
def attribute_changed_value_message
|
||||
@attribute_changed_value_message ||
|
||||
method(:default_attribute_changed_value_message)
|
||||
end
|
||||
|
||||
def default_attribute_changed_value_message
|
||||
<<-MESSAGE.strip
|
||||
As indicated in the message above, :#{result.attribute_setter.attribute_name}
|
||||
seems to be changing certain values as they are set, and this could have
|
||||
something to do with why this test is failing. If you've overridden the writer
|
||||
method for this attribute, then you may need to change it to make this test
|
||||
pass, or do something else entirely.
|
||||
MESSAGE
|
||||
end
|
||||
|
||||
def descriptions_for_preset_values
|
||||
attribute_setters_for_values_to_preset.
|
||||
map(&:attribute_setter_description)
|
||||
end
|
||||
|
||||
def description_for_resulting_attribute_setter
|
||||
result.attribute_setter_description
|
||||
end
|
||||
|
||||
def attribute_setters_for_values_to_preset
|
||||
@_attribute_setters_for_values_to_preset ||=
|
||||
AttributeSetters.new(self, values_to_preset)
|
||||
end
|
||||
|
||||
def attribute_setters_and_validators_for_values_to_set
|
||||
@_attribute_setters_and_validators_for_values_to_set ||=
|
||||
AttributeSettersAndValidators.new(
|
||||
self,
|
||||
values_to_set.map { |value| [attribute_to_set, value] }
|
||||
)
|
||||
end
|
||||
|
||||
def inspected_values_to_set
|
||||
Shoulda::Matchers::Util.inspect_values(values_to_set).to_sentence(
|
||||
two_words_connector: " or ",
|
||||
last_word_connector: ", or "
|
||||
)
|
||||
end
|
||||
|
||||
def default_expected_message
|
||||
if expects_strict?
|
||||
"#{human_attribute_name} #{default_attribute_message}"
|
||||
else
|
||||
default_attribute_message
|
||||
end
|
||||
end
|
||||
|
||||
def default_attribute_message
|
||||
default_error_message(
|
||||
options[:expected_message],
|
||||
default_attribute_message_values
|
||||
)
|
||||
end
|
||||
|
||||
def default_attribute_message_values
|
||||
defaults = {
|
||||
model_name: model_name,
|
||||
instance: instance,
|
||||
attribute: attribute_to_check_message_against,
|
||||
}
|
||||
|
||||
defaults.merge(options[:expected_message_values])
|
||||
end
|
||||
|
||||
def model_name
|
||||
instance.class.to_s.underscore
|
||||
end
|
||||
|
||||
def human_attribute_name
|
||||
instance.class.human_attribute_name(
|
||||
attribute_to_check_message_against
|
||||
)
|
||||
negative_failure_message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,87 +1,34 @@
|
|||
require 'forwardable'
|
||||
|
||||
module Shoulda
|
||||
module Matchers
|
||||
module ActiveModel
|
||||
# @private
|
||||
class DisallowValueMatcher
|
||||
extend Forwardable
|
||||
|
||||
def_delegators(
|
||||
:allow_matcher,
|
||||
:_after_setting_value,
|
||||
:attribute_changed_value_message=,
|
||||
:attribute_to_set,
|
||||
# :description,
|
||||
:expects_strict?,
|
||||
:failure_message_preface,
|
||||
:failure_message_preface=,
|
||||
:ignore_interference_by_writer,
|
||||
:last_attribute_setter_used,
|
||||
:last_value_set,
|
||||
:model,
|
||||
# :simple_description,
|
||||
:values_to_preset=,
|
||||
)
|
||||
|
||||
def initialize(value)
|
||||
@allow_matcher = AllowValueMatcher.new(value, will_be_negated: true)
|
||||
end
|
||||
|
||||
def description
|
||||
ValidationMatcher::BuildDescription.call(self, simple_description)
|
||||
end
|
||||
|
||||
class DisallowValueMatcher < AllowOrDisallowValueMatcher
|
||||
def simple_description
|
||||
"not #{allow_matcher.simple_description}"
|
||||
"fail validation when :#{attribute_to_set} is set to " +
|
||||
"#{inspected_values_to_set}"
|
||||
end
|
||||
|
||||
def matches?(subject)
|
||||
allow_matcher.does_not_match?(subject)
|
||||
# !allow_matcher.matches?(subject)
|
||||
super(subject)
|
||||
|
||||
@result = run(:first_to_unexpectedly_not_fail)
|
||||
@result.nil?
|
||||
end
|
||||
|
||||
def does_not_match?(subject)
|
||||
allow_matcher.matches?(subject)
|
||||
# !allow_matcher.does_not_match?(subject)
|
||||
end
|
||||
# def does_not_match?(subject)
|
||||
# super(subject)
|
||||
|
||||
def for(attribute)
|
||||
allow_matcher.for(attribute)
|
||||
self
|
||||
end
|
||||
|
||||
def on(context)
|
||||
allow_matcher.on(context)
|
||||
self
|
||||
end
|
||||
|
||||
def with_message(message, options={})
|
||||
allow_matcher.with_message(message, options)
|
||||
self
|
||||
end
|
||||
|
||||
def strict(strict = true)
|
||||
allow_matcher.strict(strict)
|
||||
self
|
||||
end
|
||||
|
||||
def ignoring_interference_by_writer(value = :always)
|
||||
allow_matcher.ignoring_interference_by_writer(value)
|
||||
self
|
||||
end
|
||||
# @result = run(:first_to_unexpectedly_not_pass)
|
||||
# !@result.nil?
|
||||
# end
|
||||
|
||||
def failure_message
|
||||
allow_matcher.failure_message_when_negated
|
||||
negative_failure_message
|
||||
end
|
||||
|
||||
def failure_message_when_negated
|
||||
allow_matcher.failure_message
|
||||
positive_failure_message
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :allow_matcher
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ module Shoulda
|
|||
|
||||
def default_error_message(type, options = {})
|
||||
model_name = options.delete(:model_name)
|
||||
attribute = options.delete(:attribute)
|
||||
attribute = options.fetch(:attribute)
|
||||
instance = options.delete(:instance)
|
||||
|
||||
RailsShim.generate_validation_message(
|
||||
|
|
|
@ -79,21 +79,24 @@ module Shoulda
|
|||
# @private
|
||||
class ValidateAbsenceOfMatcher < ValidationMatcher
|
||||
def initialize(attribute)
|
||||
super
|
||||
super(attribute)
|
||||
@expected_message = :present
|
||||
end
|
||||
|
||||
def matches?(subject)
|
||||
super(subject)
|
||||
disallows_value_of(value, @expected_message)
|
||||
def simple_description
|
||||
"fail validation when :#{attribute} is empty/falsy"
|
||||
end
|
||||
|
||||
def simple_description
|
||||
"validate that :#{@attribute} is empty/falsy"
|
||||
protected
|
||||
|
||||
def add_submatchers
|
||||
add_matcher_disallowing([value], expected_message)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :expected_message
|
||||
|
||||
def value
|
||||
if reflection
|
||||
obj = reflection.klass.new
|
||||
|
@ -115,9 +118,9 @@ module Shoulda
|
|||
end
|
||||
|
||||
def column_type
|
||||
@subject.class.respond_to?(:columns_hash) &&
|
||||
@subject.class.columns_hash[@attribute.to_s].respond_to?(:type) &&
|
||||
@subject.class.columns_hash[@attribute.to_s].type
|
||||
subject.class.respond_to?(:columns_hash) &&
|
||||
subject.class.columns_hash[attribute.to_s].respond_to?(:type) &&
|
||||
subject.class.columns_hash[attribute.to_s].type
|
||||
end
|
||||
|
||||
def collection?
|
||||
|
@ -129,8 +132,8 @@ module Shoulda
|
|||
end
|
||||
|
||||
def reflection
|
||||
@subject.class.respond_to?(:reflect_on_association) &&
|
||||
@subject.class.reflect_on_association(@attribute)
|
||||
subject.class.respond_to?(:reflect_on_association) &&
|
||||
subject.class.reflect_on_association(attribute)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -375,15 +375,13 @@ EOT
|
|||
end
|
||||
|
||||
def matches?(subject)
|
||||
super(subject)
|
||||
|
||||
if @array
|
||||
add_submatchers_to_test_array
|
||||
elsif @range
|
||||
add_submatchers_to_test_range
|
||||
end
|
||||
|
||||
all_submatchers_match?
|
||||
super(subject)
|
||||
end
|
||||
|
||||
def pretty_print(pp)
|
||||
|
|
|
@ -13,6 +13,7 @@ module Shoulda
|
|||
@submatchers = []
|
||||
@expected_message = nil
|
||||
@expects_custom_validation_message = false
|
||||
@_submatchers_added = false
|
||||
@was_negated = nil
|
||||
end
|
||||
|
||||
|
@ -49,14 +50,23 @@ module Shoulda
|
|||
|
||||
def matches?(subject)
|
||||
@subject = subject
|
||||
@was_negated = false
|
||||
false
|
||||
|
||||
if !@_submatchers_added
|
||||
add_submatchers
|
||||
@_submatchers_added = true
|
||||
end
|
||||
|
||||
all_submatchers_match?.tap do
|
||||
@was_negated = false
|
||||
end
|
||||
end
|
||||
|
||||
def does_not_match?(subject)
|
||||
@subject = subject
|
||||
!matches?(subject)
|
||||
@was_negated = true
|
||||
|
||||
!matches?(subject).tap do
|
||||
@was_negated = true
|
||||
end
|
||||
end
|
||||
|
||||
def failure_message
|
||||
|
@ -116,6 +126,9 @@ module Shoulda
|
|||
subject.class
|
||||
end
|
||||
|
||||
def add_submatchers
|
||||
end
|
||||
|
||||
def add_submatcher(submatcher)
|
||||
submatchers << submatcher
|
||||
end
|
||||
|
@ -132,7 +145,7 @@ module Shoulda
|
|||
end
|
||||
alias_method :add_matcher_allowing, :allows_value_of
|
||||
|
||||
def disallows_value_of(value_or_values, message: nil, &block)
|
||||
def disallows_value_of(value_or_values, message = nil, &block)
|
||||
matcher =
|
||||
if value_or_values.is_a?(Array)
|
||||
disallow_value_matcher(*value_or_values, message: message, &block)
|
||||
|
|
|
@ -44,11 +44,9 @@ module Shoulda
|
|||
if matcher.try(:expects_custom_validation_message?)
|
||||
description_clauses.last << ' with a custom message'
|
||||
end
|
||||
|
||||
description_clauses.last << ' on failure'
|
||||
elsif matcher.try(:expects_custom_validation_message?)
|
||||
description_clauses <<
|
||||
'producing a custom validation error on failure'
|
||||
'producing a custom validation error'
|
||||
end
|
||||
|
||||
description_clauses
|
||||
|
|
|
@ -41,6 +41,10 @@ module Shoulda
|
|||
expects_strict? == captured_validation_exception?
|
||||
end
|
||||
|
||||
def has_matching_validation_messages?
|
||||
matched_validation_messages.compact.any?
|
||||
end
|
||||
|
||||
def all_formatted_validation_error_messages
|
||||
format_validation_errors(all_validation_errors)
|
||||
end
|
||||
|
@ -100,10 +104,7 @@ module Shoulda
|
|||
end
|
||||
|
||||
def validation_messages_match?
|
||||
validation_message_type_matches? && (
|
||||
validation_messages.none? ||
|
||||
matched_validation_messages.compact.any?
|
||||
)
|
||||
validation_message_type_matches? && has_matching_validation_messages?
|
||||
end
|
||||
|
||||
def validation_messages
|
||||
|
|
|
@ -144,7 +144,7 @@ module Shoulda
|
|||
]
|
||||
translate_options =
|
||||
{ default: default_translation_keys }.merge(options)
|
||||
I18n.translate(primary_translation_key, translate_options)
|
||||
I18n.translate(primary_translation_key.join('.'), translate_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -79,7 +79,7 @@ module UnitTests
|
|||
overrides[:changing_values_with]
|
||||
)
|
||||
|
||||
if respond_to?(:write_attribute)
|
||||
if respond_to?(:write_attribute) && attribute_names.include?(attribute_name)
|
||||
write_attribute(attribute_name, new_value)
|
||||
else
|
||||
super(new_value)
|
||||
|
|
|
@ -57,9 +57,16 @@ describe Shoulda::Matchers::ActiveModel::ValidateAbsenceOfMatcher, type: :model
|
|||
record = define_model(:example, attr: :string).new
|
||||
|
||||
message = <<-MESSAGE
|
||||
Example did not properly validate that :attr is empty/falsy.
|
||||
After setting :attr to ‹"an arbitrary value"›, the matcher expected
|
||||
the Example to be invalid, but it was valid instead.
|
||||
Expected Example to fail validation when :attr is empty/falsy, but there
|
||||
were some issues.
|
||||
|
||||
All of these submatchers should have passed:
|
||||
|
||||
✘ should fail validation when :attr is set to ‹"an arbitrary value"›,
|
||||
producing a custom validation error
|
||||
|
||||
After setting :attr to ‹"an arbitrary value"›, the matcher expected
|
||||
the Example to be invalid, but it was valid instead.
|
||||
MESSAGE
|
||||
|
||||
assertion = lambda do
|
||||
|
@ -96,9 +103,16 @@ the Example to be invalid, but it was valid instead.
|
|||
context 'an ActiveModel class without an absence validation' do
|
||||
it 'rejects with the correct failure message' do
|
||||
message = <<-MESSAGE
|
||||
Example did not properly validate that :attr is empty/falsy.
|
||||
After setting :attr to ‹"an arbitrary value"›, the matcher expected
|
||||
the Example to be invalid, but it was valid instead.
|
||||
Expected Example to fail validation when :attr is empty/falsy, but there
|
||||
were some issues.
|
||||
|
||||
All of these submatchers should have passed:
|
||||
|
||||
✘ should fail validation when :attr is set to ‹"an arbitrary value"›,
|
||||
producing a custom validation error
|
||||
|
||||
After setting :attr to ‹"an arbitrary value"›, the matcher expected
|
||||
the Example to be invalid, but it was valid instead.
|
||||
MESSAGE
|
||||
|
||||
assertion = lambda do
|
||||
|
@ -160,9 +174,16 @@ the Example to be invalid, but it was valid instead.
|
|||
model = having_and_belonging_to_many(:children, absence: false)
|
||||
|
||||
message = <<-MESSAGE
|
||||
Parent did not properly validate that :children is empty/falsy.
|
||||
After setting :children to ‹[#<Child id: nil>]›, the matcher expected
|
||||
the Parent to be invalid, but it was valid instead.
|
||||
Expected Parent to fail validation when :children is empty/falsy, but
|
||||
there were some issues.
|
||||
|
||||
All of these submatchers should have passed:
|
||||
|
||||
✘ should fail validation when :children is set to ‹[#<Child id: nil>]›,
|
||||
producing a custom validation error
|
||||
|
||||
After setting :children to ‹[#<Child id: nil>]›, the matcher expected
|
||||
the Parent to be invalid, but it was valid instead.
|
||||
MESSAGE
|
||||
|
||||
assertion = lambda do
|
||||
|
|
Loading…
Reference in New Issue