diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index e8c27cb8b896986d165b6b62b9d5f55fcc525324..65d910fd4635b92c2b45e8eea2c5ca9dd3f1acaf 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -62,7 +62,7 @@ def serialize(attr_name, class_name_or_coder = Object) if type.serialized? type = type.subtype end - property attr_name, ActiveRecord::Type::Serialized.new(type) + property attr_name, Type::Serialized.new(type, coder) # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy # has its own hash of own serialized attributes @@ -70,25 +70,6 @@ def serialize(attr_name, class_name_or_coder = Object) end end - class Attribute < Struct.new(:coder, :value, :state) # :nodoc: - def unserialized_value(v = value) - state == :serialized ? unserialize(v) : value - end - - def serialized_value - state == :unserialized ? serialize : value - end - - def unserialize(v) - self.state = :unserialized - self.value = coder.load(v) - end - - def serialize - self.state = :serialized - self.value = coder.dump(value) - end - end # This is only added to the model when serialize is called, which # ensures we do not make things slower when serialization is not used. @@ -102,7 +83,7 @@ def initialize_attributes(attributes, options = {}) serialized_attributes.each do |key, coder| if attributes.key?(key) - attributes[key] = Attribute.new(coder, attributes[key], serialized) + attributes[key] = Type::Serialized::Attribute.new(coder, attributes[key], serialized) end end @@ -118,22 +99,6 @@ def keys_for_partial_write super | (attributes.keys & self.class.serialized_attributes.keys) end - def type_cast_attribute_for_write(column, value) - if column && coder = self.class.serialized_attributes[column.name] - Attribute.new(coder, value, :unserialized) - else - super - 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 56441d7324b9a55d17002c9218e46c7384fc26c7..d552bea8c4fbfbb8ff4849a12b19dba33691b72a 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -55,11 +55,11 @@ def __temp__#{safe_name}=(value) # specified +value+. Empty strings for fixnum and float columns are # turned into +nil+. def write_attribute(attr_name, value) - write_attribute_with_type_cast(attr_name, value, :type_cast_attribute_for_write) + write_attribute_with_type_cast(attr_name, value, :type_cast_for_write) end def raw_write_attribute(attr_name, value) - write_attribute_with_type_cast(attr_name, value, :raw_type_cast_attribute_for_write) + write_attribute_with_type_cast(attr_name, value, :raw_type_cast_for_write) end private @@ -68,13 +68,6 @@ def attribute=(attribute_name, value) write_attribute(attribute_name, value) end - def type_cast_attribute_for_write(column, value) - return value unless column - - 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 @@ -87,8 +80,10 @@ def write_attribute_with_type_cast(attr_name, value, type_cast_method) @attributes_cache[attr_name] = value end - if column || @attributes.has_key?(attr_name) - @attributes[attr_name] = send(type_cast_method, column, value) + if column + @attributes[attr_name] = column.public_send(type_cast_method, value) + elsif @attributes.has_key?(attr_name) + @attributes[attr_name] = value else raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'" end diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 42650e332d2924c6b47b5cdd8ef580420e974ec9..f66e99c9d1ea3b9558d70ec970819e60456911b6 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -17,7 +17,7 @@ module Format delegate :type, :precision, :scale, :limit, :klass, :accessor, :text?, :number?, :binary?, :serialized?, - :type_cast, :type_cast_for_write, :type_cast_for_database, + :type_cast, :type_cast_for_write, :raw_type_cast_for_write, :type_cast_for_database, to: :cast_type # Instantiates a new column in the table. diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb index cc7513ca2a5d42b179903750da0d398c03835c77..4052ac0fa01952077723175b8a214e29804dd196 100644 --- a/activerecord/lib/active_record/type/serialized.rb +++ b/activerecord/lib/active_record/type/serialized.rb @@ -1,11 +1,12 @@ module ActiveRecord module Type class Serialized < SimpleDelegator # :nodoc: - attr_reader :subtype + attr_reader :subtype, :coder - def initialize(subtype) + def initialize(subtype, coder) @subtype = subtype - super + @coder = coder + super(subtype) end def type_cast(value) @@ -16,6 +17,14 @@ def type_cast(value) end end + def type_cast_for_write(value) + Attribute.new(coder, value, :unserialized) + end + + def raw_type_cast_for_write(value) + Attribute.new(coder, value, :serialized) + end + def serialized? true end @@ -23,6 +32,26 @@ def serialized? def accessor ActiveRecord::Store::IndifferentHashAccessor end + + class Attribute < Struct.new(:coder, :value, :state) # :nodoc: + def unserialized_value(v = value) + state == :serialized ? unserialize(v) : value + end + + def serialized_value + state == :unserialized ? serialize : value + end + + def unserialize(v) + self.state = :unserialized + self.value = coder.load(v) + end + + def serialize + self.state = :serialized + self.value = coder.dump(value) + end + end end end end diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb index a5493be8f2921395342d1e92853e708a614a513b..9a4adc60cce19520c3d025a7c301846b68e6e5c5 100644 --- a/activerecord/lib/active_record/type/value.rb +++ b/activerecord/lib/active_record/type/value.rb @@ -23,10 +23,6 @@ def type_cast(value) cast_value(value) unless value.nil? end - def type_cast_for_write(value) - value - end - def type_cast_for_database(value) type_cast_for_write(value) end @@ -47,10 +43,15 @@ def serialized? false end - def klass + def klass # :nodoc: ::Object end + def type_cast_for_write(value) # :nodoc: + value + end + alias_method :raw_type_cast_for_write, :type_cast_for_write # :internal: + private # Responsible for casting values from external sources to the appropriate diff --git a/activerecord/test/cases/attribute_methods/serialization_test.rb b/activerecord/test/cases/attribute_methods/serialization_test.rb deleted file mode 100644 index 2e3bcadab1275cc5a614b1dd0fca5c47526b3db7..0000000000000000000000000000000000000000 --- a/activerecord/test/cases/attribute_methods/serialization_test.rb +++ /dev/null @@ -1,29 +0,0 @@ -require "cases/helper" - -module ActiveRecord - module AttributeMethods - class SerializationTest < ActiveSupport::TestCase - class FakeColumn < Struct.new(:name) - def type; :integer; end - def type_cast(s); "#{s}!"; end - end - - class NullCoder - def load(v); v; end - end - - def test_type_cast_serialized_value - value = Serialization::Attribute.new(NullCoder.new, "Hello world", :serialized) - type = Type::Serialized.new(FakeColumn.new) - assert_equal "Hello world!", type.type_cast(value) - end - - def test_type_cast_unserialized_value - value = Serialization::Attribute.new(nil, "Hello world", :unserialized) - type = Type::Serialized.new(FakeColumn.new) - type.type_cast(value) - assert_equal "Hello world", type.type_cast(value) - end - end - end -end