diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 3572c640eb..bf28cffbbe 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -92,6 +92,7 @@ module ActiveRecord autoload :Read autoload :TimeZoneConversion autoload :Write + autoload :Serialization end end diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 78dbbeb0e6..d3ef7f0ae5 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -34,22 +34,12 @@ module ActiveRecord protected def define_method_attribute(attr_name) - if serialized_attributes.include?(attr_name) - define_read_method_for_serialized_attribute(attr_name) - else - define_read_method(attr_name, attr_name, columns_hash[attr_name]) - end + define_read_method(attr_name, attr_name, columns_hash[attr_name]) end private def cacheable_column?(column) - serialized_attributes.include?(column.name) || attribute_types_cached_by_default.include?(column.type) - end - - # Define read method for serialized attribute. - def define_read_method_for_serialized_attribute(attr_name) - access_code = "@attributes_cache['#{attr_name}'] ||= @attributes['#{attr_name}']" - generated_attribute_methods.module_eval("def _#{attr_name}; #{access_code}; end; alias #{attr_name} _#{attr_name}", __FILE__, __LINE__) + attribute_types_cached_by_default.include?(column.type) end # Define an attribute reader method. Cope with nil column. @@ -107,28 +97,15 @@ module ActiveRecord value = @attributes[attr_name] unless value.nil? if column = column_for_attribute(attr_name) - if unserializable_attribute?(attr_name, column) - unserialize_attribute(attr_name) - else - column.type_cast(value) - end + type_cast_attribute(column, value) else value end end end - # Returns true if the attribute is of a text column and marked for serialization. - def unserializable_attribute?(attr_name, column) - column.text? && self.class.serialized_attributes.include?(attr_name) - end - - # Returns the unserialized object of the attribute. - def unserialize_attribute(attr_name) - coder = self.class.serialized_attributes[attr_name] - unserialized_object = coder.load(@attributes[attr_name]) - - @attributes.frozen? ? unserialized_object : @attributes[attr_name] = unserialized_object + def type_cast_attribute(column, value) #:nodoc: + column.type_cast(value) end private diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb new file mode 100644 index 0000000000..686754fbc2 --- /dev/null +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -0,0 +1,42 @@ +module ActiveRecord + module AttributeMethods + module Serialization + extend ActiveSupport::Concern + + module ClassMethods + def define_method_attribute(attr_name) + if serialized_attributes.include?(attr_name) + generated_attribute_methods.module_eval(<<-CODE, __FILE__, __LINE__) + def _#{attr_name} + @attributes_cache['#{attr_name}'] ||= @attributes['#{attr_name}'] + end + alias #{attr_name} _#{attr_name} + CODE + else + super + end + end + + def cacheable_column?(column) + serialized_attributes.include?(column.name) || super + end + end + + def type_cast_attribute(column, value) + coder = self.class.serialized_attributes[column.name] + + if column.text? && coder + unserialized_object = coder.load(@attributes[column.name]) + + if @attributes.frozen? + unserialized_object + else + @attributes[column.name] = unserialized_object + end + else + super + end + end + end + end +end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2a02380591..f8f10d651f 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2219,6 +2219,7 @@ MSG include AttributeMethods::PrimaryKey include AttributeMethods::TimeZoneConversion include AttributeMethods::Dirty + include AttributeMethods::Serialization include ActiveModel::MassAssignmentSecurity include Callbacks, ActiveModel::Observing, Timestamp include Associations, NamedScope