diff --git a/lib/validates_timeliness/formats.rb b/lib/validates_timeliness/formats.rb index 7082752..23e00bc 100644 --- a/lib/validates_timeliness/formats.rb +++ b/lib/validates_timeliness/formats.rb @@ -161,20 +161,26 @@ module ValidatesTimeliness # Loop through format expressions for type and call proc on matches. Allow # pre or post match strings to exist if strict is false. Otherwise wrap # regexp in start and end anchors. - # Returns 7 part time array. + # Returns time array if matches a format, nil otherwise. def parse(string, type, options={}) return string unless string.is_a?(String) options.reverse_merge!(:strict => true) + sets = if options[:format] + [ send("#{type}_expressions").assoc(options[:format]) ] + else + expression_set(type, string) + end + matches = nil - exp, processor = expression_set(type, string).find do |regexp, proc| + processor = sets.each do |format, regexp, proc| full = /\A#{regexp}\Z/ if options[:strict] full ||= case type when :date then /\A#{regexp}/ when :time then /#{regexp}\Z/ when :datetime then /\A#{regexp}\Z/ end - matches = full.match(string.strip) + break(proc) if matches = full.match(string.strip) end last = options[:include_offset] ? 8 : 7 processor.call(*matches[1..last]) if matches @@ -258,7 +264,7 @@ module ValidatesTimeliness end def compile_formats(formats) - formats.map { |format| regexp, format_proc = format_expression_generator(format) } + formats.map { |format| [ format, *format_expression_generator(format) ] } end # Pick expression set and combine date and datetimes for diff --git a/lib/validates_timeliness/validator.rb b/lib/validates_timeliness/validator.rb index 06978c7..050eed4 100644 --- a/lib/validates_timeliness/validator.rb +++ b/lib/validates_timeliness/validator.rb @@ -14,8 +14,8 @@ module ValidatesTimeliness } VALID_OPTIONS = [ - :on, :if, :unless, :allow_nil, :empty, :allow_blank, :blank, - :with_time, :with_date, :ignore_usec, + :on, :if, :unless, :allow_nil, :empty, :allow_blank, + :with_time, :with_date, :ignore_usec, :format, :invalid_time_message, :invalid_date_message, :invalid_datetime_message ] + RESTRICTION_METHODS.keys.map {|option| [option, "#{option}_message".to_sym] }.flatten @@ -29,14 +29,17 @@ module ValidatesTimeliness end def call(record, attr_name, value) - value = ValidatesTimeliness::Parser.parse(value, type, :strict => false) if value.is_a?(String) raw_value = raw_value(record, attr_name) || value + if value.is_a?(String) || @configuration[:format] + strict = !@configuration[:format].nil? + value = ValidatesTimeliness::Parser.parse(raw_value, type, :strict => strict, :format => @configuration[:format]) + end + return if (raw_value.nil? && configuration[:allow_nil]) || (raw_value.blank? && configuration[:allow_blank]) add_error(record, attr_name, :blank) and return if raw_value.blank? - - add_error(record, attr_name, "invalid_#{type}".to_sym) and return unless value + add_error(record, attr_name, "invalid_#{type}".to_sym) and return if value.nil? validate_restrictions(record, attr_name, value) end @@ -107,7 +110,6 @@ module ValidatesTimeliness def add_error(record, attr_name, message, interpolate=nil) if defined?(I18n) - # use i18n support in AR for message or use custom message passed to validation method custom = custom_error_messages[message] record.errors.add(attr_name, custom || message, interpolate || {}) else diff --git a/spec/formats_spec.rb b/spec/formats_spec.rb index 2c233c9..e6e1589 100644 --- a/spec/formats_spec.rb +++ b/spec/formats_spec.rb @@ -100,7 +100,7 @@ describe ValidatesTimeliness::Formats do end end - describe "extracting values" do + describe "parse" do it "should return time array from date string" do time_array = formats.parse('12:13:14', :time, :strict => true) @@ -143,11 +143,19 @@ describe ValidatesTimeliness::Formats do end end - describe "removing formats" do - before do - formats.compile_format_expressions + describe "parse with format option" do + it "should return values if string matches specified format" do + time_array = formats.parse('2000-02-01 12:13:14', :datetime, :format => 'yyyy-mm-dd hh:nn:ss') + time_array.should == [2000,2,1,12,13,14,0] end - + + it "should return nil if string does not match specified format" do + time_array = formats.parse('2000-02-01 12:13', :datetime, :format => 'yyyy-mm-dd hh:nn:ss') + time_array.should be_nil + end + end + + describe "removing formats" do it "should remove format from format array" do formats.remove_formats(:time, 'h.nn_ampm') formats.time_formats.should_not include("h o'clock") @@ -165,7 +173,7 @@ describe ValidatesTimeliness::Formats do after do formats.time_formats << 'h.nn_ampm' - # reload class instead + formats.compile_format_expressions end end @@ -223,7 +231,7 @@ describe ValidatesTimeliness::Formats do def validate(time_string, type) valid = false - formats.send("#{type}_expressions").each do |(regexp, processor)| + formats.send("#{type}_expressions").each do |format, regexp, processor| valid = true and break if /\A#{regexp}\Z/ =~ time_string end valid diff --git a/spec/validator_spec.rb b/spec/validator_spec.rb index 5eff1d9..4eee8be 100644 --- a/spec/validator_spec.rb +++ b/spec/validator_spec.rb @@ -473,6 +473,20 @@ describe ValidatesTimeliness::Validator do end end + describe "instance with format option" do + it "should validate attribute when value matches format" do + configure_validator(:type => :time, :format => 'hh:nn:ss') + validate_with(:birth_time, "12:00:00") + should_have_no_error(:birth_time, :invalid_time) + end + + it "should not validate attribute when value does not match format" do + configure_validator(:type => :time, :format => 'hh:nn:ss') + validate_with(:birth_time, "12:00") + should_have_error(:birth_time, :invalid_time) + end + end + describe "custom_error_messages" do it "should return hash of custom error messages from configuration with _message truncated from keys" do configure_validator(:type => :date, :invalid_date_message => 'thats no date')