aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLilibeth De La Cruz <lilibethdlc@gmail.com>2013-01-25 04:14:27 -0400
committerAndrew White <andyw@pixeltrix.co.uk>2013-02-17 16:19:09 +0000
commit3c5f631caa160c2e5f4ec3ee0b7536a474b7b98f (patch)
tree40964c101787515d4ad92bc6deda2b16e6b37311
parentf7c845745313efc23e9020dea93b5fd9ca104568 (diff)
downloadrails-3c5f631caa160c2e5f4ec3ee0b7536a474b7b98f.tar.gz
rails-3c5f631caa160c2e5f4ec3ee0b7536a474b7b98f.tar.bz2
rails-3c5f631caa160c2e5f4ec3ee0b7536a474b7b98f.zip
Fix handling of dirty time zone aware attributes
Previously, when `time_zone_aware_attributes` were enabled, after changing a datetime or timestamp attribute and then changing it back to the original value, `changed_attributes` still tracked the attribute as changed. This caused `[attribute]_changed?` and `changed?` methods to return true incorrectly. Example: in_time_zone 'Paris' do order = Order.new original_time = Time.local(2012, 10, 10) order.shipped_at = original_time order.save order.changed? # => false # changing value order.shipped_at = Time.local(2013, 1, 1) order.changed? # => true # reverting to original value order.shipped_at = original_time order.changed? # => false, used to return true end (cherry picked from commit bc982cbcb34129ea2cfe8aa1f8e0b40e444e68db) Conflicts: activerecord/CHANGELOG.md activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb Backport of #9073 Fixes #8898
-rw-r--r--activerecord/CHANGELOG.md31
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb4
-rw-r--r--activerecord/test/cases/dirty_test.rb2
3 files changed, 35 insertions, 2 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index b0e0d56990..febead3067 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,36 @@
## unreleased ##
+* Fix handling of dirty time zone aware attributes
+
+ Previously, when `time_zone_aware_attributes` were enabled, after
+ changing a datetime or timestamp attribute and then changing it back
+ to the original value, `changed_attributes` still tracked the
+ attribute as changed. This caused `[attribute]_changed?` and
+ `changed?` methods to return true incorrectly.
+
+ Example:
+
+ in_time_zone 'Paris' do
+ order = Order.new
+ original_time = Time.local(2012, 10, 10)
+ order.shipped_at = original_time
+ order.save
+ order.changed? # => false
+
+ # changing value
+ order.shipped_at = Time.local(2013, 1, 1)
+ order.changed? # => true
+
+ # reverting to original value
+ order.shipped_at = original_time
+ order.changed? # => false, used to return true
+ end
+
+ Backport of #9073
+ Fixes #8898
+
+ *Lilibeth De La Cruz*
+
* Fix counter cache columns not updated when replacing `has_many :through`
associations.
Backport #8400.
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 45810322de..6f3112e654 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -43,9 +43,9 @@ module ActiveRecord
time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
end
time = time.in_time_zone rescue nil if time
- changed = read_attribute(:#{attr_name}) != time
+ previous_time = attribute_changed?("#{attr_name}") ? changed_attributes["#{attr_name}"] : read_attribute(:#{attr_name})
write_attribute(:#{attr_name}, original_time)
- #{attr_name}_will_change! if changed
+ #{attr_name}_will_change! if previous_time != time
@attributes_cache["#{attr_name}"] = time
end
EOV
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index b4e43bdfbc..9756c182b9 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -76,6 +76,8 @@ class DirtyTest < ActiveRecord::TestCase
assert pirate.created_on_changed?
assert_kind_of ActiveSupport::TimeWithZone, pirate.created_on_was
assert_equal old_created_on, pirate.created_on_was
+ pirate.created_on = old_created_on
+ assert !pirate.created_on_changed?
end
end