changed format of string used in multi param for invalid or partial values

This commit is contained in:
Adam Meehan 2010-01-12 22:05:14 +11:00
parent f1a0016bf7
commit 82c0e1bcd3
5 changed files with 150 additions and 56 deletions

View File

@ -6,43 +6,49 @@ module ValidatesTimeliness
module ActionView module ActionView
# Intercepts the date and time select helpers to allow the # Intercepts the date and time select helpers to allow the
# attribute value before type cast to be used as in the select helpers. # attribute value before type cast to be used as in the select helpers.
# This means that an invalid date or time will be redisplayed rather than the # This means that an invalid date or time will be redisplayed rather than the
# type cast value which would be nil if invalid. # type cast value which would be nil if invalid.
module InstanceTag #
# Its a minor user experience improvement to be able to see original value
# entered to aid correction.
#
module InstanceTag
def self.included(base) def self.included(base)
selector_method = Rails::VERSION::STRING < '2.2' ? :date_or_time_select : :datetime_selector selector_method = Rails::VERSION::STRING.to_f < 2.2 ? :date_or_time_select : :datetime_selector
base.class_eval do base.class_eval do
alias_method :datetime_selector_without_timeliness, selector_method alias_method :datetime_selector_without_timeliness, selector_method
alias_method selector_method, :datetime_selector_with_timeliness alias_method selector_method, :datetime_selector_with_timeliness
end end
base.alias_method_chain :value, :timeliness base.alias_method_chain :value, :timeliness
end end
TimelinessDateTime = Struct.new(:year, :month, :day, :hour, :min, :sec) TimelinessDateTime = Struct.new(:year, :month, :day, :hour, :min, :sec)
def datetime_selector_with_timeliness(*args) def datetime_selector_with_timeliness(*args)
@timeliness_date_or_time_tag = true @timeliness_date_or_time_tag = true
datetime_selector_without_timeliness(*args) datetime_selector_without_timeliness(*args)
end end
def value_with_timeliness(object) def value_with_timeliness(object)
return value_without_timeliness(object) unless @timeliness_date_or_time_tag return value_without_timeliness(object) unless @timeliness_date_or_time_tag
raw_value = value_before_type_cast(object) raw_value = value_before_type_cast(object)
if raw_value.nil? || raw_value.acts_like?(:time) || raw_value.is_a?(Date) if raw_value.nil? || raw_value.acts_like?(:time) || raw_value.is_a?(Date)
return value_without_timeliness(object) return value_without_timeliness(object)
end end
time_array = ValidatesTimeliness::Formats.parse(raw_value, :datetime) date, time = raw_value.split(' ')
date_array = date.split('-')
TimelinessDateTime.new(*time_array[0..5]) time_array = time.split(':')
end
TimelinessDateTime.new(*(date_array + time_array).map {|v| v.blank? ? nil : v.to_i})
end
end end
end end
end end

View File

@ -22,24 +22,23 @@ module ValidatesTimeliness
end end
def extract_date_from_multiparameter_attributes(values) def extract_date_from_multiparameter_attributes(values)
year = ValidatesTimeliness::Formats.unambiguous_year(values[0].rjust(2, "0")) year = values[0].blank? ? nil : ValidatesTimeliness::Formats.unambiguous_year(values[0].rjust(2, "0"))
[year, *values.slice(1, 2).map { |s| s.rjust(2, "0") }].join("-") [year, *values.slice(1, 2).map { |s| s.blank? ? nil : s.rjust(2, "0") }].join("-")
end end
def extract_time_from_multiparameter_attributes(values) def extract_time_from_multiparameter_attributes(values)
values[3..5].map { |s| s.rjust(2, "0") }.join(":") values[3..5].map { |s| s.blank? ? nil : s.rjust(2, "0") }.join(":")
end end
end end
module MultiparameterAttributes module MultiparameterAttributes
def self.included(base) def self.included(base)
base.alias_method_chain :execute_callstack_for_multiparameter_attributes, :timeliness base.alias_method_chain :execute_callstack_for_multiparameter_attributes, :timeliness
end end
# Assign dates and times as formatted strings to force the use of the plugin parser # Assign dates and times as formatted strings to force the use of the plugin parser
# and store a before_type_cast value for attribute
def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack) def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack)
errors = [] errors = []
callstack.each do |name, values| callstack.each do |name, values|
@ -47,7 +46,7 @@ module ValidatesTimeliness
if column && [:date, :time, :datetime].include?(column.type) if column && [:date, :time, :datetime].include?(column.type)
begin begin
callstack.delete(name) callstack.delete(name)
if values.empty? if values.empty? || values.all?(&:nil?)
send("#{name}=", nil) send("#{name}=", nil)
else else
value = ValidatesTimeliness::ActiveRecord.time_array_to_string(values, column.type) value = ValidatesTimeliness::ActiveRecord.time_array_to_string(values, column.type)
@ -63,7 +62,7 @@ module ValidatesTimeliness
end end
execute_callstack_for_multiparameter_attributes_without_timeliness(callstack) execute_callstack_for_multiparameter_attributes_without_timeliness(callstack)
end end
end end
end end

