added :before option to add_formats to insert above existing format

This commit is contained in:
Adam Meehan 2008-07-20 07:45:22 +10:00
parent 53d57cb7ac
commit 5f55fad076
4 changed files with 67 additions and 21 deletions

16
README
View File

@ -94,7 +94,7 @@ as dates.
validates_datetime :appointment_date, :before => Proc.new { 1.week.from_now } validates_datetime :appointment_date, :before => Proc.new { 1.week.from_now }
== DATE/TIME FORMATS: === DATE/TIME FORMATS:
So what formats does the plugin allow. Well there are default formats which can So what formats does the plugin allow. Well there are default formats which can
be added to easily using the plugins format rules. Also formats can be easily be added to easily using the plugins format rules. Also formats can be easily
@ -175,8 +175,9 @@ For the technically minded (well you are developers), these formats are compiled
into regular expressions at runtime so don't add any extra overhead than using into regular expressions at runtime so don't add any extra overhead than using
regular expressions directly. So, no, it won't make your app slow! regular expressions directly. So, no, it won't make your app slow!
To see all defined formats look in the lib/validates_timeliness/formats.rb.
== CUSTOMISING FORMATS: === CUSTOMISING FORMATS:
I hear you say "Thats greats but I don't want X format to be valid". Well to I hear you say "Thats greats but I don't want X format to be valid". Well to
remove a format stick this in an initializer file or environment.rb remove a format stick this in an initializer file or environment.rb
@ -192,8 +193,17 @@ Ahh, then add it yourself. Again stick this in an initializer file or environmen
Now '10 o'clock' will be a valid value. So easy, no more whingeing! Now '10 o'clock' will be a valid value. So easy, no more whingeing!
Because formats are evaluated in order, adding a format which may be ambiguous
with an existing format, will mean your format is ignored. If you need to make
your new format higher precedence than an existing format, you can include the
before option like so
== EXTERNAL PARSER: ValidatesTimeliness::Formats.add_formats(:time, 'ss:nn:hh', :before => 'hh:nn:ss')
Now a time of '59:30:23' will be interpreted as 11:30:59 pm. This option saves
you adding a new one and deleting an old one to get it to work.
=== EXTERNAL PARSER:
I mentioned earlier that you could use a pluggable or alternative parser such I mentioned earlier that you could use a pluggable or alternative parser such
as Chronic instead of the in built one. So if you need some super fancy stuff that as Chronic instead of the in built one. So if you need some super fancy stuff that

View File

