From 41e7c68d87903d0596228b6c1ae2c5d87b209280 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 28 Mar 2010 22:51:48 -0700 Subject: Ruby 1.9.2: marshaling round-trips Time#zone --- .../lib/active_support/core_ext/time/marshal.rb | 56 ++++++++++++++++++++++ .../core_ext/time/marshal_with_utc_flag.rb | 26 ---------- activesupport/lib/active_support/time.rb | 2 +- activesupport/test/core_ext/time_ext_test.rb | 20 ++++---- activesupport/test/message_encryptor_test.rb | 2 +- activesupport/test/message_verifier_test.rb | 2 +- 6 files changed, 67 insertions(+), 41 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/time/marshal.rb delete mode 100644 activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb diff --git a/activesupport/lib/active_support/core_ext/time/marshal.rb b/activesupport/lib/active_support/core_ext/time/marshal.rb new file mode 100644 index 0000000000..1a4d918ce7 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/time/marshal.rb @@ -0,0 +1,56 @@ +# Pre-1.9 versions of Ruby have a bug with marshaling Time instances, where utc instances are +# unmarshalled in the local zone, instead of utc. We're layering behavior on the _dump and _load +# methods so that utc instances can be flagged on dump, and coerced back to utc on load. +if !Marshal.load(Marshal.dump(Time.now.utc)).utc? + class Time + class << self + alias_method :_load_without_utc_flag, :_load + def _load(marshaled_time) + time = _load_without_utc_flag(marshaled_time) + time.instance_eval do + if defined?(@marshal_with_utc_coercion) + val = remove_instance_variable("@marshal_with_utc_coercion") + end + val ? utc : self + end + end + end + + alias_method :_dump_without_utc_flag, :_dump + def _dump(*args) + obj = dup + obj.instance_variable_set('@marshal_with_utc_coercion', utc?) + obj._dump_without_utc_flag(*args) + end + end +end + +# Ruby 1.9.2 adds utc_offset and zone to Time, but marshaling only +# preserves utc_offset. Preserve zone also, even though it may not +# work in some edge cases. +if Time.local(2010).zone != Marshal.load(Marshal.dump(Time.local(2010))).zone + class Time + class << self + alias_method :_load_without_zone, :_load + def _load(marshaled_time) + time = _load_without_zone(marshaled_time) + time.instance_eval do + if zone = defined?(@_zone) && remove_instance_variable('@_zone') + ary = to_a + ary[-1] = zone + utc? ? Time.utc(*ary) : Time.local(*ary) + else + self + end + end + end + end + + alias_method :_dump_without_zone, :_dump + def _dump(*args) + obj = dup + obj.instance_variable_set('@_zone', zone) + obj._dump_without_zone(*args) + end + end +end diff --git a/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb b/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb deleted file mode 100644 index 8d46d80251..0000000000 --- a/activesupport/lib/active_support/core_ext/time/marshal_with_utc_flag.rb +++ /dev/null @@ -1,26 +0,0 @@ -# Pre-1.9 versions of Ruby have a bug with marshaling Time instances, where utc instances are -# unmarshalled in the local zone, instead of utc. We're layering behavior on the _dump and _load -# methods so that utc instances can be flagged on dump, and coerced back to utc on load. -if RUBY_VERSION < '1.9' - class Time - class << self - alias_method :_original_load, :_load - def _load(marshaled_time) - time = _original_load(marshaled_time) - time.instance_eval do - if defined?(@marshal_with_utc_coercion) - val = remove_instance_variable("@marshal_with_utc_coercion") - end - val ? utc : self - end - end - end - - alias_method :_original_dump, :_dump - def _dump(*args) - obj = dup - obj.instance_variable_set('@marshal_with_utc_coercion', utc?) - obj._original_dump(*args) - end - end -end diff --git a/activesupport/lib/active_support/time.rb b/activesupport/lib/active_support/time.rb index 0f421421d0..784c7173a9 100644 --- a/activesupport/lib/active_support/time.rb +++ b/activesupport/lib/active_support/time.rb @@ -14,7 +14,7 @@ require 'date' require 'time' require 'active_support/core_ext/time/publicize_conversion_methods' -require 'active_support/core_ext/time/marshal_with_utc_flag' +require 'active_support/core_ext/time/marshal' require 'active_support/core_ext/time/acts_like' require 'active_support/core_ext/time/calculations' require 'active_support/core_ext/time/conversions' diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 159b7d8366..c24c8619c6 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -758,33 +758,29 @@ end class TimeExtMarshalingTest < Test::Unit::TestCase def test_marshaling_with_utc_instance t = Time.utc(2000) - marshaled = Marshal.dump t - unmarshaled = Marshal.load marshaled - assert_equal t, unmarshaled + unmarshaled = Marshal.load(Marshal.dump(t)) assert_equal "UTC", unmarshaled.zone + assert_equal t, unmarshaled end def test_marshaling_with_local_instance t = Time.local(2000) - marshaled = Marshal.dump t - unmarshaled = Marshal.load marshaled - assert_equal t, unmarshaled + unmarshaled = Marshal.load(Marshal.dump(t)) assert_equal t.zone, unmarshaled.zone + assert_equal t, unmarshaled end def test_marshaling_with_frozen_utc_instance t = Time.utc(2000).freeze - marshaled = Marshal.dump t - unmarshaled = Marshal.load marshaled - assert_equal t, unmarshaled + unmarshaled = Marshal.load(Marshal.dump(t)) assert_equal "UTC", unmarshaled.zone + assert_equal t, unmarshaled end def test_marshaling_with_frozen_local_instance t = Time.local(2000).freeze - marshaled = Marshal.dump t - unmarshaled = Marshal.load marshaled - assert_equal t, unmarshaled + unmarshaled = Marshal.load(Marshal.dump(t)) assert_equal t.zone, unmarshaled.zone + assert_equal t, unmarshaled end end diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb index 5c2b44f188..2fba62bdd6 100644 --- a/activesupport/test/message_encryptor_test.rb +++ b/activesupport/test/message_encryptor_test.rb @@ -4,7 +4,7 @@ require 'active_support/time' class MessageEncryptorTest < Test::Unit::TestCase def setup @encryptor = ActiveSupport::MessageEncryptor.new(ActiveSupport::SecureRandom.hex(64)) - @data = {:some=>"data", :now=>Time.now} + @data = { :some => "data", :now => Time.local(2010) } end def test_simple_round_tripping diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index 714a3b3a39..4821311244 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -12,7 +12,7 @@ require 'active_support/time' class MessageVerifierTest < Test::Unit::TestCase def setup @verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!") - @data = {:some=>"data", :now=>Time.now} + @data = { :some => "data", :now => Time.local(2010) } end def test_simple_round_tripping -- cgit v1.2.3