Browse files

Make before_type_cast available for datetime fields

Signed-off-by: Santiago Pastorino <santiago@wyeworks.com>
  • Loading branch information...
1 parent 0448247 commit c8b7606734cc556ae17a9dd5bb12994a3cff6b7e @amatsuda amatsuda committed with spastorino Dec 16, 2010
View
5 activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -37,12 +37,13 @@ def #{attr_name}(reload = false)
def define_method_attribute=(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
method_body, line = <<-EOV, __LINE__ + 1
- def #{attr_name}=(time)
+ def #{attr_name}=(original_time)
+ time = original_time.dup
@nragaz
nragaz added a note Feb 6, 2011

original_time.dup gets called even if original_time is nil, resulting in an exception (can't dup NilClass). An unless original_time.nil? would help.

@amatsuda
Ruby on Rails member
amatsuda added a note Feb 6, 2011

Oh, thanks @nragaz, that was my mistake...

@amatsuda
Ruby on Rails member
amatsuda added a note Feb 6, 2011
From 9138bb1ad26d8b0c8a12722f9ac07e5c433f3f9f Mon Sep 17 00:00:00 2001
From: Akira Matsuda <ronnie@dio.jp>
Date: Mon, 7 Feb 2011 08:29:06 +0900
Subject: [PATCH] avoid nil.dup

---
 .../attribute_methods/time_zone_conversion.rb      |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index a72eecb..76218d2 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -41,7 +41,7 @@ module ActiveRecord
             if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
               method_body, line = <<-EOV, __LINE__ + 1
                 def #{attr_name}=(original_time)
-                  time = original_time.dup
+                  time = original_time.dup unless original_time.nil?
                   unless time.acts_like?(:time)
                     time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
                   end
-- 
1.7.3.5
@nragaz
nragaz added a note Feb 6, 2011

Thank you! Was just about to do a patch myself.

@spastorino
Ruby on Rails member

Can you provide a patch with a test?, thanks.

@stid
stid added a note Feb 9, 2011

Guys, this was released (not patched) in 3.0.4, I'm getting this error in authologic when he try to update the last_login column after a new session/user is created and in a bunch of tests.

Update: Sorry, my mistake, I was using the 30-stable branch. This was not released in 3.0.4 final.

This breaks when using the home_run gem since Date#dup (Date.allocate) is not implemented: "TypeError: allocator undefined for Date"

What exactly is being achieved by duping the Date? Is there another way this can be achieved?

Actually, looks like it's being reported as a bug on home_run: https://github.com/jeremyevans/home_run/issues#issue/21

@adzap
adzap added a note Mar 1, 2011

Although that is an issue for home_run to fix, as dup is valid on a Date instance. I have submitted a ticket which removes the dup because it is not necessary. No in-place change is made to the time value which could effect the original_time value.

https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/6489-fix-before_type_cast-for-timezone-aware-datetime-attributes#ticket-6489-3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
unless time.acts_like?(:time)
time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
end
time = time.in_time_zone rescue nil if time
- write_attribute(:#{attr_name}, time)
+ write_attribute(:#{attr_name}, (time || original_time))
end
EOV
generated_attribute_methods.module_eval(method_body, __FILE__, line)
View
20 activerecord/test/cases/attribute_methods_test.rb
@@ -119,20 +119,16 @@ def test_read_attributes_before_type_cast_on_boolean
unless current_adapter?(:Mysql2Adapter)
def test_read_attributes_before_type_cast_on_datetime
developer = Developer.find(:first)
- # Oracle adapter returns Time before type cast
- unless current_adapter?(:OracleAdapter)
- assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"]
- else
- assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db)
+ assert_equal developer.created_at.to_s(:db), developer.attributes_before_type_cast["created_at"].to_s
- developer.created_at = "345643456"
- assert_equal developer.created_at_before_type_cast, "345643456"
- assert_equal developer.created_at, nil
+ developer.created_at = "345643456"
- developer.created_at = "2010-03-21 21:23:32"
- assert_equal developer.created_at_before_type_cast, "2010-03-21 21:23:32"
- assert_equal developer.created_at, Time.parse("2010-03-21 21:23:32")
- end
+ assert_equal developer.created_at_before_type_cast, "345643456"
+ assert_equal developer.created_at, nil
+
+ developer.created_at = "2010-03-21 21:23:32"
+ assert_equal developer.created_at_before_type_cast.to_s, "2010-03-21 21:23:32"
@adzap
adzap added a note Feb 7, 2011

You do a to_s on the before_type_cast value which will convert the Time instance back to a string. So this does not test that a string value is stored for the before_type_cast when the original_time is a valid time string.

@amatsuda
Ruby on Rails member
amatsuda added a note Feb 7, 2011

You're absolutely right.
Just pushed the fix. 65e08cf
Thanks for your advice!

@adzap
adzap added a note Feb 7, 2011

No problem. But I am surprised that this change actually passes. From the code, if the time string is successfully parsed, the time variable will be Time instance. Therefore when (time || original_time) is evaluated, it will pass the time instance to be stored by write_attribute. So when you call before_type_cast you should get the same Time instance, and not the original string value.

Am I missing something?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ assert_equal developer.created_at, Time.parse("2010-03-21 21:23:32")
end
end

1 comment on commit c8b7606

Please sign in to comment.