add :format option to validate and parse with specific a format

This commit is contained in:
Adam Meehan 2009-04-10 10:57:27 +10:00
parent f041524124
commit bb94e234bc
4 changed files with 47 additions and 17 deletions

View File

@ -161,20 +161,26 @@ module ValidatesTimeliness
# Loop through format expressions for type and call proc on matches. Allow # 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 # pre or post match strings to exist if strict is false. Otherwise wrap
# regexp in start and end anchors. # 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={}) def parse(string, type, options={})
return string unless string.is_a?(String) return string unless string.is_a?(String)
options.reverse_merge!(:strict => true) options.reverse_merge!(:strict => true)
sets = if options[:format]
[ send("#{type}_expressions").assoc(options[:format]) ]
else
expression_set(type, string)
end
matches = nil 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 = /\A#{regexp}\Z/ if options[:strict]
full ||= case type full ||= case type
when :date then /\A#{regexp}/ when :date then /\A#{regexp}/
when :time then /#{regexp}\Z/ when :time then /#{regexp}\Z/
when :datetime then /\A#{regexp}\Z/ when :datetime then /\A#{regexp}\Z/
end end
matches = full.match(string.strip) break(proc) if matches = full.match(string.strip)
end end
last = options[:include_offset] ? 8 : 7 last = options[:include_offset] ? 8 : 7
processor.call(*matches[1..last]) if matches processor.call(*matches[1..last]) if matches
@ -258,7 +264,7 @@ module ValidatesTimeliness
end end
def compile_formats(formats) def compile_formats(formats)
formats.map { |format| regexp, format_proc = format_expression_generator(format) } formats.map { |format| [ format, *format_expression_generator(format) ] }
end end
# Pick expression set and combine date and datetimes for # Pick expression set and combine date and datetimes for

View File

@ -14,8 +14,8 @@ module ValidatesTimeliness
} }
VALID_OPTIONS = [ VALID_OPTIONS = [
:on, :if, :unless, :allow_nil, :empty, :allow_blank, :blank, :on, :if, :unless, :allow_nil, :empty, :allow_blank,
:with_time, :with_date, :ignore_usec, :with_time, :with_date, :ignore_usec, :format,
:invalid_time_message, :invalid_date_message, :invalid_datetime_message :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
@ -29,14 +29,17 @@ module ValidatesTimeliness
end end
def call(record, attr_name, value) 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 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]) 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, :blank) and return if raw_value.blank?
add_error(record, attr_name, "invalid_#{type}".to_sym) and return if value.nil?
add_error(record, attr_name, "invalid_#{type}".to_sym) and return unless value
validate_restrictions(record, attr_name, value) validate_restrictions(record, attr_name, value)
end end
@ -107,7 +110,6 @@ module ValidatesTimeliness
def add_error(record, attr_name, message, interpolate=nil) 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
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

View File

@ -100,7 +100,7 @@ describe ValidatesTimeliness::Formats do
end end
end end
describe "extracting values" do describe "parse" do
it "should return time array from date string" do it "should return time array from date string" do
time_array = formats.parse('12:13:14', :time, :strict => true) time_array = formats.parse('12:13:14', :time, :strict => true)
@ -143,11 +143,19 @@ describe ValidatesTimeliness::Formats do
end end
end end
describe "removing formats" do describe "parse with format option" do
before do it "should return values if string matches specified format" do
formats.compile_format_expressions 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 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 it "should remove format from format array" do
formats.remove_formats(:time, 'h.nn_ampm') formats.remove_formats(:time, 'h.nn_ampm')
formats.time_formats.should_not include("h o'clock") formats.time_formats.should_not include("h o'clock")
@ -165,7 +173,7 @@ describe ValidatesTimeliness::Formats do
after do after do
formats.time_formats << 'h.nn_ampm' formats.time_formats << 'h.nn_ampm'
# reload class instead formats.compile_format_expressions
end end
end end
@ -223,7 +231,7 @@ describe ValidatesTimeliness::Formats do
def validate(time_string, type) def validate(time_string, type)
valid = false 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 valid = true and break if /\A#{regexp}\Z/ =~ time_string
end end
valid valid

View File

@ -473,6 +473,20 @@ describe ValidatesTimeliness::Validator do
end end
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 describe "custom_error_messages" do
it "should return hash of custom error messages from configuration with _message truncated from keys" 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') configure_validator(:type => :date, :invalid_date_message => 'thats no date')