View File

@ -1,19 +1,17 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
ValidatesTimeliness.enable_datetime_select_extension!
describe 'ValidatesTimeliness::ActionView::InstanceTag' do describe 'ValidatesTimeliness::ActionView::InstanceTag' do
include ActionView::Helpers::DateHelper include ActionView::Helpers::DateHelper
include ActionController::Assertions::SelectorAssertions include ActionController::Assertions::SelectorAssertions
before do before do
@person = Person.new @person = Person.new
end end
it "should display invalid datetime as datetime_select values" do it "should display invalid datetime as datetime_select values" do
@person.birth_date_and_time = "2008-02-30 12:00:22" @person.birth_date_and_time = "2008-02-30 12:00:22"
output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true) output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true)
output.should have_tag('select[id=person_birth_date_and_time_1i]') do output.should have_tag('select[id=person_birth_date_and_time_1i]') do
with_tag('option[selected=selected]', '2008') with_tag('option[selected=selected]', '2008')
end end
@ -33,10 +31,33 @@ describe 'ValidatesTimeliness::ActionView::InstanceTag' do
with_tag('option[selected=selected]', '22') with_tag('option[selected=selected]', '22')
end end
end end
it "should display datetime_select when datetime value is nil" do it "should display datetime_select when datetime value is nil" do
@person.birth_date_and_time = nil @person.birth_date_and_time = nil
output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true) output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true)
output.should have_tag('select', 6) output.should have_tag('select', 6)
end end
it "should display datetime_select with no values selected for missing parts" do
@person.birth_date_and_time = '2000-- ::'
output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true)
output.should have_tag('select[id=person_birth_date_and_time_1i]') do
with_tag('option[selected=selected]')
end
output.should have_tag('select[id=person_birth_date_and_time_2i]') do
without_tag('option[selected=selected]')
end
output.should have_tag('select[id=person_birth_date_and_time_3i]') do
without_tag('option[selected=selected]')
end
output.should have_tag('select[id=person_birth_date_and_time_4i]') do
without_tag('option[selected=selected]')
end
output.should have_tag('select[id=person_birth_date_and_time_5i]') do
without_tag('option[selected=selected]')
end
output.should have_tag('select[id=person_birth_date_and_time_6i]') do
without_tag('option[selected=selected]')
end
end
end end

View File

