2010-12-15 22:34:19 +00:00
|
|
|
module Shoulda # :nodoc:
|
|
|
|
module Matchers
|
2011-05-06 11:51:12 +00:00
|
|
|
module ActiveModel # :nodoc:
|
2012-06-04 15:56:47 +00:00
|
|
|
# Ensure that the attribute is numeric.
|
2010-12-15 22:34:19 +00:00
|
|
|
#
|
|
|
|
# Options:
|
|
|
|
# * <tt>with_message</tt> - value the test expects to find in
|
|
|
|
# <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
|
|
|
|
# translation for <tt>:not_a_number</tt>.
|
2011-10-27 02:21:06 +00:00
|
|
|
# * <tt>only_integer</tt> - allows only integer values
|
2013-03-19 04:50:39 +00:00
|
|
|
# * <tt>odd</tt> - Specifies the value must be an odd number.
|
|
|
|
# * <tt>even</tt> - Specifies the value must be an even number.
|
2010-12-15 22:34:19 +00:00
|
|
|
#
|
2011-10-27 02:21:06 +00:00
|
|
|
# Examples:
|
|
|
|
# it { should validate_numericality_of(:price) }
|
|
|
|
# it { should validate_numericality_of(:age).only_integer }
|
2013-03-19 04:50:39 +00:00
|
|
|
# it { should validate_numericality_of(:frequency).odd }
|
|
|
|
# it { should validate_numericality_of(:frequency).even }
|
2010-12-15 22:34:19 +00:00
|
|
|
#
|
|
|
|
def validate_numericality_of(attr)
|
|
|
|
ValidateNumericalityOfMatcher.new(attr)
|
|
|
|
end
|
|
|
|
|
2012-10-16 17:45:06 +00:00
|
|
|
class ValidateNumericalityOfMatcher
|
2014-01-13 01:43:36 +00:00
|
|
|
NUMERIC_NAME = 'numbers'
|
2012-10-23 16:30:18 +00:00
|
|
|
NON_NUMERIC_VALUE = 'abcd'
|
|
|
|
|
2012-04-24 22:00:26 +00:00
|
|
|
def initialize(attribute)
|
2012-10-16 17:45:06 +00:00
|
|
|
@attribute = attribute
|
|
|
|
@submatchers = []
|
|
|
|
|
|
|
|
add_disallow_value_matcher
|
2012-04-24 22:00:26 +00:00
|
|
|
end
|
2011-10-27 02:21:06 +00:00
|
|
|
|
|
|
|
def only_integer
|
2013-11-01 21:57:42 +00:00
|
|
|
add_submatcher(NumericalityMatchers::OnlyIntegerMatcher.new(@attribute))
|
2013-02-19 10:09:13 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def is_greater_than(value)
|
2013-11-01 21:57:42 +00:00
|
|
|
add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :>).for(@attribute))
|
2013-02-19 10:09:13 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def is_greater_than_or_equal_to(value)
|
2013-11-01 21:57:42 +00:00
|
|
|
add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :>=).for(@attribute))
|
2013-02-19 10:09:13 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def is_equal_to(value)
|
2013-11-01 21:57:42 +00:00
|
|
|
add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :==).for(@attribute))
|
2013-02-19 10:09:13 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def is_less_than(value)
|
2013-11-01 21:57:42 +00:00
|
|
|
add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :<).for(@attribute))
|
2013-02-19 10:09:13 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def is_less_than_or_equal_to(value)
|
2013-11-01 21:57:42 +00:00
|
|
|
add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :<=).for(@attribute))
|
2011-10-27 02:21:06 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2013-03-19 04:50:39 +00:00
|
|
|
def odd
|
2014-01-13 01:43:36 +00:00
|
|
|
odd_number_matcher = NumericalityMatchers::OddNumberMatcher.new(@attribute)
|
2013-03-19 04:50:39 +00:00
|
|
|
add_submatcher(odd_number_matcher)
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def even
|
2014-01-13 01:43:36 +00:00
|
|
|
even_number_matcher = NumericalityMatchers::EvenNumberMatcher.new(@attribute)
|
2013-03-19 04:50:39 +00:00
|
|
|
add_submatcher(even_number_matcher)
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2010-12-15 22:34:19 +00:00
|
|
|
def with_message(message)
|
2012-10-23 16:30:18 +00:00
|
|
|
@submatchers.each { |matcher| matcher.with_message(message) }
|
2010-12-15 22:34:19 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def matches?(subject)
|
2012-10-16 17:45:06 +00:00
|
|
|
@subject = subject
|
|
|
|
submatchers_match?
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def description
|
2014-01-13 01:43:36 +00:00
|
|
|
"only allow #{allowed_types} for #{@attribute}#{comparison_descriptions}"
|
2012-10-16 17:45:06 +00:00
|
|
|
end
|
|
|
|
|
2013-12-24 11:24:27 +00:00
|
|
|
def failure_message
|
2013-04-12 17:11:07 +00:00
|
|
|
submatcher_failure_messages_for_should.last
|
|
|
|
end
|
2013-12-24 11:24:27 +00:00
|
|
|
alias failure_message_for_should failure_message
|
2013-04-12 17:11:07 +00:00
|
|
|
|
2013-12-24 11:24:27 +00:00
|
|
|
def failure_message_when_negated
|
2013-04-12 17:11:07 +00:00
|
|
|
submatcher_failure_messages_for_should_not.last
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
2013-12-24 11:24:27 +00:00
|
|
|
alias failure_message_for_should_not failure_message_when_negated
|
2010-12-15 22:34:19 +00:00
|
|
|
|
2012-03-30 14:36:55 +00:00
|
|
|
private
|
|
|
|
|
2012-10-16 17:45:06 +00:00
|
|
|
def add_disallow_value_matcher
|
2012-10-23 16:30:18 +00:00
|
|
|
disallow_value_matcher = DisallowValueMatcher.new(NON_NUMERIC_VALUE).
|
|
|
|
for(@attribute).
|
|
|
|
with_message(:not_a_number)
|
|
|
|
|
|
|
|
add_submatcher(disallow_value_matcher)
|
2012-04-04 00:20:50 +00:00
|
|
|
end
|
|
|
|
|
2012-10-16 17:45:06 +00:00
|
|
|
def add_submatcher(submatcher)
|
|
|
|
@submatchers << submatcher
|
2011-10-27 02:21:06 +00:00
|
|
|
end
|
|
|
|
|
2012-10-23 16:30:18 +00:00
|
|
|
def submatchers_match?
|
|
|
|
failing_submatchers.empty?
|
2012-10-16 17:45:06 +00:00
|
|
|
end
|
|
|
|
|
2013-04-12 17:11:07 +00:00
|
|
|
def submatcher_failure_messages_for_should
|
2013-12-24 11:24:27 +00:00
|
|
|
failing_submatchers.map(&:failure_message)
|
2012-10-23 16:30:18 +00:00
|
|
|
end
|
|
|
|
|
2013-04-12 17:11:07 +00:00
|
|
|
def submatcher_failure_messages_for_should_not
|
2013-12-24 11:24:27 +00:00
|
|
|
failing_submatchers.map(&:failure_message_when_negated)
|
2013-04-12 17:11:07 +00:00
|
|
|
end
|
|
|
|
|
2012-10-23 16:30:18 +00:00
|
|
|
def failing_submatchers
|
2013-07-10 17:05:41 +00:00
|
|
|
@failing_submatchers ||= @submatchers.select { |matcher| !matcher.matches?(@subject) }
|
2012-10-16 17:45:06 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def allowed_types
|
2014-01-13 01:43:36 +00:00
|
|
|
allowed_array = submatcher_allowed_types
|
|
|
|
allowed_array.empty? ? NUMERIC_NAME : allowed_array.join(', ')
|
2012-10-16 17:45:06 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def submatcher_allowed_types
|
2014-01-13 01:43:36 +00:00
|
|
|
@submatchers.inject([]){|m, s| m << s.allowed_type if s.respond_to?(:allowed_type); m }
|
|
|
|
end
|
|
|
|
|
|
|
|
def comparison_descriptions
|
|
|
|
description_array = submatcher_comparison_descriptions
|
|
|
|
description_array.empty? ? '' : ' which are ' + submatcher_comparison_descriptions.join(' and ')
|
|
|
|
end
|
|
|
|
|
|
|
|
def submatcher_comparison_descriptions
|
|
|
|
@submatchers.inject([]){|m, s| m << s.comparison_description if s.respond_to?(:comparison_description); m }
|
2012-03-30 14:36:55 +00:00
|
|
|
end
|
|
|
|
end
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|