Merge branch 'between'

This commit is contained in:
Adam Meehan 2009-01-01 20:30:22 +11:00
commit a7c6e37333
6 changed files with 283 additions and 133 deletions

View File

@ -64,6 +64,7 @@ the valid range of dates or times allowed
:on_or_before - Attribute must be equal to or before this value to be valid :on_or_before - Attribute must be equal to or before this value to be valid
:after - Attribute must be after this value to be valid :after - Attribute must be after this value to be valid
:on_or_after - Attribute must be equal to or after this value to be valid :on_or_after - Attribute must be equal to or after this value to be valid
:between - Attribute must be between the values to be valid
Regular validation options: Regular validation options:
:allow_nil - Allow a nil value to be valid :allow_nil - Allow a nil value to be valid
@ -79,6 +80,7 @@ the valid range of dates or times allowed
:on_or_before_message :on_or_before_message
:after_message :after_message
:on_or_after_message :on_or_after_message
:between_message
The temporal restrictions can take 4 different value types: The temporal restrictions can take 4 different value types:
@ -86,6 +88,7 @@ The temporal restrictions can take 4 different value types:
* Date, Time, or DateTime object value * Date, Time, or DateTime object value
* Proc or lambda object * Proc or lambda object
* A symbol matching the method name in the model * A symbol matching the method name in the model
* Between option takes an array of two values or a range
When an attribute value is compared to temporal restrictions, they are compared as When an attribute value is compared to temporal restrictions, they are compared as
the same type as the validation method type. So using validates_date means all the same type as the validation method type. So using validates_date means all
@ -266,7 +269,8 @@ For Rails 2.0/2.1:
:before => "must be before %s", :before => "must be before %s",
:on_or_before => "must be on or before %s", :on_or_before => "must be on or before %s",
:after => "must be after %s", :after => "must be after %s",
:on_or_after => "must be on or after %s" :on_or_after => "must be on or after %s",
:between => "must be between %s and %s"
) )
Where %s is the interpolation value for the restriction. Where %s is the interpolation value for the restriction.
@ -279,6 +283,7 @@ Rails 2.2+ using the I18n system to define new defaults:
messages: messages:
on_or_before: "must be equal to or before {{restriction}}" on_or_before: "must be equal to or before {{restriction}}"
on_or_after: "must be equal to or after {{restriction}}" on_or_after: "must be equal to or after {{restriction}}"
between: "must be between {{earliest}} and {{latest}}"
The {{restriction}} signifies where the interpolation value for the restriction The {{restriction}} signifies where the interpolation value for the restriction
will be inserted. will be inserted.

View File

@ -9,3 +9,4 @@ en:
on_or_before: "must be on or before {{restriction}}" on_or_before: "must be on or before {{restriction}}"
after: "must be after {{restriction}}" after: "must be after {{restriction}}"
on_or_after: "must be on or after {{restriction}}" on_or_after: "must be on or after {{restriction}}"
between: "must be between {{earliest}} and {{latest}}"

View File