@ -179,15 +179,15 @@ module ValidatesTimeliness
# #
# Examples: # Examples:
# #
# 'yyyy-mm-dd hh:nn' => lambda {|y,m,d,h,n| md||=0; [unambiguous_year(y),month_index(m),d,full_hour(h,md),n,nil,nil].map {|t| t.to_i unless t.nil? } } # 'yyyy-mm-dd hh:nn' => lambda {|y,m,d,h,n| md||=0; [unambiguous_year(y),month_index(m),d,full_hour(h,md),n,nil,nil].map {|t| t.to_i if t } }
# 'dd/mm/yyyy h:nn_ampm' => lambda {|d,m,y,h,n,md| md||=0; [unambiguous_year(y),month_index(m),d,full_hour(h,md),n,nil,nil].map {|t| t.to_i unless t.nil? } } # 'dd/mm/yyyy h:nn_ampm' => lambda {|d,m,y,h,n,md| md||=0; [unambiguous_year(y),month_index(m),d,full_hour(h,md),n,nil,nil].map {|t| t.to_i if t } }
# #
def format_proc(order) def format_proc(order)
arg_map = format_proc_args arg_map = format_proc_args
args = order.invert.sort.map {|p| arg_map[p[1]][1] } args = order.invert.sort.map {|p| arg_map[p[1]][1] }
arr = [nil] * 7 arr = [nil] * 7
order.keys.each {|k| i = arg_map[k][0]; arr[i] = arg_map[k][2] unless i.nil? } order.keys.each {|k| i = arg_map[k][0]; arr[i] = arg_map[k][2] unless i.nil? }
proc_string = "lambda {|#{args.join(',')}| md||=nil; [#{arr.map {|i| i.nil? ? 'nil' : i }.join(',')}].map {|t| t.to_i unless t.nil? } }" proc_string = "lambda {|#{args.join(',')}| md||=nil; [#{arr.map {|i| i.nil? ? 'nil' : i }.join(',')}].map {|t| t.to_i if t } }"
eval proc_string eval proc_string
end end
@ -202,7 +202,8 @@ module ValidatesTimeliness
end end
# 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. # pre or post match strings to exist if strict is false. Otherwise wrap
# regexp in start and end anchors.
# Returns 7 part datetime array. # Returns 7 part datetime array.
def extract_date_time_values(time_string, type, strict=true) def extract_date_time_values(time_string, type, strict=true)
expressions = self.send("#{type}_expressions") expressions = self.send("#{type}_expressions")
@ -217,6 +218,7 @@ module ValidatesTimeliness
return time_array return time_array
end end
# Delete formats of specified type. Error raised if format not found.
def remove_formats(type, *remove_formats) def remove_formats(type, *remove_formats)
remove_formats.each do |format| remove_formats.each do |format|
unless self.send("#{type}_formats").delete(format) unless self.send("#{type}_formats").delete(format)
@ -226,14 +228,22 @@ module ValidatesTimeliness
compile_format_expressions compile_format_expressions
end 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) def add_formats(type, *add_formats)
formats = self.send("#{type}_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| add_formats.each do |format|
if formats.include?(format) raise "Format #{format} is already included in #{type} formats" if formats.include?(format)
raise "Format #{format} is already included in #{type} formats"
end index = before ? formats.index(before) : -1
formats << format formats.insert(index, format)
end end
compile_format_expressions compile_format_expressions
end end

View File

@ -180,6 +180,10 @@ describe ValidatesTimeliness::Formats do
validate('2.12am', :time).should be_false validate('2.12am', :time).should be_false
end end
it "should raise error if format does not exist" do
lambda { formats.remove_formats(:time, "ss:hh:nn") }.should raise_error()
end
after do after do
formats.time_formats << 'h.nn_ampm' formats.time_formats << 'h.nn_ampm'
end end
@ -190,7 +194,7 @@ describe ValidatesTimeliness::Formats do
formats.compile_format_expressions formats.compile_format_expressions
end end
it "should add format to format array" do it "should add format to format array" do
formats.add_formats(:time, "h o'clock") formats.add_formats(:time, "h o'clock")
formats.time_formats.should include("h o'clock") formats.time_formats.should include("h o'clock")
end end
@ -201,8 +205,24 @@ describe ValidatesTimeliness::Formats do
validate("12 o'clock", :time).should be_true validate("12 o'clock", :time).should be_true
end end
it "should add format before specified format and be higher precedence" do
formats.add_formats(:time, "ss:hh:nn", :before => 'hh:nn:ss')
validate("59:23:58", :time).should be_true
time_array = formats.extract_date_time_values('59:23:58', :time)
time_array.should == [nil,nil,nil,23,58,59,nil]
end
it "should raise error if format exists" do
lambda { formats.add_formats(:time, "hh:nn:ss") }.should raise_error()
end
it "should raise error if format exists" do
lambda { formats.add_formats(:time, "ss:hh:nn", :before => 'nn:hh:ss') }.should raise_error()
end
after do after do
formats.time_formats.delete("h o'clock") formats.time_formats.delete("h o'clock")
formats.time_formats.delete("ss:hh:nn")
end end
end end

View File

@ -53,10 +53,12 @@ describe ValidatesTimeliness::Validations do
@person.should be_valid @person.should be_valid
end end
it "should be valid with values before epoch" do # What is going on? No fall back.
@person.birth_date_and_time = "1960-01-31 12:12:12" it "should be valid with values before out of Time range" do
@person.birth_date = "1960-01-31" @person.birth_date_and_time = "1890-01-31 12:12:12"
@person.birth_time = "23:59" @person.birth_date = "1890-01-31"
@person.birth_time = "23:59:59"
puts @person.errors.inspect
@person.should be_valid @person.should be_valid
end end
@ -74,7 +76,7 @@ describe ValidatesTimeliness::Validations do
before :all do before :all do
class DateTimeBeforeAfter < Person class DateTimeBeforeAfter < Person
validates_timeliness_of :birth_date_and_time, :type => :datetime, validates_timeliness_of :birth_date_and_time, :type => :datetime,
:before => Time.now, :after => 1.day.ago :before => lambda { Time.now }, :after => lambda { 1.day.ago}
end end
end end
@ -111,8 +113,8 @@ describe ValidatesTimeliness::Validations do
before :all do before :all do
class DateTimeOnOrBeforeAndAfter < Person class DateTimeOnOrBeforeAndAfter < Person
validates_timeliness_of :birth_date_and_time, :type => :datetime, validates_timeliness_of :birth_date_and_time, :type => :datetime,
:on_or_before => Time.now.at_midnight, :on_or_before => lambda { Time.now.at_midnight },
:on_or_after => 1.day.ago :on_or_after => lambda { 1.day.ago }
end end
end end
@ -311,8 +313,12 @@ describe ValidatesTimeliness::Validations do
describe "with mixed value and restriction types" do describe "with mixed value and restriction types" do
before :all do before :all do
class MixedBeforeAndAfter < Person class MixedBeforeAndAfter < Person
validates_timeliness_of :birth_date_and_time, :before => Date.new(2008,1,2), :after => lambda { Time.mktime(2008, 1, 1) } validates_timeliness_of :birth_date_and_time,
validates_timeliness_of :birth_date, :type => :date, :on_or_before => Time.mktime(2008, 1, 2), :on_or_after => :birth_date_and_time :before => Date.new(2008,1,2),
:after => lambda { Time.mktime(2008, 1, 1) }
validates_timeliness_of :birth_date, :type => :date,
:on_or_before => lambda { Time.mktime(2008, 1, 2) },
:on_or_after => :birth_date_and_time
end end
end end