From 56110b20cc57b23ebfdf58cb7cbc75dad5cf995d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20=C3=81lvarez?= Date: Thu, 3 Feb 2011 00:26:39 +0100 Subject: [PATCH] Make serialized columns with explicit object_type return a new instance of the object instead of nil --- activerecord/lib/active_record/base.rb | 24 ++++++++++++++++--- .../lib/active_record/coders/yaml_column.rb | 2 + activerecord/test/cases/base_test.rb | 19 +++++++++++++++ activerecord/test/cases/coders/yaml_column_test.rb | 5 ++- 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index c592490..b19501c 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -249,6 +249,17 @@ module ActiveRecord #:nodoc: # user = User.create(:preferences => %w( one two three )) # User.find(user.id).preferences # raises SerializationTypeMismatch # + # When you specify a class option, the default value for that attribute will be a new + # instance of that class. + # + # class User < ActiveRecord::Base + # serialize :preferences, OpenStruct + # end + # + # user = User.new + # user.preferences.theme_color = "red" + # + # # == Single table inheritance # # Active Record allows inheritance by storing the name of the class in a column that by @@ -1409,6 +1420,7 @@ MSG @changed_attributes = {} ensure_proper_type + set_serialized_attributes populate_with_current_scope_attributes self.attributes = attributes unless attributes.nil? @@ -1447,10 +1459,7 @@ MSG def init_with(coder) @attributes = coder['attributes'] - (@attributes.keys & self.class.serialized_attributes.keys).each do |key| - coder = self.class.serialized_attributes[key] - @attributes[key] = coder.load @attributes[key] - end + set_serialized_attributes @attributes_cache, @previously_changed, @changed_attributes = {}, {}, {} @association_cache = {} @@ -1461,6 +1470,13 @@ MSG run_callbacks :initialize end + def set_serialized_attributes + (@attributes.keys & self.class.serialized_attributes.keys).each do |key| + coder = self.class.serialized_attributes[key] + @attributes[key] = coder.load @attributes[key] + end + end + # Specifies how the record is dumped by +Marshal+. # # +_dump+ emits a marshalled hash which has been passed to +encode_with+. Override this diff --git a/activerecord/lib/active_record/coders/yaml_column.rb b/activerecord/lib/active_record/coders/yaml_column.rb index fcecc11..fb59d9f 100644 --- a/activerecord/lib/active_record/coders/yaml_column.rb +++ b/activerecord/lib/active_record/coders/yaml_column.rb @@ -19,6 +19,7 @@ module ActiveRecord end def load(yaml) + return object_class.new if object_class != Object && yaml.nil? return yaml unless yaml.is_a?(String) && yaml =~ /^---/ begin obj = YAML.load(yaml) @@ -27,6 +28,7 @@ module ActiveRecord raise SerializationTypeMismatch, "Attribute was supposed to be a #{object_class}, but was a #{obj.class}" end + obj ||= object_class.new if object_class != Object obj rescue *RESCUE_ERRORS diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 5cbc527..a255c07 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1002,6 +1002,25 @@ class BasicsTest < ActiveRecord::TestCase Topic.serialize(:content) end + def test_serialized_default_class + Topic.serialize(:content, Hash) + topic = Topic.new + assert_equal Hash, topic.content.class + assert_equal Hash, topic.read_attribute(:content).class + topic.content["beer"] = "MadridRb" + assert topic.save + topic.reload + assert_equal Hash, topic.content.class + assert_equal "MadridRb", topic.content["beer"] + ensure + Topic.serialize(:content) + end + + def test_serialized_no_default_class_for_object + topic = Topic.new + assert_nil topic.content + end + def test_serialized_boolean_value_true Topic.serialize(:content) topic = Topic.new(:content => true) diff --git a/activerecord/test/cases/coders/yaml_column_test.rb b/activerecord/test/cases/coders/yaml_column_test.rb index f85f11b..c7dcc21 100644 --- a/activerecord/test/cases/coders/yaml_column_test.rb +++ b/activerecord/test/cases/coders/yaml_column_test.rb @@ -1,3 +1,4 @@ + require "cases/helper" module ActiveRecord @@ -20,9 +21,9 @@ module ActiveRecord assert_nil coder.load "--- " end - def test_nil_is_ok_with_different_class + def test_returns_new_with_different_class coder = YAMLColumn.new SerializationTypeMismatch - assert_nil coder.load "--- " + assert_equal SerializationTypeMismatch, coder.load("--- ").class end def test_returns_string_unless_starts_with_dash -- 1.7.0.3