@ -2,120 +2,156 @@ module Spec
module Rails module Rails
module Matchers module Matchers
class ValidateTimeliness class ValidateTimeliness
cattr_accessor :test_values
@@test_values = { VALIDITY_TEST_VALUES = {
:date => {:pass => '2000-01-01', :fail => '2000-01-32'}, :date => {:pass => '2000-01-01', :fail => '2000-01-32'},
:time => {:pass => '12:00', :fail => '25:00'}, :time => {:pass => '12:00', :fail => '25:00'},
:datetime => {:pass => '2000-01-01 00:00:00', :fail => '2000-01-32 00:00:00'} :datetime => {:pass => '2000-01-01 00:00:00', :fail => '2000-01-32 00:00:00'}
} }
OPTION_TEST_SETTINGS = {
:before => { :method => :-, :modify_on => :valid },
:after => { :method => :+, :modify_on => :valid },
:on_or_before => { :method => :+, :modify_on => :invalid },
:on_or_after => { :method => :-, :modify_on => :invalid }
}
def initialize(attribute, options) def initialize(attribute, options)
@expected, @options = attribute, options @expected, @options = attribute, options
@validator = ValidatesTimeliness::Validator.new(options) @validator = ValidatesTimeliness::Validator.new(options)
compile_error_messages
end
def compile_error_messages
messages = validator.send(:error_messages)
@messages = messages.inject({}) {|h, (k, v)| h[k] = v.gsub(/ (\%s|\{\{\w*\}\})/, ''); h }
end end
def matches?(record) def matches?(record)
@record = record @record = record
type = options[:type] @type = options[:type]
invalid_value = @@test_values[type][:fail] valid = test_validity
valid_value = parse_and_cast(@@test_values[type][:pass])
valid = error_matching(invalid_value, /#{messages["invalid_#{type}".to_sym]}/) &&
no_error_matching(valid_value, /#{messages["invalid_#{type}".to_sym]}/)
valid = test_option(:before, :-) if options[:before] && valid valid = test_option(:before) if @options[:before] && valid
valid = test_option(:after, :+) if options[:after] && valid valid = test_option(:after) if @options[:after] && valid
valid = test_option(:on_or_before, :+, :modify_on => :invalid) if options[:on_or_before] && valid valid = test_option(:on_or_before) if @options[:on_or_before] && valid
valid = test_option(:on_or_after, :-, :modify_on => :invalid) if options[:on_or_after] && valid valid = test_option(:on_or_after) if @options[:on_or_after] && valid
valid = test_between if @options[:between] && valid
return valid return valid
end end
def failure_message def failure_message
"expected model to validate #{options[:type]} attribute #{expected.inspect} with #{last_failure}" "expected model to validate #{@type} attribute #{@expected.inspect} with #{@last_failure}"
end end
def negative_failure_message def negative_failure_message
"expected not to validate #{options[:type]} attribute #{expected.inspect}" "expected not to validate #{@type} attribute #{@expected.inspect}"
end end
def description def description
"have validated #{options[:type]} attribute #{expected.inspect}" "have validated #{@type} attribute #{@expected.inspect}"
end end
private private
attr_reader :actual, :expected, :record, :options, :messages, :last_failure, :validator
def test_validity
def test_option(option, modifier, settings={}) invalid_value = VALIDITY_TEST_VALUES[@type][:fail]
settings.reverse_merge!(:modify_on => :valid) valid_value = parse_and_cast(VALIDITY_TEST_VALUES[@type][:pass])
boundary = parse_and_cast(options[option]) error_matching(invalid_value, "invalid_#{@type}".to_sym) &&
no_error_matching(valid_value, "invalid_#{@type}".to_sym)
end
def test_option(option)
settings = OPTION_TEST_SETTINGS[option]
boundary = parse_and_cast(@options[option])
method = settings[:method]
valid_value, invalid_value = if settings[:modify_on] == :valid valid_value, invalid_value = if settings[:modify_on] == :valid
[ boundary.send(modifier, 1), boundary ] [ boundary.send(method, 1), boundary ]
else else
[ boundary, boundary.send(modifier, 1) ] [ boundary, boundary.send(method, 1) ]
end end
message = messages[option] error_matching(invalid_value, option) &&
error_matching(invalid_value, /#{message}/) && no_error_matching(valid_value, option)
no_error_matching(valid_value, /#{message}/) end
def test_before
before = parse_and_cast(@options[:before])
error_matching(before - 1, :before) &&
no_error_matching(before, :before)
end
def test_between
between = parse_and_cast(@options[:between])
error_matching(between.first - 1, :between) &&
error_matching(between.last + 1, :between) &&
no_error_matching(between.first, :between) &&
no_error_matching(between.last, :between)
end end
def parse_and_cast(value) def parse_and_cast(value)
value = validator.send(:restriction_value, value, record) value = @validator.send(:restriction_value, value, @record)
validator.send(:type_cast_value, value) @validator.send(:type_cast_value, value)
end end
def error_matching(value, match) def error_matching(value, option)
record.send("#{expected}=", value) match = error_message_for(option)
record.valid? @record.send("#{@expected}=", value)
errors = record.errors.on(expected) @record.valid?
pass = [ errors ].flatten.any? {|error| match === error } errors = @record.errors.on(@expected)
@last_failure = "error matching #{match.inspect} when value is #{format_value(value)}" unless pass pass = [ errors ].flatten.any? {|error| /#{match}/ === error }
@last_failure = "error matching '#{match}' when value is #{format_value(value)}" unless pass
pass pass
end end
def no_error_matching(value, match) def no_error_matching(value, option)
pass = !error_matching(value, match) pass = !error_matching(value, option)
@last_failure = "no error matching #{match.inspect} when value is #{format_value(value)}" unless pass unless pass
error = error_message_for(option)
@last_failure = "no error matching '#{error}' when value is #{format_value(value)}"
end
pass pass
end end
def error_message_for(option)
msg = @validator.send(:error_messages)[option]
restriction = @validator.send(:restriction_value, @validator.configuration[option], @record)
if restriction
restriction = [restriction] unless restriction.is_a?(Array)
restriction.map! {|r| @validator.send(:type_cast_value, r) }
interpolate = @validator.send(:interpolation_values, option, restriction )
if defined?(I18n)
msg = @record.errors.generate_message(@expected, option, interpolate)
else
msg = msg % interpolate
end
end
msg
end
def format_value(value) def format_value(value)
return value if value.is_a?(String) return value if value.is_a?(String)
value.strftime(ValidatesTimeliness::Validator.error_value_formats[options[:type]]) value.strftime(ValidatesTimeliness::Validator.error_value_formats[@type])
end end
end end
def validate_date(attribute, options={}) def validate_date(attribute, options={})
options[:type] = :date options[:type] = :date
validate_timeliness_of(attribute, options) ValidateTimeliness.new(attribute, options)
end end
def validate_time(attribute, options={}) def validate_time(attribute, options={})
options[:type] = :time options[:type] = :time
validate_timeliness_of(attribute, options) ValidateTimeliness.new(attribute, options)
end end
def validate_datetime(attribute, options={}) def validate_datetime(attribute, options={})
options[:type] = :datetime options[:type] = :datetime
validate_timeliness_of(attribute, options)
end
private
def validate_timeliness_of(attribute, options={})
ValidateTimeliness.new(attribute, options) ValidateTimeliness.new(attribute, options)
end end
end end
end end
end end

View File

@ -11,6 +11,14 @@ module ValidatesTimeliness
:datetime => '%Y-%m-%d %H:%M:%S' :datetime => '%Y-%m-%d %H:%M:%S'
} }
RESTRICTION_METHODS = {
:before => :<,
:after => :>,
:on_or_before => :<=,
:on_or_after => :>=,
:between => lambda {|v, r| (r.first..r.last).include?(v) }
}
attr_reader :configuration, :type attr_reader :configuration, :type
def initialize(configuration) def initialize(configuration)
@ -40,21 +48,17 @@ module ValidatesTimeliness
end end
def validate_restrictions(record, attr_name, value) def validate_restrictions(record, attr_name, value)
restriction_methods = {:before => '<', :after => '>', :on_or_before => '<=', :on_or_after => '>='}
display = self.class.error_value_formats[type]
value = type_cast_value(value) value = type_cast_value(value)
restriction_methods.each do |option, method| RESTRICTION_METHODS.each do |option, method|
next unless restriction = configuration[option] next unless restriction = configuration[option]
begin begin
compare = restriction_value(restriction, record) restriction = restriction_value(restriction, record)
next if compare.nil? next if restriction.nil?
compare = type_cast_value(compare) restriction = type_cast_value(restriction)
unless value.send(method, compare) unless evaluate_restriction(restriction, value, method)
add_error(record, attr_name, option, :restriction => compare.strftime(display)) add_error(record, attr_name, option, interpolation_values(option, restriction))
end end
rescue rescue
unless self.class.ignore_restriction_errors unless self.class.ignore_restriction_errors
@ -63,15 +67,41 @@ module ValidatesTimeliness
end end
end end
end end
def interpolation_values(option, restriction)
format = self.class.error_value_formats[type]
restriction = [restriction] unless restriction.is_a?(Array)
if defined?(I18n)
message = custom_error_messages[option] || I18n.translate('activerecord.errors.messages')[option]
subs = message.scan(/\{\{([^\}]*)\}\}/)
interpolations = {}
subs.each_with_index {|s, i| interpolations[s[0].to_sym] = restriction[i].strftime(format) }
interpolations
else
restriction.map {|r| r.strftime(format) }
end
end
def evaluate_restriction(restriction, value, comparator)
return true if restriction.nil?
case comparator
when Symbol
value.send(comparator, restriction)
when Proc
comparator.call(value, restriction)
end
end
def add_error(record, attr_name, message, interpolate={}) def add_error(record, attr_name, message, interpolate=nil)
if defined?(I18n) if defined?(I18n)
# use i18n support in AR for message or use custom message passed to validation method # use i18n support in AR for message or use custom message passed to validation method
custom = custom_error_messages[message] custom = custom_error_messages[message]
record.errors.add(attr_name, custom || message, interpolate) record.errors.add(attr_name, custom || message, interpolate || {})
else else
message = error_messages[message] if message.is_a?(Symbol) message = error_messages[message] if message.is_a?(Symbol)
message = message % interpolate.values unless interpolate.empty? message = message % interpolate
record.errors.add(attr_name, message) record.errors.add(attr_name, message)
end end
end end
@ -83,7 +113,12 @@ module ValidatesTimeliness
def custom_error_messages def custom_error_messages
return @custom_error_messages if defined?(@custom_error_messages) return @custom_error_messages if defined?(@custom_error_messages)
@custom_error_messages = configuration.inject({}) {|h, (k, v)| h[$1.to_sym] = v if k.to_s =~ /(.*)_message$/;h } @custom_error_messages = configuration.inject({}) {|msgs, (k, v)|
if md = /(.*)_message$/.match(k.to_s)
msgs[md[0].to_sym] = v
end
msgs
}
end end
def restriction_value(restriction, record) def restriction_value(restriction, record)
@ -94,13 +129,20 @@ module ValidatesTimeliness
restriction_value(record.send(restriction), record) restriction_value(record.send(restriction), record)
when Proc when Proc
restriction_value(restriction.call(record), record) restriction_value(restriction.call(record), record)
when Array
restriction.map {|r| restriction_value(r, record) }.sort
when Range
restriction_value([restriction.first, restriction.last], record)
else else
record.class.parse_date_time(restriction, type, false) record.class.parse_date_time(restriction, type, false)
end end
end end
def type_cast_value(value) def type_cast_value(value)
case type if value.is_a?(Array)
value.map {|v| type_cast_value(v) }
else
case type
when :time when :time
value.to_dummy_time value.to_dummy_time
when :date when :date
@ -109,10 +151,11 @@ module ValidatesTimeliness
if value.is_a?(DateTime) || value.is_a?(Time) if value.is_a?(DateTime) || value.is_a?(Time)
value.to_time value.to_time
else else
value.to_time(ValidatesTimelines.default_timezone) value.to_time(ValidatesTimeliness.default_timezone)
end end
else else
nil nil
end
end end
end end

View File

@ -6,13 +6,17 @@ end
class WithValidation < Person class WithValidation < Person
validates_date :birth_date, validates_date :birth_date,
:before => '2000-01-10', :after => '2000-01-01', :before => '2000-01-10', :after => '2000-01-01',
:on_or_before => '2000-01-09', :on_or_after => '2000-01-02' :on_or_before => '2000-01-09', :on_or_after => '2000-01-02',
:between => ['2000-01-01', '2000-01-03']
validates_time :birth_time, validates_time :birth_time,
:before => '23:00', :after => '09:00', :before => '23:00', :after => '09:00',
:on_or_before => '22:00', :on_or_after => '10:00' :on_or_before => '22:00', :on_or_after => '10:00',
:between => ['09:00', '17:00']
validates_datetime :birth_date_and_time, validates_datetime :birth_date_and_time,
:before => '2000-01-10 23:00', :after => '2000-01-01 09:00', :before => '2000-01-10 23:00', :after => '2000-01-01 09:00',
:on_or_before => '2000-01-09 23:00', :on_or_after => '2000-01-02 09:00' :on_or_before => '2000-01-09 23:00', :on_or_after => '2000-01-02 09:00',
:between => ['2000-01-01 09:00', '2000-01-01 17:00']
end end
@ -27,20 +31,21 @@ end
describe "ValidateTimeliness matcher" do describe "ValidateTimeliness matcher" do
attr_accessor :no_validation, :with_validation attr_accessor :no_validation, :with_validation
@@attribute_for_type = { :date => :birth_date, :time => :birth_time, :datetime => :birth_date_and_time }
before do before do
@no_validation = NoValidation.new @no_validation = NoValidation.new
@with_validation = WithValidation.new @with_validation = WithValidation.new
end end
[:date, :time, :datetime].each do |type| [:date, :time, :datetime].each do |type|
attribute = type == :datetime ? :date_and_time : type
it "should report that #{type} is validated" do it "should report that #{type} is validated" do
with_validation.should self.send("validate_#{type}", "birth_#{attribute}".to_sym) with_validation.should self.send("validate_#{type}", attribute_for_type(type))
end end
it "should report that #{type} is not validated" do it "should report that #{type} is not validated" do
no_validation.should_not self.send("validate_#{type}", "birth_#{attribute}".to_sym) no_validation.should_not self.send("validate_#{type}", attribute_for_type(type))
end end
end end
@ -52,18 +57,17 @@ describe "ValidateTimeliness matcher" do
} }
[:date, :time, :datetime].each do |type| [:date, :time, :datetime].each do |type|
attribute = type == :datetime ? :date_and_time : type
it "should report that #{type} is validated" do it "should report that #{type} is validated" do
with_validation.should self.send("validate_#{type}", "birth_#{attribute}", :before => test_values[type][0]) with_validation.should self.send("validate_#{type}", attribute_for_type(type), :before => test_values[type][0])
end end
it "should report that #{type} is not validated when option value is incorrect" do it "should report that #{type} is not validated when option value is incorrect" do
with_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :before => test_values[type][1]) with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :before => test_values[type][1])
end end
it "should report that #{type} is not validated with option" do it "should report that #{type} is not validated with option" do
no_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :before => test_values[type][0]) no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :before => test_values[type][0])
end end
end end
end end
@ -76,18 +80,17 @@ describe "ValidateTimeliness matcher" do
} }
[:date, :time, :datetime].each do |type| [:date, :time, :datetime].each do |type|
attribute = type == :datetime ? :date_and_time : type
it "should report that #{type} is validated" do it "should report that #{type} is validated" do
with_validation.should self.send("validate_#{type}", "birth_#{attribute}", :after => test_values[type][0]) with_validation.should self.send("validate_#{type}", attribute_for_type(type), :after => test_values[type][0])
end end
it "should report that #{type} is not validated when option value is incorrect" do it "should report that #{type} is not validated when option value is incorrect" do
with_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :after => test_values[type][1]) with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :after => test_values[type][1])
end end
it "should report that #{type} is not validated with option" do it "should report that #{type} is not validated with option" do
no_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :after => test_values[type][0]) no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :after => test_values[type][0])
end end
end end
end end
@ -100,18 +103,17 @@ describe "ValidateTimeliness matcher" do
} }
[:date, :time, :datetime].each do |type| [:date, :time, :datetime].each do |type|
attribute = type == :datetime ? :date_and_time : type
it "should report that #{type} is validated" do it "should report that #{type} is validated" do
with_validation.should self.send("validate_#{type}", "birth_#{attribute}", :on_or_before => test_values[type][0]) with_validation.should self.send("validate_#{type}", attribute_for_type(type), :on_or_before => test_values[type][0])
end end
it "should report that #{type} is not validated when option value is incorrect" do it "should report that #{type} is not validated when option value is incorrect" do
with_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :on_or_before => test_values[type][1]) with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :on_or_before => test_values[type][1])
end end
it "should report that #{type} is not validated with option" do it "should report that #{type} is not validated with option" do
no_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :on_or_before => test_values[type][0]) no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :on_or_before => test_values[type][0])
end end
end end
end end
@ -124,22 +126,44 @@ describe "ValidateTimeliness matcher" do
} }
[:date, :time, :datetime].each do |type| [:date, :time, :datetime].each do |type|
attribute = type == :datetime ? :date_and_time : type
it "should report that #{type} is validated" do it "should report that #{type} is validated" do
with_validation.should self.send("validate_#{type}", "birth_#{attribute}", :on_or_after => test_values[type][0]) with_validation.should self.send("validate_#{type}", attribute_for_type(type), :on_or_after => test_values[type][0])
end end
it "should report that #{type} is not validated when option value is incorrect" do it "should report that #{type} is not validated when option value is incorrect" do
with_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :on_or_after => test_values[type][1]) with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :on_or_after => test_values[type][1])
end end
it "should report that #{type} is not validated with option" do it "should report that #{type} is not validated with option" do
no_validation.should_not self.send("validate_#{type}", "birth_#{attribute}", :on_or_after => test_values[type][0]) no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :on_or_after => test_values[type][0])
end end
end end
end end
describe "between option" do
test_values = {
:date => [ ['2000-01-01', '2000-01-03'], ['2000-01-01', '2000-01-04'] ],
:time => [ ['09:00', '17:00'], ['09:00', '17:01'] ],
:datetime => [ ['2000-01-01 09:00', '2000-01-01 17:00'], ['2000-01-01 09:00', '2000-01-01 17:01'] ]
}
[:date, :time, :datetime].each do |type|
it "should report that #{type} is validated" do
with_validation.should self.send("validate_#{type}", attribute_for_type(type), :between => test_values[type][0])
end
it "should report that #{type} is not validated when option value is incorrect" do
with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :between => test_values[type][1])
end
it "should report that #{type} is not validated with option" do
no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :between => test_values[type][0])
end
end
end
describe "custom messages" do describe "custom messages" do
before do before do
@ -175,4 +199,8 @@ describe "ValidateTimeliness matcher" do
end end
end end
def attribute_for_type(type)
@@attribute_for_type[type.to_sym]
end
end end

