From 82c0e1bcd32df47a30c8614769e0742f08bb8afd Mon Sep 17 00:00:00 2001 From: Adam Meehan Date: Tue, 12 Jan 2010 22:05:14 +1100 Subject: [PATCH] changed format of string used in multi param for invalid or partial values --- .../action_view/instance_tag.rb | 40 +++--- .../multiparameter_attributes.rb | 15 ++- spec/action_view/instance_tag_spec.rb | 33 ++++- .../multiparameter_attributes_spec.rb | 114 ++++++++++++++---- spec/spec_helper.rb | 4 +- 5 files changed, 150 insertions(+), 56 deletions(-) diff --git a/lib/validates_timeliness/action_view/instance_tag.rb b/lib/validates_timeliness/action_view/instance_tag.rb index 48f4c5b..9201d48 100644 --- a/lib/validates_timeliness/action_view/instance_tag.rb +++ b/lib/validates_timeliness/action_view/instance_tag.rb @@ -6,43 +6,49 @@ module ValidatesTimeliness 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. # This means that an invalid date or time will be redisplayed rather than the # 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) - 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 alias_method :datetime_selector_without_timeliness, selector_method alias_method selector_method, :datetime_selector_with_timeliness end base.alias_method_chain :value, :timeliness 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) @timeliness_date_or_time_tag = true datetime_selector_without_timeliness(*args) end - + def value_with_timeliness(object) return value_without_timeliness(object) unless @timeliness_date_or_time_tag - + raw_value = value_before_type_cast(object) - + if raw_value.nil? || raw_value.acts_like?(:time) || raw_value.is_a?(Date) return value_without_timeliness(object) end - - time_array = ValidatesTimeliness::Formats.parse(raw_value, :datetime) - - TimelinessDateTime.new(*time_array[0..5]) - end - + + date, time = raw_value.split(' ') + date_array = date.split('-') + time_array = time.split(':') + + TimelinessDateTime.new(*(date_array + time_array).map {|v| v.blank? ? nil : v.to_i}) + end + end - end + end end diff --git a/lib/validates_timeliness/active_record/multiparameter_attributes.rb b/lib/validates_timeliness/active_record/multiparameter_attributes.rb index 463eaa8..aafd39b 100644 --- a/lib/validates_timeliness/active_record/multiparameter_attributes.rb +++ b/lib/validates_timeliness/active_record/multiparameter_attributes.rb @@ -22,24 +22,23 @@ module ValidatesTimeliness end def extract_date_from_multiparameter_attributes(values) - year = ValidatesTimeliness::Formats.unambiguous_year(values[0].rjust(2, "0")) - [year, *values.slice(1, 2).map { |s| s.rjust(2, "0") }].join("-") + year = values[0].blank? ? nil : ValidatesTimeliness::Formats.unambiguous_year(values[0].rjust(2, "0")) + [year, *values.slice(1, 2).map { |s| s.blank? ? nil : s.rjust(2, "0") }].join("-") end 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 module MultiparameterAttributes - + def self.included(base) 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 - # and store a before_type_cast value for attribute def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack) errors = [] callstack.each do |name, values| @@ -47,7 +46,7 @@ module ValidatesTimeliness if column && [:date, :time, :datetime].include?(column.type) begin callstack.delete(name) - if values.empty? + if values.empty? || values.all?(&:nil?) send("#{name}=", nil) else value = ValidatesTimeliness::ActiveRecord.time_array_to_string(values, column.type) @@ -63,7 +62,7 @@ module ValidatesTimeliness end execute_callstack_for_multiparameter_attributes_without_timeliness(callstack) end - + end end diff --git a/spec/action_view/instance_tag_spec.rb b/spec/action_view/instance_tag_spec.rb index 435ca62..4984f27 100644 --- a/spec/action_view/instance_tag_spec.rb +++ b/spec/action_view/instance_tag_spec.rb @@ -1,19 +1,17 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') -ValidatesTimeliness.enable_datetime_select_extension! - describe 'ValidatesTimeliness::ActionView::InstanceTag' do include ActionView::Helpers::DateHelper include ActionController::Assertions::SelectorAssertions - + before do @person = Person.new end - + it "should display invalid datetime as datetime_select values" do @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.should have_tag('select[id=person_birth_date_and_time_1i]') do with_tag('option[selected=selected]', '2008') end @@ -33,10 +31,33 @@ describe 'ValidatesTimeliness::ActionView::InstanceTag' do with_tag('option[selected=selected]', '22') end end - + it "should display datetime_select when datetime value is nil" do @person.birth_date_and_time = nil output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true) output.should have_tag('select', 6) 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 diff --git a/spec/active_record/multiparameter_attributes_spec.rb b/spec/active_record/multiparameter_attributes_spec.rb index 17032e9..100cd27 100644 --- a/spec/active_record/multiparameter_attributes_spec.rb +++ b/spec/active_record/multiparameter_attributes_spec.rb @@ -9,44 +9,110 @@ describe ValidatesTimeliness::ActiveRecord::MultiparameterAttributes do time_string = time_array_to_string([2000,2,1,9,10,11], :datetime) time_string.should == "2000-02-01 09:10:11" end - + it "should convert array for date type into date string" do time_string = time_array_to_string([2000,2,1], :date) time_string.should == "2000-02-01" end - + 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.should == "09:10:11" end - - describe "execute_callstack_for_multiparameter_attributes" do - before do - @callstack = { - '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] - } + + describe "execute_callstack_for_multiparameter_attributes" do + + describe "for valid values" do + before do + @callstack = { + '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 - - 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) + + describe "for invalid values" do + before do + @callstack = { + 'birth_date_and_time' => [2000,13,1,9,10,11], + 'birth_date' => [2000,2,41,9,10,11], + 'birth_time' => [2000,2,1,25,10,11] + } + 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 - - 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) + + describe "for missing values" do + it "should store nil if all datetime values nil" do + 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 def time_array_to_string(*args) ValidatesTimeliness::ActiveRecord.time_array_to_string(*args) end - + end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 54cbfa9..def92d1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -13,7 +13,7 @@ if vendored = File.exists?(vendored_rails) Dir.glob(vendored_rails + "/**/lib").each { |dir| $:.unshift dir } else begin - require 'ginger' + require 'ginger' rescue LoadError end if ENV['VERSION'] @@ -47,6 +47,8 @@ end require 'validates_timeliness' require 'validates_timeliness/matcher' +ValidatesTimeliness.enable_datetime_select_extension! + ActiveRecord::Migration.verbose = false ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})