From 685b0054febfc1818ab3099be09013d8d558a409 Mon Sep 17 00:00:00 2001 From: Adam Meehan Date: Thu, 22 May 2008 07:35:01 +1000 Subject: [PATCH] set time zone aware attributes on in spec helper for AR --- lib/validates_timeliness/attribute_methods.rb | 30 +++++ lib/validates_timeliness/base.rb | 31 ----- spec/attribute_methods_spec.rb | 117 +++++++++++++----- spec/base_spec.rb | 38 ------ spec/spec_helper.rb | 4 +- 5 files changed, 119 insertions(+), 101 deletions(-) diff --git a/lib/validates_timeliness/attribute_methods.rb b/lib/validates_timeliness/attribute_methods.rb index 5522dfc..eaaf543 100644 --- a/lib/validates_timeliness/attribute_methods.rb +++ b/lib/validates_timeliness/attribute_methods.rb @@ -22,7 +22,37 @@ module ValidatesTimeliness end end + def strict_time_type_cast(time) + unless time.acts_like?(:time) + # check for invalid date + time.to_date rescue time = nil + # convert to time if still valid + time = defined?(ActiveSupport::TimeWithZone) ? Time.zone.parse(time) : time.to_time rescue nil + end + time.respond_to?(:in_time_zone) ? time.in_time_zone : time + end + + def read_attribute(attr_name) + attr_name = attr_name.to_s + if !(value = @attributes[attr_name]).nil? + if column = column_for_attribute(attr_name) + if unserializable_attribute?(attr_name, column) + unserialize_attribute(attr_name) + elsif column.klass == Time + strict_time_type_cast(value) + else + column.type_cast(value) + end + else + value + end + else + nil + end + end + module ClassMethods + # Rails > 2.0.2 module New # Store time value as is including as a string. Only convert on read diff --git a/lib/validates_timeliness/base.rb b/lib/validates_timeliness/base.rb index 22d8634..2a76d7b 100644 --- a/lib/validates_timeliness/base.rb +++ b/lib/validates_timeliness/base.rb @@ -31,36 +31,5 @@ module ValidatesTimeliness end end - def strict_time_type_cast(time) - if time.acts_like?(:time) - time.respond_to?(:in_time_zone) ? time.in_time_zone : time - else - klass = ActiveRecord::ConnectionAdapters::Column - # check for invalid date - time = nil unless klass.string_to_date(time) - # convert to time if still valid - time = klass.string_to_time(time) if time - end - end - - def read_attribute(attr_name) - attr_name = attr_name.to_s - if !(value = @attributes[attr_name]).nil? - if column = column_for_attribute(attr_name) - if unserializable_attribute?(attr_name, column) - unserialize_attribute(attr_name) - elsif column.klass == Time - strict_time_type_cast(value) - else - column.type_cast(value) - end - else - value - end - else - nil - end - end - end end diff --git a/spec/attribute_methods_spec.rb b/spec/attribute_methods_spec.rb index 7aee83d..4bb93d8 100644 --- a/spec/attribute_methods_spec.rb +++ b/spec/attribute_methods_spec.rb @@ -1,45 +1,100 @@ require File.dirname(__FILE__) + '/spec_helper' describe ValidatesTimeliness::AttributeMethods do - - describe "for Time columns" do - before do - @person = Person.new + before do + @person = Person.new + end + + describe "read_attribute" do + it "should return time object from time string" do + @attributes = {} + self.stub!(:column_for_attribute).and_return( mock('Column', :klass => Time) ) + self.stub!(:unserializable_attribute?).and_return(false) + + @attributes['birth_date_and_time'] = "1980-01-01 00:00:00" + read_attribute(:birth_date_and_time).should be_kind_of(Time) end - it "should return string value for attribute_before_type_cast when written as string" do + it "should return nil from invalid time string" do + @attributes = {} + self.stub!(:column_for_attribute).and_return( mock('Column', :klass => Time) ) + self.stub!(:unserializable_attribute?).and_return(false) + + @attributes['birth_date_and_time'] = "1980-02-30 00:00:00" + read_attribute(:birth_date_and_time).should be_nil + end + end + + describe "strict_time_type_cast" do + it "should return time object for valid time string" do + strict_time_type_cast("2000-01-01 12:13:14").should be_kind_of(Time) + end + + it "should return nil for time string with invalid date part" do + strict_time_type_cast("2000-02-30 12:13:14").should be_nil + end + + it "should return nil for time string with invalid time part" do + strict_time_type_cast("2000-02-01 25:13:14").should be_nil + end + + it "should return time object for time object" do + strict_time_type_cast(Time.now).should be_kind_of(Time) + end + + it "should convert time string into current timezone" do + time = strict_time_type_cast("2000-01-01 12:13:14") + Time.zone.utc_offset.should == 0 + time.zone.should == 'UTC' + end + end + + it "should return string value for attribute_before_type_cast when written as string" do + time_string = "2000-06-01 01:02:03" + @person.birth_date_and_time = time_string + @person.birth_date_and_time_before_type_cast.should == time_string + end + + it "should return Time object for attribute_before_type_cast when written as Time" do + @person.birth_date_and_time = Time.mktime(2000, 06, 01, 1, 2, 3) + @person.birth_date_and_time_before_type_cast.should be_kind_of(Time) + end + + it "should return Time object using attribute read method when written with string" do + @person.birth_date_and_time = "2000-06-01 01:02:03" + @person.birth_date_and_time.should be_kind_of(Time) + end + + # This fails running as plugin under vendor using Rails 2.1RC + # due to write_attribute_with_dirty ignoring the write method for time zone + # method. But invalid dates do return nil when running app. + it "should return nil when time is invalid" do + @person.birth_date_and_time = "2000-02-30 01:02:03" + @person.birth_date_and_time.should be_nil + end + + unless Rails::VERSION::STRING <= '2.0.2' + it "should return stored time string as Time with correct timezone" do + Time.zone = 'Melbourne' time_string = "2000-06-01 01:02:03" @person.birth_date_and_time = time_string - @person.birth_date_and_time_before_type_cast.should == time_string + @person.birth_date_and_time.utc_offset.should == 10.hours + @person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S').should == time_string end - - it "should return Time object for attribute_before_type_cast when written as Time" do - @person.birth_date_and_time = Time.mktime(2000, 06, 01, 1, 2, 3) - @person.birth_date_and_time_before_type_cast.should be_kind_of(Time) - end - - it "should return Time object using attribute read method when written with string" do - @person.birth_date_and_time = "2000-06-01 01:02:03" - @person.birth_date_and_time.should be_kind_of(Time) - end - + end + + describe "time attribute persistance" do unless Rails::VERSION::STRING <= '2.0.2' - it "should return stored time string as Time with correct timezone" do - Time.zone = TimeZone['Melbourne'] - time_string = "2000-06-01 01:02:03" + it "should return time object from database in correct timezone" do + Time.zone = 'Melbourne' + time_string = "1980-06-01 09:00:00" + @person = Person.new @person.birth_date_and_time = time_string - @person.birth_date_and_time.utc_offset.should == 10.hours - @person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S').should == time_string + @person.save + @person.reload + @person.birth_date_and_time.to_s.should == time_string end end - - # This fails running as plugin under vendor using Rails 2.1RC - # due to write_attribute_with_dirty ignoring the write method for time zone - # method. But invalid dates do return nil when running app. - it "should return nil when time is invalid" do - @person.birth_date_and_time = "2000-02-30 01:02:03" - @person.birth_date_and_time.should be_nil - end - end + end diff --git a/spec/base_spec.rb b/spec/base_spec.rb index 83540fd..0b19d19 100644 --- a/spec/base_spec.rb +++ b/spec/base_spec.rb @@ -7,7 +7,6 @@ describe ValidatesTimeliness::Base do before do self.class.stub!(:reflect_on_aggregation).and_return(nil) - end it "should convert time array into string" do @@ -35,41 +34,4 @@ describe ValidatesTimeliness::Base do end end - describe "strict_time_type_cast" do - it "should return time object for valid time string" do - strict_time_type_cast("2000-01-01 12:13:14").should be_kind_of(Time) - end - - it "should return nil for time string with invalid date part" do - strict_time_type_cast("2000-02-30 12:13:14").should be_nil - end - - it "should return nil for time string with invalid time part" do - strict_time_type_cast("2000-02-01 25:13:14").should be_nil - end - - it "should return time object for time object" do - strict_time_type_cast(Time.now).should be_kind_of(Time) - end - end - - describe "read_attribute" do - it "should return time object from time string" do - @attributes = {} - self.stub!(:column_for_attribute).and_return( mock('Column', :klass => Time) ) - self.stub!(:unserializable_attribute?).and_return(false) - - @attributes['birth_date_and_time'] = "1980-01-01 00:00:00" - read_attribute(:birth_date_and_time).should be_kind_of(Time) - end - - it "should return nil from invalid time string" do - @attributes = {} - self.stub!(:column_for_attribute).and_return( mock('Column', :klass => Time) ) - self.stub!(:unserializable_attribute?).and_return(false) - - @attributes['birth_date_and_time'] = "1980-02-30 00:00:00" - read_attribute(:birth_date_and_time).should be_nil - end - end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2c52505..f8a8f6f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -21,7 +21,9 @@ else end -Time.zone_default = Time.send!(:get_zone, 'UTC') if vendored_rails +Time.zone_default = TimeZone['UTC'] +ActiveRecord::Base.default_timezone = :utc +ActiveRecord::Base.time_zone_aware_attributes = true require 'validates_timeliness'