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 }
== DATE/TIME FORMATS:
=== DATE/TIME FORMATS:
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
@ -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
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
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!
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
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:
#
# '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? } }
# '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? } }
# '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 if t } }
#
def format_proc(order)
arg_map = format_proc_args
args = order.invert.sort.map {|p| arg_map[p[1]][1] }
arr = [nil] * 7
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
end
@ -202,7 +202,8 @@ module ValidatesTimeliness
end
# 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.
def extract_date_time_values(time_string, type, strict=true)
expressions = self.send("#{type}_expressions")
@ -217,6 +218,7 @@ module ValidatesTimeliness
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)
@ -226,14 +228,22 @@ module ValidatesTimeliness
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|
if formats.include?(format)
raise "Format #{format} is already included in #{type} formats"
end
formats << 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

View File

@ -180,6 +180,10 @@ describe ValidatesTimeliness::Formats do
validate('2.12am', :time).should be_false
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
formats.time_formats << 'h.nn_ampm'
end
@ -190,7 +194,7 @@ describe ValidatesTimeliness::Formats do
formats.compile_format_expressions
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.time_formats.should include("h o'clock")
end
@ -201,8 +205,24 @@ describe ValidatesTimeliness::Formats do
validate("12 o'clock", :time).should be_true
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
formats.time_formats.delete("h o'clock")
formats.time_formats.delete("ss:hh:nn")
end
end

View File

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