module Shoulda # :nodoc:
module ActiveRecord # :nodoc:
module Matchers
# Ensures that the length of the attribute is validated.
#
# Options:
# * is_at_least - minimum length of this attribute
# * is_at_most - maximum length of this attribute
# * is_equal_to - exact requred length of this attribute
# * with_short_message - value the test expects to find in
# errors.on(:attribute). Regexp or string. Defaults to the
# translation for :too_short.
# * with_long_message - value the test expects to find in
# errors.on(:attribute). Regexp or string. Defaults to the
# translation for :too_long.
# * with_message - value the test expects to find in
# errors.on(:attribute). Regexp or string. Defaults to the
# translation for :wrong_length. Used in conjunction with
# is_equal_to.
#
# Examples:
# it { should ensure_length_of(:password).
# is_at_least(6).
# is_at_most(20) }
# it { should ensure_length_of(:name).
# is_at_least(3).
# with_short_message(/not long enough/) }
# it { should ensure_length_of(:ssn).
# is_equal_to(9).
# with_message(/is invalid/) }
def ensure_length_of(attr)
EnsureLengthOfMatcher.new(attr)
end
class EnsureLengthOfMatcher < ValidationMatcher # :nodoc:
include Helpers
def is_at_least(length)
@minimum = length
@short_message ||= :too_short
self
end
def is_at_most(length)
@maximum = length
@long_message ||= :too_long
self
end
def is_equal_to(length)
@minimum = length
@maximum = length
@short_message ||= :wrong_length
self
end
def with_short_message(message)
@short_message = message if message
self
end
alias_method :with_message, :with_short_message
def with_long_message(message)
@long_message = message if message
self
end
def description
description = "ensure #{@attribute} has a length "
if @minimum && @maximum
if @minimum == @maximum
description << "of exactly #{@minimum}"
else
description << "between #{@minimum} and #{@maximum}"
end
else
description << "of at least #{@minimum}" if @minimum
description << "of at most #{@maximum}" if @maximum
end
description
end
def matches?(subject)
super(subject)
translate_messages!
disallows_lower_length &&
allows_minimum_length &&
((@minimum == @maximum) ||
(disallows_higher_length &&
allows_maximum_length))
end
private
def translate_messages!
if Symbol === @short_message
@short_message = default_error_message(@short_message,
:count => @minimum)
end
if Symbol === @long_message
@long_message = default_error_message(@long_message,
:count => @maximum)
end
end
def disallows_lower_length
@minimum == 0 ||
@minimum.nil? ||
disallows_length_of(@minimum - 1, @short_message)
end
def disallows_higher_length
@maximum.nil? || disallows_length_of(@maximum + 1, @long_message)
end
def allows_minimum_length
allows_length_of(@minimum, @short_message)
end
def allows_maximum_length
allows_length_of(@maximum, @long_message)
end
def allows_length_of(length, message)
length.nil? || allows_value_of(string_of_length(length), message)
end
def disallows_length_of(length, message)
length.nil? || disallows_value_of(string_of_length(length), message)
end
def string_of_length(length)
'x' * length
end
end
end
end
end