@ -9,44 +9,110 @@ describe ValidatesTimeliness::ActiveRecord::MultiparameterAttributes do
time_string = time_array_to_string([2000,2,1,9,10,11], :datetime) time_string = time_array_to_string([2000,2,1,9,10,11], :datetime)
time_string.should == "2000-02-01 09:10:11" time_string.should == "2000-02-01 09:10:11"
end end
it "should convert array for date type into date string" do it "should convert array for date type into date string" do
time_string = time_array_to_string([2000,2,1], :date) time_string = time_array_to_string([2000,2,1], :date)
time_string.should == "2000-02-01" time_string.should == "2000-02-01"
end end
it "should convert array for time type into time string" do it "should convert array for time type into time string" do
time_string = time_array_to_string([2000,1,1,9,10,11], :time) time_string = time_array_to_string([2000,1,1,9,10,11], :time)
time_string.should == "09:10:11" time_string.should == "09:10:11"
end end
describe "execute_callstack_for_multiparameter_attributes" do describe "execute_callstack_for_multiparameter_attributes" do
before do
@callstack = { describe "for valid values" do
'birth_date_and_time' => [2000,2,1,9,10,11], before do
'birth_date' => [2000,2,1,9,10,11], @callstack = {
'birth_time' => [2000,2,1,9,10,11] 'birth_date_and_time' => [2000,2,1,9,10,11],
} 'birth_date' => [2000,2,1,9,10,11],
'birth_time' => [2000,2,1,9,10,11]
}
end
it "should store datetime string for datetime column" do
obj.should_receive(:birth_date_and_time=).once.with("2000-02-01 09:10:11")
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack)
end
it "should store date string for a date column" do
obj.should_receive(:birth_date=).once.with("2000-02-01")
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack)
end
it "should store time string for a time column" do
obj.should_receive(:birth_time=).once.with("09:10:11")
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack)
end
end end
it "should store datetime string for datetime column" do describe "for invalid values" do
obj.should_receive(:birth_date_and_time=).once.with("2000-02-01 09:10:11") before do
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack) @callstack = {
end 'birth_date_and_time' => [2000,13,1,9,10,11],
'birth_date' => [2000,2,41,9,10,11],
it "should store date string for a date column" do 'birth_time' => [2000,2,1,25,10,11]
obj.should_receive(:birth_date=).once.with("2000-02-01") }
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack) end
it "should store invalid datetime string for datetime column" do
obj.should_receive(:birth_date_and_time=).once.with("2000-13-01 09:10:11")
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack)
end
it "should store invalid date string for a date column" do
obj.should_receive(:birth_date=).once.with("2000-02-41")
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack)
end
it "should store invalid time string for a time column" do
obj.should_receive(:birth_time=).once.with("25:10:11")
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack)
end
end end
it "should store time string for a time column" do describe "for missing values" do
obj.should_receive(:birth_time=).once.with("09:10:11") it "should store nil if all datetime values nil" do
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack) obj.should_receive(:birth_date_and_time=).once.with(nil)
callstack = { 'birth_date_and_time' => [nil,nil,nil,nil,nil,nil] }
obj.send(:execute_callstack_for_multiparameter_attributes, callstack)
end
it "should store nil year as empty value in string" do
obj.should_receive(:birth_date_and_time=).once.with("-02-01 09:10:11")
callstack = { 'birth_date_and_time' => [nil,2,1,9,10,11] }
obj.send(:execute_callstack_for_multiparameter_attributes, callstack)
end
it "should store nil month as empty value in string" do
obj.should_receive(:birth_date_and_time=).once.with("2000--01 09:10:11")
callstack = { 'birth_date_and_time' => [2000,nil,1,9,10,11] }
obj.send(:execute_callstack_for_multiparameter_attributes, callstack)
end
it "should store nil day as empty value in string" do
obj.should_receive(:birth_date_and_time=).once.with("2000-02- 09:10:11")
callstack = { 'birth_date_and_time' => [2000,2,nil,9,10,11] }
obj.send(:execute_callstack_for_multiparameter_attributes, callstack)
end
it "should store nil hour as empty value in string" do
obj.should_receive(:birth_date_and_time=).once.with("2000-02-01 :10:11")
callstack = { 'birth_date_and_time' => [2000,2,1,nil,10,11] }
obj.send(:execute_callstack_for_multiparameter_attributes, callstack)
end
it "should store nil minute as empty value in string" do
obj.should_receive(:birth_date_and_time=).once.with("2000-02-01 09:10:")
callstack = { 'birth_date_and_time' => [2000,2,1,9,10,nil] }
obj.send(:execute_callstack_for_multiparameter_attributes, callstack)
end
end end
end end
def time_array_to_string(*args) def time_array_to_string(*args)
ValidatesTimeliness::ActiveRecord.time_array_to_string(*args) ValidatesTimeliness::ActiveRecord.time_array_to_string(*args)
end end
end end

View File

@ -13,7 +13,7 @@ if vendored = File.exists?(vendored_rails)
Dir.glob(vendored_rails + "/**/lib").each { |dir| $:.unshift dir } Dir.glob(vendored_rails + "/**/lib").each { |dir| $:.unshift dir }
else else
begin begin
require 'ginger' require 'ginger'
rescue LoadError rescue LoadError
end end
if ENV['VERSION'] if ENV['VERSION']
@ -47,6 +47,8 @@ end
require 'validates_timeliness' require 'validates_timeliness'
require 'validates_timeliness/matcher' require 'validates_timeliness/matcher'
ValidatesTimeliness.enable_datetime_select_extension!
ActiveRecord::Migration.verbose = false ActiveRecord::Migration.verbose = false
ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'}) ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})