added :with_date and :with_time options

refactored restriction_value into evaluate_option_value class method for more general usage
refactored type_cast_value into class method
This commit is contained in:
Adam Meehan 2009-02-09 16:44:03 +11:00
parent 904c202fb4
commit 862b41f903
3 changed files with 119 additions and 63 deletions

View File

@ -92,8 +92,8 @@ module Spec
end end
def parse_and_cast(value) def parse_and_cast(value)
value = @validator.send(:restriction_value, value, @record) value = @validator.class.send(:evaluate_option_value, value, @type, @record)
@validator.send(:type_cast_value, value) @validator.class.send(:type_cast_value, value, @type)
end end
def error_matching(value, option) def error_matching(value, option)
@ -117,11 +117,11 @@ module Spec
def error_message_for(option) def error_message_for(option)
msg = @validator.send(:error_messages)[option] msg = @validator.send(:error_messages)[option]
restriction = @validator.send(:restriction_value, @validator.configuration[option], @record) restriction = @validator.class.send(:evaluate_option_value, @validator.configuration[option], @type, @record)
if restriction if restriction
restriction = [restriction] unless restriction.is_a?(Array) restriction = [restriction] unless restriction.is_a?(Array)
restriction.map! {|r| @validator.send(:type_cast_value, r) } restriction.map! {|r| @validator.class.send(:type_cast_value, r, @type) }
interpolate = @validator.send(:interpolation_values, option, restriction ) interpolate = @validator.send(:interpolation_values, option, restriction )
# get I18n message if defined and has interpolation keys in msg # get I18n message if defined and has interpolation keys in msg
if defined?(I18n) && !@validator.send(:custom_error_messages).include?(option) if defined?(I18n) && !@validator.send(:custom_error_messages).include?(option)

View File

@ -20,7 +20,8 @@ module ValidatesTimeliness
} }
VALID_OPTIONS = [ VALID_OPTIONS = [
:on, :allow_nil, :empty, :allow_blank, :blank, :invalid_time_message, :invalid_date_message, :invalid_datetime_message :on, :allow_nil, :empty, :allow_blank, :blank, :with_time, :with_date,
:invalid_time_message, :invalid_date_message, :invalid_datetime_message
] + RESTRICTION_METHODS.keys.map {|option| [option, "#{option}_message".to_sym] }.flatten ] + RESTRICTION_METHODS.keys.map {|option| [option, "#{option}_message".to_sym] }.flatten
attr_reader :configuration, :type attr_reader :configuration, :type
@ -52,14 +53,21 @@ module ValidatesTimeliness
end end
def validate_restrictions(record, attr_name, value) def validate_restrictions(record, attr_name, value)
value = type_cast_value(value) value = if @configuration[:with_time] || @configuration[:with_date]
restriction_type = :datetime
combine_date_and_time(value, record)
else
restriction_type = type
self.class.type_cast_value(value, type)
end
return if value.nil?
RESTRICTION_METHODS.each do |option, method| RESTRICTION_METHODS.each do |option, method|
next unless restriction = configuration[option] next unless restriction = configuration[option]
begin begin
restriction = restriction_value(restriction, record) restriction = self.class.evaluate_option_value(restriction, restriction_type, record)
next if restriction.nil? next if restriction.nil?
restriction = type_cast_value(restriction) restriction = self.class.type_cast_value(restriction, restriction_type)
unless evaluate_restriction(restriction, value, method) unless evaluate_restriction(restriction, value, method)
add_error(record, attr_name, option, interpolation_values(option, restriction)) add_error(record, attr_name, option, interpolation_values(option, restriction))
@ -123,49 +131,67 @@ module ValidatesTimeliness
} }
end end
def restriction_value(restriction, record) def combine_date_and_time(value, record)
case restriction if type == :date
when Time, Date, DateTime date = value
restriction time = @configuration[:with_time]
when Symbol
restriction_value(record.send(restriction), record)
when Proc
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) date = @configuration[:with_date]
end time = value
end
def type_cast_value(value)
if value.is_a?(Array)
value.map {|v| type_cast_value(v) }
else
case type
when :time
value.to_dummy_time
when :date
value.to_date
when :datetime
if value.is_a?(DateTime) || value.is_a?(Time)
value.to_time
else
value.to_time(ValidatesTimeliness.default_timezone)
end
else
nil
end
end end
date, time = self.class.evaluate_option_value(date, :date, record), self.class.evaluate_option_value(time, :time, record)
return if date.nil? || time.nil?
record.class.send(:make_time, [date.year, date.month, date.day, time.hour, time.min, time.sec, time.usec])
end end
def validate_options(options) def validate_options(options)
invalid_types = [:time, :date, :datetime] invalid_for_type = ([:time, :date, :datetime] - [@type]).map {|k| "invalid_#{k}_message".to_sym }
invalid_types.delete(@type) invalid_for_type << :with_date unless @type == :time
valid_options = VALID_OPTIONS.reject {|option| invalid_types.include?("#{option}_message".to_sym) } invalid_for_type << :with_time unless @type == :date
options.assert_valid_keys(valid_options) options.assert_valid_keys(VALID_OPTIONS - invalid_for_type)
end
# class methods
class << self
def evaluate_option_value(value, type, record)
case value
when Time, Date, DateTime
value
when Symbol
evaluate_option_value(record.send(value), type, record)
when Proc
evaluate_option_value(value.call(record), type, record)
when Array
value.map {|r| evaluate_option_value(r, type, record) }.sort
when Range
evaluate_option_value([value.first, value.last], type, record)
else
record.class.parse_date_time(value, type, false)
end
end
def type_cast_value(value, type)
if value.is_a?(Array)
value.map {|v| type_cast_value(v, type) }
else
case type
when :time
value.to_dummy_time
when :date
value.to_date
when :datetime
if value.is_a?(DateTime) || value.is_a?(Time)
value.to_time
else
value.to_time(ValidatesTimeliness.default_timezone)
end
else
nil
end
end
end
end end
end end

