added compile_set method to get expressions and combine date and datetime expressions to allow a date string to parse when type is datetime

made internal methods private
This commit is contained in:
Adam Meehan 2008-07-23 12:39:20 +10:00
parent 85ac2bfc69
commit 7500781887
2 changed files with 97 additions and 70 deletions

View File

@ -83,15 +83,15 @@ module ValidatesTimeliness
]
@@datetime_formats = [
'yyyy-mm-dd hh:nn:ss.u',
'yyyy-mm-dd hh:nn:ss',
'yyyy-mm-dd h:nn',
'yyyy-mm-dd hh:nn:ss.u',
'm/d/yy h:nn:ss',
'm/d/yy h:nn',
'm/d/yy h:nn_ampm',
'm/d/yy h:nn',
'd/m/yy hh:nn:ss',
'd/m/yy h:nn',
'd/m/yy h:nn_ampm',
'd/m/yy h:nn',
'ddd, dd mmm yyyy hh:nn:ss (zo|tz)', # RFC 822
'ddd mmm d hh:nn:ss zo yyyy', # Ruby time string
'yyyy-mm-ddThh:nn:ss(?:Z|zo)' # iso 8601
@ -149,8 +149,76 @@ module ValidatesTimeliness
:meridian => [nil, 'md', nil]
}
class << self
class << self
def compile_format_expressions
@@time_expressions = compile_formats(@@time_formats)
@@date_expressions = compile_formats(@@date_formats)
@@datetime_expressions = compile_formats(@@datetime_formats)
end
# 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.
def parse(string, type, strict=true)
return string unless string.is_a?(String)
expressions = expression_set(type, string)
time_array = nil
expressions.each do |(regexp, processor)|
regexp = strict || type == :datetime ? /\A#{regexp}\Z/ : (type == :date ? /\A#{regexp}/ : /#{regexp}\Z/)
if matches = regexp.match(string.strip)
time_array = processor.call(*matches[1..7])
break
end
end
return time_array
end
# Delete formats of specified type. Error raised if format not found.
def remove_formats(type, *remove_formats)
remove_formats.each do |format|
unless self.send("#{type}_formats").delete(format)
raise "Format #{format} not found in #{type} formats"
end
end
compile_format_expressions
end
# Adds new formats. Must specify format type and can specify a :before
# option to nominate which format the new formats should be inserted in
# front on to take higher precedence.
# Error is raise if format already exists or if :before format is not found.
def add_formats(type, *add_formats)
formats = self.send("#{type}_formats")
options = {}
options = add_formats.pop if add_formats.last.is_a?(Hash)
before = options[:before]
raise "Format for :before option #{format} was not found." if before && !formats.include?(before)
add_formats.each do |format|
raise "Format #{format} is already included in #{type} formats" if formats.include?(format)
index = before ? formats.index(before) : -1
formats.insert(index, format)
end
compile_format_expressions
end
# Removes formats where the 1 or 2 digit month comes first, to eliminate
# formats which are ambiguous with the European style of day then month.
# The mmm token is ignored as its not ambigous.
def remove_us_formats
us_format_regexp = /\Am{1,2}[^m]/
date_formats.reject! { |format| us_format_regexp =~ format }
datetime_formats.reject! { |format| us_format_regexp =~ format }
compile_format_expressions
end
private
# Compile formats into validation regexps and format procs
def format_expression_generator(string_format)
regexp = string_format.dup
@ -195,71 +263,25 @@ module ValidatesTimeliness
def compile_formats(formats)
formats.collect { |format| regexp, format_proc = format_expression_generator(format) }
end
def compile_format_expressions
@@time_expressions = compile_formats(@@time_formats)
@@date_expressions = compile_formats(@@date_formats)
@@datetime_expressions = compile_formats(@@datetime_formats)
end
# 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.
def parse(time_string, type, strict=true)
expressions = self.send("#{type}_expressions")
time_array = nil
expressions.each do |(regexp, processor)|
regexp = strict || type == :datetime ? /\A#{regexp}\Z/ : (type == :date ? /\A#{regexp}/ : /#{regexp}\Z/)
if matches = regexp.match(time_string.strip)
time_array = processor.call(*matches[1..7])
break
end
# Pick expression set and combine date and datetimes for
# datetime attributes to allow date string as datetime
def expression_set(type, string)
case type
when :date
date_expressions
when :time
time_expressions
when :datetime
# gives a speed-up for date string as datetime attributes
if string.length < 11
date_expressions + datetime_expressions
else
datetime_expressions + date_expressions
end
end
return time_array
end
# Delete formats of specified type. Error raised if format not found.
def remove_formats(type, *remove_formats)
remove_formats.each do |format|
unless self.send("#{type}_formats").delete(format)
raise "Format #{format} not found in #{type} formats"
end
end
compile_format_expressions
end
# Adds new formats. Must specify format type and can specify a :before
# option to nominate which format the new formats should be inserted in
# front on to take higher precedence.
# Error is raise if format already exists or if :before format is not found.
def add_formats(type, *add_formats)
formats = self.send("#{type}_formats")
options = {}
options = add_formats.pop if add_formats.last.is_a?(Hash)
before = options[:before]
raise "Format for :before option #{format} was not found." if before && !formats.include?(before)
add_formats.each do |format|
raise "Format #{format} is already included in #{type} formats" if formats.include?(format)
index = before ? formats.index(before) : -1
formats.insert(index, format)
end
compile_format_expressions
end
# Removes formats where the 1 or 2 digit month comes first, to eliminate
# formats which are ambiguous with the European style of day then month.
# The mmm token is ignored as its not ambigous.
def remove_us_formats
us_format_regexp = /\Am{1,2}[^m]/
date_formats.reject! { |format| us_format_regexp =~ format }
datetime_formats.reject! { |format| us_format_regexp =~ format }
compile_format_expressions
end
def full_hour(hour, meridian)
hour = hour.to_i
return hour if meridian.nil?

View File

@ -153,6 +153,11 @@ describe ValidatesTimeliness::Formats do
time_array.should == [2000,2,1,12,13,14,0]
end
it "should parse date string when type is datetime" do
time_array = formats.parse('2000-02-01', :datetime, false)
time_array.should == [2000,2,1,0,0,0,0]
end
it "should ignore time when extracting date and strict is false" do
time_array = formats.parse('2000-02-01 12:12', :date, false)
time_array.should == [2000,2,1,0,0,0,0]
@ -252,15 +257,15 @@ describe ValidatesTimeliness::Formats do
def generate_regexp(format)
# wrap in line start and end anchors to emulate extract values method
/\A#{formats.format_expression_generator(format)[0]}\Z/
/\A#{formats.send(:format_expression_generator, format)[0]}\Z/
end
def generate_regexp_str(format)
formats.format_expression_generator(format)[0].inspect
formats.send(:format_expression_generator, format)[0].inspect
end
def generate_proc(format)
formats.format_expression_generator(format)[1]
formats.send(:format_expression_generator, format)[1]
end
def delete_format(type, format)