Skip to content

Commit

Permalink
Add the qualifier allow_blank to validate_length_of (#1318)
Browse files Browse the repository at this point in the history
Co-authored-by: Elliot Winkler <elliot.winkler@gmail.com>
  • Loading branch information
mcmire committed Jul 11, 2020
1 parent 421a49d commit a4289e9
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 24 deletions.
23 changes: 2 additions & 21 deletions lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb
Expand Up @@ -311,15 +311,6 @@ def in_range(range)
self
end

def allow_blank
@options[:allow_blank] = true
self
end

def expects_to_allow_blank?
@options[:allow_blank]
end

def allow_nil
@options[:allow_nil] = true
self
Expand Down Expand Up @@ -423,14 +414,14 @@ def matches_for_array?
allows_all_values_in_array? &&
disallows_all_values_outside_of_array? &&
allows_nil_value? &&
allows_blank_value?
allow_blank_matches?
end

def does_not_match_for_array?
disallows_any_values_in_array? ||
allows_any_value_outside_of_array? ||
disallows_nil_value? ||
disallows_blank_value?
allow_blank_does_not_match?
end

def allows_lower_value
Expand Down Expand Up @@ -616,16 +607,6 @@ def disallows_nil_value?
@options[:allow_nil] && disallows_value_of(nil)
end

def allows_blank_value?
@options[:allow_blank] != true ||
BLANK_VALUES.all? { |value| allows_value_of(value) }
end

def disallows_blank_value?
@options[:allow_blank] &&
BLANK_VALUES.any? { |value| disallows_value_of(value) }
end

def inspected_array
Shoulda::Matchers::Util.inspect_values(@array).to_sentence(
two_words_connector: " or ",
Expand Down
30 changes: 27 additions & 3 deletions lib/shoulda/matchers/active_model/validate_length_of_matcher.rb
Expand Up @@ -232,13 +232,34 @@ module ActiveModel
# it { should validate_length_of(:bio).is_at_least(15).allow_nil }
# end
#
# # Test::Unit
# # Minitest (Shoulda)
# class UserTest < ActiveSupport::TestCase
# should validate_length_of(:bio).is_at_least(15).allow_nil
# end
#
# @return [ValidateLengthOfMatcher]
#
# # ##### allow_blank
#
# Use `allow_blank` to assert that the attribute allows blank.
#
# class User
# include ActiveModel::Model
# attr_accessor :bio
#
# validates_length_of :bio, minimum: 15, allow_blank: true
# end
#
# # RSpec
# describe User do
# it { should validate_length_of(:bio).is_at_least(15).allow_blank }
# end
#
# # Minitest (Shoulda)
# class UserTest < ActiveSupport::TestCase
# should validate_length_of(:bio).is_at_least(15).allow_blank
# end
#
def validate_length_of(attr)
ValidateLengthOfMatcher.new(attr)
end
Expand Down Expand Up @@ -331,15 +352,17 @@ def matches?(subject)

lower_bound_matches? &&
upper_bound_matches? &&
allow_nil_matches?
allow_nil_matches? &&
allow_blank_matches?
end

def does_not_match?(subject)
super(subject)

lower_bound_does_not_match? ||
upper_bound_does_not_match? ||
allow_nil_does_not_match?
allow_nil_does_not_match? ||
allow_blank_does_not_match?
end

private
Expand Down Expand Up @@ -376,6 +399,7 @@ def allows_lower_length?
def disallows_lower_length?
!@options.key?(:minimum) ||
@options[:minimum] == 0 ||
(@options[:minimum] == 1 && expects_to_allow_blank?) ||
disallows_length_of?(
@options[:minimum] - 1,
translated_short_message
Expand Down
25 changes: 25 additions & 0 deletions lib/shoulda/matchers/active_model/validation_matcher.rb
Expand Up @@ -24,6 +24,11 @@ def on(context)
self
end

def allow_blank
options[:allow_blank] = true
self
end

def strict
@expects_strict = true
self
Expand Down Expand Up @@ -116,8 +121,20 @@ def disallow_value_matcher(value, message = nil, &block)
)
end

def allow_blank_matches?
!expects_to_allow_blank? ||
blank_values.all? { |value| allows_value_of(value) }
end

def allow_blank_does_not_match?
expects_to_allow_blank? &&
blank_values.all? { |value| disallows_value_of(value) }
end

private

attr_reader :options

def overall_failure_message
Shoulda::Matchers.word_wrap(
"Expected #{model.name} to #{description}, but this could not be " +
Expand Down Expand Up @@ -161,6 +178,14 @@ def run_allow_or_disallow_matcher(matcher)
@last_submatcher_run = matcher
matcher.matches?(subject)
end

def expects_to_allow_blank?
options[:allow_blank]
end

def blank_values
['', ' ', "\n", "\r", "\t", "\f"]
end
end
end
end
Expand Down
Expand Up @@ -311,6 +311,62 @@ def configure_validation_matcher(matcher)
end
end

context 'qualified with allow_blank' do
context 'and validating with allow_blank' do
context 'with minimum' do
context 'and minimum is 1' do
it 'accepts' do
expect(validating_length(minimum: 1, allow_blank: true)).
to validate_length_of(:attr).is_at_least(1).allow_blank
end
end

context 'and minimum is greater than 1' do
it 'accepts' do
expect(validating_length(minimum: 2, allow_blank: true)).
to validate_length_of(:attr).is_at_least(2).allow_blank
end
end
end

context 'with maximum' do
context 'and maximum is 0' do
it 'accepts' do
expect(validating_length(maximum: 0, allow_blank: true)).
to validate_length_of(:attr).is_at_most(0).allow_blank
end
end

context 'and maximum is greater than 0' do
it 'accepts' do
expect(validating_length(maximum: 1, allow_blank: true)).
to validate_length_of(:attr).is_at_most(1).allow_blank
end
end
end
end

context 'and not validating with allow_blank' do
it 'rejects' do
assertion = lambda do
expect(validating_length(minimum: 1)).
to validate_length_of(:attr).is_at_least(1).allow_blank
end

message = <<-MESSAGE
Expected Example to validate that the length of :attr is at least 1, but
this could not be proved.
After setting :attr to ‹""›, the matcher expected the Example to be
valid, but it was invalid instead, producing these validation errors:
* attr: ["is too short (minimum is 1 character)"]
MESSAGE

expect(&assertion).to fail_with_message(message)
end
end
end

def define_model_validating_length(options = {})
options = options.dup
attribute_name = options.delete(:attribute_name) { :attr }
Expand Down

0 comments on commit a4289e9

Please sign in to comment.