View File

@ -18,9 +18,8 @@ describe ValidatesTimeliness::Validator do
describe "option keys validation" do describe "option keys validation" do
before do before do
@valid_options = ValidatesTimeliness::Validator::VALID_OPTIONS.inject({}) {|hash, opt| hash[opt] = nil; hash } keys = ValidatesTimeliness::Validator::VALID_OPTIONS - [:invalid_date_message, :invalid_time_message, :with_date, :with_time]
@valid_options.delete(:invalid_date_message) @valid_options = keys.inject({}) {|hash, opt| hash[opt] = nil; hash }
@valid_options.delete(:invalid_time_message)
end end
it "should raise error if invalid option key passed" do it "should raise error if invalid option key passed" do
@ -33,55 +32,55 @@ describe ValidatesTimeliness::Validator do
end end
end end
describe "restriction_value" do describe "evaluate_option_value" do
it "should return Time object when restriction is Time object" do it "should return Time object when restriction is Time object" do
restriction_value(Time.now, :datetime).should be_kind_of(Time) evaluate_option_value(Time.now, :datetime).should be_kind_of(Time)
end end
it "should return Time object when restriction is string" do it "should return Time object when restriction is string" do
restriction_value("2007-01-01 12:00", :datetime).should be_kind_of(Time) evaluate_option_value("2007-01-01 12:00", :datetime).should be_kind_of(Time)
end end
it "should return Time object when restriction is method and method returns Time object" do it "should return Time object when restriction is method and method returns Time object" do
person.stub!(:datetime_attr).and_return(Time.now) person.stub!(:datetime_attr).and_return(Time.now)
restriction_value(:datetime_attr, :datetime).should be_kind_of(Time) evaluate_option_value(:datetime_attr, :datetime).should be_kind_of(Time)
end end
it "should return Time object when restriction is method and method returns string" do it "should return Time object when restriction is method and method returns string" do
person.stub!(:datetime_attr).and_return("2007-01-01 12:00") person.stub!(:datetime_attr).and_return("2007-01-01 12:00")
restriction_value(:datetime_attr, :datetime).should be_kind_of(Time) evaluate_option_value(:datetime_attr, :datetime).should be_kind_of(Time)
end end
it "should return Time object when restriction is proc which returns Time object" do it "should return Time object when restriction is proc which returns Time object" do
restriction_value(lambda { Time.now }, :datetime).should be_kind_of(Time) evaluate_option_value(lambda { Time.now }, :datetime).should be_kind_of(Time)
end end
it "should return Time object when restriction is proc which returns string" do it "should return Time object when restriction is proc which returns string" do
restriction_value(lambda {"2007-01-01 12:00"}, :datetime).should be_kind_of(Time) evaluate_option_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 it "should return array of Time objects when restriction is array of Time objects" do
time1, time2 = Time.now, 1.day.ago time1, time2 = Time.now, 1.day.ago
restriction_value([time1, time2], :datetime).should == [time2, time1] evaluate_option_value([time1, time2], :datetime).should == [time2, time1]
end end
it "should return array of Time objects when restriction is array of strings" do it "should return array of Time objects when restriction is array of strings" do
time1, time2 = "2000-01-02", "2000-01-01" 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)] evaluate_option_value([time1, time2], :datetime).should == [Person.parse_date_time(time2, :datetime), Person.parse_date_time(time1, :datetime)]
end end
it "should return array of Time objects when restriction is Range of Time objects" do it "should return array of Time objects when restriction is Range of Time objects" do
time1, time2 = Time.now, 1.day.ago time1, time2 = Time.now, 1.day.ago
restriction_value(time1..time2, :datetime).should == [time2, time1] evaluate_option_value(time1..time2, :datetime).should == [time2, time1]
end end
it "should return array of Time objects when restriction is Range of time strings" do it "should return array of Time objects when restriction is Range of time strings" do
time1, time2 = "2000-01-02", "2000-01-01" 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)] evaluate_option_value(time1..time2, :datetime).should == [Person.parse_date_time(time2, :datetime), Person.parse_date_time(time1, :datetime)]
end end
def restriction_value(restriction, type) def evaluate_option_value(restriction, type)
configure_validator(:type => type) configure_validator(:type => type)
validator.send(:restriction_value, restriction, person) validator.class.send(:evaluate_option_value, restriction, type, person)
end end
end end
@ -347,6 +346,37 @@ describe ValidatesTimeliness::Validator do
end end
end end
describe "instance with :with_time option" do
it "should validate date attribute as datetime combining value of :with_time against restrictions " do
configure_validator(:type => :date, :with_time => '12:31', :on_or_before => Time.mktime(2000,1,1,12,30))
validate_with(:birth_date, "2000-01-01")
should_have_error(:birth_date, :on_or_before)
end
it "should skip restriction validation if :with_time value is nil" do
configure_validator(:type => :date, :with_time => nil, :on_or_before => Time.mktime(2000,1,1,12,30))
validate_with(:birth_date, "2000-01-01")
should_have_no_error(:birth_date, :on_or_before)
end
end
describe "instance with :with_date option" do
it "should validate time attribute as datetime combining value of :with_date against restrictions " do
configure_validator(:type => :time, :with_date => '2009-01-01', :on_or_before => Time.mktime(2000,1,1,12,30))
validate_with(:birth_date, "12:30")
should_have_error(:birth_date, :on_or_before)
end
it "should skip restriction validation if :with_date value is nil" do
configure_validator(:type => :time, :with_date => nil, :on_or_before => Time.mktime(2000,1,1,12,30))
validate_with(:birth_date, "12:30")
should_have_no_error(:birth_date, :on_or_before)
end
end
describe "instance with mixed value and restriction types" do describe "instance with mixed value and restriction types" do
it "should validate datetime attribute with Date restriction" do it "should validate datetime attribute with Date restriction" do