View File

@ -43,6 +43,25 @@ describe ValidatesTimeliness::Validator do
restriction_value(lambda {"2007-01-01 12:00"}, :datetime).should be_kind_of(Time) restriction_value(lambda {"2007-01-01 12:00"}, :datetime).should be_kind_of(Time)
end end
it "should return array of Time objects when restriction is array of Time objects" do
time1, time2 = Time.now, 1.day.ago
restriction_value([time1, time2], :datetime).should == [time2, time1]
end
it "should return array of Time objects when restriction is array of strings" do
time1, time2 = "2000-01-02", "2000-01-01"
restriction_value([time1, time2], :datetime).should == [Person.parse_date_time(time2, :datetime), Person.parse_date_time(time1, :datetime)]
end
it "should return array of Time objects when restriction is Range of Time objects" do
time1, time2 = Time.now, 1.day.ago
restriction_value(time1..time2, :datetime).should == [time2, time1]
end
it "should return array of Time objects when restriction is Range of time strings" do
time1, time2 = "2000-01-02", "2000-01-01"
restriction_value(time1..time2, :datetime).should == [Person.parse_date_time(time2, :datetime), Person.parse_date_time(time1, :datetime)]
end
def restriction_value(restriction, type) def restriction_value(restriction, type)
configure_validator(:type => type) configure_validator(:type => type)
validator.send(:restriction_value, restriction, person) validator.send(:restriction_value, restriction, person)
@ -212,83 +231,101 @@ describe ValidatesTimeliness::Validator do
end end
end end
describe "instance with on_or_before and on_or_after restrictions" do describe "instance with between restriction" do
describe "for datetime type" do describe "for datetime type" do
before do before do
configure_validator(:on_or_before => Time.now.at_midnight, :on_or_after => 1.day.ago) configure_validator(:between => [1.day.ago.at_midnight, 1.day.from_now.at_midnight])
end end
it "should have error when value is past :on_or_before restriction" do it "should have error when value is before earlist :between restriction" do
validate_with(:birth_date_and_time, Time.now.at_midnight + 1) validate_with(:birth_date_and_time, 2.days.ago)
should_have_error(:birth_date_and_time, :on_of_before) should_have_error(:birth_date_and_time, :between)
end end
it "should be valid when value is equal to :on_or_before restriction" do it "should have error when value is after latest :between restriction" do
validate_with(:birth_date_and_time, Time.now.at_midnight) validate_with(:birth_date_and_time, 2.days.from_now)
should_have_no_error(:birth_date_and_time, :on_of_before) should_have_error(:birth_date_and_time, :between)
end end
it "should have error when value is before :on_or_after restriction" do it "should be valid when value is equal to earliest :between restriction" do
validate_with(:birth_date_and_time, 1.days.ago - 1) validate_with(:birth_date_and_time, 1.day.ago.at_midnight)
should_have_error(:birth_date_and_time, :on_of_after) should_have_no_error(:birth_date_and_time, :between)
end end
it "should be valid when value is value equal to :on_or_after restriction" do it "should be valid when value is equal to latest :between restriction" do
validate_with(:birth_date_and_time, 1.day.ago) validate_with(:birth_date_and_time, 1.day.from_now.at_midnight)
should_have_no_error(:birth_date_and_time, :on_of_after) should_have_no_error(:birth_date_and_time, :between)
end
it "should allow a range for between restriction" do
configure_validator(:type => :datetime, :between => (1.day.ago.at_midnight)..(1.day.from_now.at_midnight))
validate_with(:birth_date_and_time, 1.day.from_now.at_midnight)
should_have_no_error(:birth_date_and_time, :between)
end end
end end
describe "for date type" do describe "for date type" do
before :each do before do
configure_validator(:on_or_before => 1.day.from_now, :on_or_after => 1.day.ago, :type => :date) configure_validator(:type => :date, :between => [1.day.ago.to_date, 1.day.from_now.to_date])
end end
it "should have error when value is past :on_or_before restriction" do it "should have error when value is before earlist :between restriction" do
validate_with(:birth_date, 2.days.from_now) validate_with(:birth_date, 2.days.ago.to_date)
should_have_error(:birth_date, :on_or_before) should_have_error(:birth_date, :between)
end end
it "should have error when value is before :on_or_after restriction" do it "should have error when value is after latest :between restriction" do
validate_with(:birth_date, 2.days.ago) validate_with(:birth_date, 2.days.from_now.to_date)
should_have_error(:birth_date, :on_or_after) should_have_error(:birth_date, :between)
end end
it "should be valid when value is equal to :on_or_before restriction" do it "should be valid when value is equal to earliest :between restriction" do
validate_with(:birth_date, 1.day.from_now) validate_with(:birth_date, 1.day.ago.to_date)
should_have_no_error(:birth_date, :on_or_before) should_have_no_error(:birth_date, :between)
end end
it "should be valid when value value is equal to :on_or_after restriction" do it "should be valid when value is equal to latest :between restriction" do
validate_with(:birth_date, 1.day.ago) validate_with(:birth_date, 1.day.from_now.to_date)
should_have_no_error(:birth_date, :on_or_before) should_have_no_error(:birth_date, :between)
end
it "should allow a range for between restriction" do
configure_validator(:type => :date, :between => (1.day.ago.to_date)..(1.day.from_now.to_date))
validate_with(:birth_date, 1.day.from_now.to_date)
should_have_no_error(:birth_date, :between)
end end
end end
describe "for time type" do describe "for time type" do
before :each do before do
configure_validator(:on_or_before => "23:00", :on_or_after => "06:00", :type => :time) configure_validator(:type => :time, :between => ["09:00", "17:00"])
end end
it "should have error when value is past :on_or_before restriction" do it "should have error when value is before earlist :between restriction" do
validate_with(:birth_time, "23:01") validate_with(:birth_time, "08:59")
should_have_error(:birth_time, :on_or_before) should_have_error(:birth_time, :between)
end end
it "should have error when value is before :on_or_after restriction" do it "should have error when value is after latest :between restriction" do
validate_with(:birth_time, "05:59") validate_with(:birth_time, "17:01")
should_have_error(:birth_time, :on_or_after) should_have_error(:birth_time, :between)
end end
it "should be valid when value is on boundary of :on_or_before restriction" do it "should be valid when value is equal to earliest :between restriction" do
validate_with(:birth_time, "23:00") validate_with(:birth_time, "09:00")
should_have_no_error(:birth_time, :on_or_before) should_have_no_error(:birth_time, :between)
end end
it "should be valid when value is on boundary of :on_or_after restriction" do it "should be valid when value is equal to latest :between restriction" do
validate_with(:birth_time, "06:00") validate_with(:birth_time, "17:00")
should_have_no_error(:birth_time, :on_or_after) should_have_no_error(:birth_time, :between)
end
it "should allow a range for between restriction" do
configure_validator(:type => :time, :between => "09:00".."17:00")
validate_with(:birth_time, "17:00")
should_have_no_error(:birth_time, :between)
end end
end end
end end
@ -433,6 +470,6 @@ describe ValidatesTimeliness::Validator do
def error_messages def error_messages
return @error_messages if defined?(@error_messages) return @error_messages if defined?(@error_messages)
messages = validator.send(:error_messages) messages = validator.send(:error_messages)
@error_messages = messages.inject({}) {|h, (k, v)| h[k] = v.gsub(/ (\%s|\{\{\w*\}\})/, ''); h } @error_messages = messages.inject({}) {|h, (k, v)| h[k] = v.sub(/ (\%s|\{\{\w*\}\}).*/, ''); h }
end end
end end