Refactor inclusion & exclusion matchers

This is part of a collection of commits that aim to improve failure
messages across the board, in order to make matchers easier to debug
when something goes wrong.

* Make the description of the matcher more readable.
* Add boolean methods to check whether `allow_nil` or `allow_blank` have
  been specified, to conform to the interface that ValidationMatcher
  introduces.
* Fix or fill in tests involving failure messages and descriptions to
  match these changes and recent changes to ValidationMatcher and
  allow_value.
This commit is contained in:
Elliot Winkler 2015-12-13 16:47:59 -07:00
parent cecccc6836
commit 2e35b3730e
4 changed files with 138 additions and 24 deletions

View File

@ -122,9 +122,9 @@ module Shoulda
class ValidateExclusionOfMatcher < ValidationMatcher
def initialize(attribute)
super(attribute)
@expected_message = :exclusion
@array = nil
@range = nil
@expected_message = nil
end
def in_array(array)
@ -139,13 +139,23 @@ module Shoulda
self
end
def with_message(message)
@expected_message = message if message
self
end
def simple_description
if @range
"validate that :#{@attribute} lies outside the range #{@range.inspect}"
else
description = "validate that :#{@attribute}"
def description
"ensure exclusion of #{@attribute} in #{inspect_message}"
if @array.many?
description << " is neither " + @array.map(&:inspect).to_sentence(
two_words_connector: " nor ",
last_word_connector: ", nor "
)
else
description << " is not #{@array.first.inspect}"
end
description
end
end
def matches?(subject)
@ -165,28 +175,24 @@ module Shoulda
def disallows_all_values_in_array?
@array.all? do |value|
disallows_value_of(value, expected_message)
disallows_value_of(value, @expected_message)
end
end
def allows_lower_value
@minimum == 0 || allows_value_of(@minimum - 1, expected_message)
@minimum == 0 || allows_value_of(@minimum - 1, @expected_message)
end
def allows_higher_value
allows_value_of(@maximum + 1, expected_message)
allows_value_of(@maximum + 1, @expected_message)
end
def disallows_minimum_value
disallows_value_of(@minimum, expected_message)
disallows_value_of(@minimum, @expected_message)
end
def disallows_maximum_value
disallows_value_of(@maximum, expected_message)
end
def expected_message
@expected_message || :exclusion
disallows_value_of(@maximum, @expected_message)
end
def inspect_message

View File

@ -310,31 +310,63 @@ EOT
self
end
def expects_to_allow_blank?
@options[:allow_blank]
end
def allow_nil(allow_nil = true)
@options[:allow_nil] = allow_nil
self
end
def expects_to_allow_nil?
@options[:allow_nil]
end
def with_message(message)
if message
@expects_custom_validation_message = true
@low_message = message
@high_message = message
end
self
end
def with_low_message(message)
@low_message = message if message
if message
@expects_custom_validation_message = true
@low_message = message
end
self
end
def with_high_message(message)
@high_message = message if message
if message
@high_message = message
end
self
end
def description
"ensure inclusion of #{@attribute} in #{inspect_message}"
def simple_description
if @range
"validate that :#{@attribute} lies inside the range #{@range.inspect}"
else
description = "validate that :#{@attribute}"
if @array.many?
description << " is either " + @array.map(&:inspect).to_sentence(
two_words_connector: " or ",
last_word_connector: ", or "
)
else
description << " is #{@array.first.inspect}"
end
description
end
end
def matches?(subject)
@ -398,7 +430,9 @@ EOT
end
def disallows_lower_value
@minimum == 0 || disallows_value_of(@minimum - 1, @low_message)
@minimum.nil? ||
@minimum == 0 ||
disallows_value_of(@minimum - 1, @low_message)
end
def disallows_higher_value

View File

@ -57,6 +57,14 @@ describe Shoulda::Matchers::ActiveModel::ValidateExclusionOfMatcher, type: :mode
expect(model).to validate_exclusion_of(:attr).in_range(2...5).
with_message(/should be out of this range/)
end
it 'has correct description' do
matcher = validate_exclusion_of(:attr).in_range(1..10)
expect(matcher.description).to eq(
'validate that :attr lies outside the range 1..10'
)
end
end
context 'an attribute which must be excluded from an array' do
@ -75,9 +83,31 @@ describe Shoulda::Matchers::ActiveModel::ValidateExclusionOfMatcher, type: :mode
not_to validate_exclusion_of(:attr).in_array(%w(cat dog))
end
it 'has correct description' do
expect(validate_exclusion_of(:attr).in_array([true, 'dog']).description).
to eq 'ensure exclusion of attr in [true, "dog"]'
context 'when there is one value' do
it 'has correct description' do
expect(validate_exclusion_of(:attr).in_array([true]).description).
to eq 'validate that :attr is not true'
end
end
context 'when there are two values' do
it 'has correct description' do
matcher = validate_exclusion_of(:attr).in_array([true, 'dog'])
expect(matcher.description).to eq(
'validate that :attr is neither true nor "dog"'
)
end
end
context 'when there are three or more values' do
it 'has correct description' do
matcher = validate_exclusion_of(:attr).in_array([true, 'dog', 'cat'])
expect(matcher.description).to eq(
'validate that :attr is neither true, "dog", nor "cat"'
)
end
end
def validating_exclusion(options)

View File

@ -679,6 +679,50 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
end
end
describe '#description' do
context 'given an array of values' do
context 'when there is one value' do
it 'returns the correct string' do
matcher = validate_inclusion_of(:attr).in_array([true])
expect(matcher.description).to eq(
'validate that :attr is true'
)
end
end
context 'when there are two values' do
it 'returns the correct string' do
matcher = validate_inclusion_of(:attr).in_array([true, 'dog'])
expect(matcher.description).to eq(
'validate that :attr is either true or "dog"'
)
end
end
context 'when there are three or more values' do
it 'returns the correct string' do
matcher = validate_inclusion_of(:attr).in_array([true, 'dog', 'cat'])
expect(matcher.description).to eq(
'validate that :attr is either true, "dog", or "cat"'
)
end
end
end
context 'given a range of values' do
it 'returns the correct string' do
matcher = validate_inclusion_of(:attr).in_range(1..10)
expect(matcher.description).to eq(
'validate that :attr lies inside the range 1..10'
)
end
end
end
def object_builder_class
@_object_builder_class ||= Struct.new(:attribute, :object, :validation_options)
end