diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 7a03d4f9e1..1c85197ec7 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* Fixed serialized fields returning serialized data after being updated with + `update_column`. + + *Simon Hørup Eskildsen* + * Fixed polymorphic eager loading when using a String as foreign key. Fixes #14734. diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index c3466153d6..53a9c874bf 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -142,6 +142,14 @@ module ActiveRecord end end + def raw_type_cast_attribute_for_write(column, value) + if column && coder = self.class.serialized_attributes[column.name] + Attribute.new(coder, value, :serialized) + else + super + end + end + def _field_changed?(attr, old, value) if self.class.serialized_attributes.include?(attr) old != value diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index c853fc0917..56441d7324 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -55,24 +55,12 @@ module ActiveRecord # specified +value+. Empty strings for fixnum and float columns are # turned into +nil+. def write_attribute(attr_name, value) - attr_name = attr_name.to_s - attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key - @attributes_cache.delete(attr_name) - column = column_for_attribute(attr_name) - - # If we're dealing with a binary column, write the data to the cache - # so we don't attempt to typecast multiple times. - if column && column.binary? - @attributes_cache[attr_name] = value - end - - if column || @attributes.has_key?(attr_name) - @attributes[attr_name] = type_cast_attribute_for_write(column, value) - else - raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'" - end + write_attribute_with_type_cast(attr_name, value, :type_cast_attribute_for_write) + end + + def raw_write_attribute(attr_name, value) + write_attribute_with_type_cast(attr_name, value, :raw_type_cast_attribute_for_write) end - alias_method :raw_write_attribute, :write_attribute private # Handle *= for method_missing. @@ -85,6 +73,26 @@ module ActiveRecord column.type_cast_for_write value end + alias_method :raw_type_cast_attribute_for_write, :type_cast_attribute_for_write + + def write_attribute_with_type_cast(attr_name, value, type_cast_method) + attr_name = attr_name.to_s + attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key + @attributes_cache.delete(attr_name) + column = column_for_attribute(attr_name) + + # If we're dealing with a binary column, write the data to the cache + # so we don't attempt to typecast multiple times. + if column && column.binary? + @attributes_cache[attr_name] = value + end + + if column || @attributes.has_key?(attr_name) + @attributes[attr_name] = send(type_cast_method, column, value) + else + raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'" + end + end end end end diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb index 5609cf310c..c8f9d7cf87 100644 --- a/activerecord/test/cases/serialized_attribute_test.rb +++ b/activerecord/test/cases/serialized_attribute_test.rb @@ -244,4 +244,20 @@ class SerializedAttributeTest < ActiveRecord::TestCase type = Topic.column_types["content"] assert !type.instance_variable_get("@column").is_a?(ActiveRecord::AttributeMethods::Serialization::Type) end + + def test_serialized_column_should_unserialize_after_update_column + t = Topic.create(content: "first") + assert_equal("first", t.content) + + t.update_column(:content, Topic.serialized_attributes["content"].dump("second")) + assert_equal("second", t.content) + end + + def test_serialized_column_should_unserialize_after_update_attribute + t = Topic.create(content: "first") + assert_equal("first", t.content) + + t.update_attribute(:content, "second") + assert_equal("second", t.content) + end end