diff options
author | Sean Griffin <sean@thoughtbot.com> | 2014-05-28 08:24:59 -0700 |
---|---|---|
committer | Sean Griffin <sean@thoughtbot.com> | 2014-05-29 12:12:44 -0700 |
commit | 2eb547a4d9cfb4ed1c92397921402048162f5fc8 (patch) | |
tree | 4deededa75b1e55f6bcb0c8156334c51aab560b3 | |
parent | 4a3f71b6fb07f5bbf9e43b259a7429c96752e00b (diff) | |
download | rails-2eb547a4d9cfb4ed1c92397921402048162f5fc8.tar.gz rails-2eb547a4d9cfb4ed1c92397921402048162f5fc8.tar.bz2 rails-2eb547a4d9cfb4ed1c92397921402048162f5fc8.zip |
Refactor serialized types to be partially defined as custom properties
Many of the methods defined in `AttributeMethods::Serialization` can be
refactored onto this type as well, but this is a reasonable small step.
Removes the `Type` class, and the need for `decorate_columns` to handle
serialized types.
10 files changed, 52 insertions, 50 deletions
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 47c6f94ba7..e8c27cb8b8 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -58,32 +58,18 @@ module ActiveRecord Coders::YAMLColumn.new(class_name_or_coder) end + type = columns_hash[attr_name.to_s].cast_type + if type.serialized? + type = type.subtype + end + property attr_name, ActiveRecord::Type::Serialized.new(type) + # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy # has its own hash of own serialized attributes self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder) end end - class Type # :nodoc: - delegate :type, :type_cast_for_database, to: :@column - - def initialize(column) - @column = column - end - - def type_cast(value) - if value.state == :serialized - value.unserialized_value @column.type_cast value.value - else - value.unserialized_value - end - end - - def accessor - ActiveRecord::Store::IndifferentHashAccessor - end - end - class Attribute < Struct.new(:coder, :value, :state) # :nodoc: def unserialized_value(v = value) state == :serialized ? unserialize(v) : value diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index a62617ab47..42650e332d 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -15,8 +15,10 @@ module ActiveRecord attr_reader :name, :default, :cast_type, :null, :sql_type, :default_function - delegate :type, :precision, :scale, :limit, :klass, :text?, :number?, :binary?, - :type_cast, :type_cast_for_write, :type_cast_for_database, to: :cast_type + delegate :type, :precision, :scale, :limit, :klass, :accessor, + :text?, :number?, :binary?, :serialized?, + :type_cast, :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/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb index 9a5e2d05ef..a579746815 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb @@ -35,10 +35,6 @@ module ActiveRecord end end # :startdoc: - - def accessor - cast_type.accessor - end end end end diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index a4e10ed2e7..ad6428d8a8 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -224,14 +224,6 @@ module ActiveRecord def decorate_columns(columns_hash) # :nodoc: return if columns_hash.empty? - @serialized_column_names ||= self.columns_hash.keys.find_all do |name| - serialized_attributes.key?(name) - end - - @serialized_column_names.each do |name| - columns_hash[name] = AttributeMethods::Serialization::Type.new(columns_hash[name]) - end - @time_zone_column_names ||= self.columns_hash.find_all do |name, col| create_time_zone_conversion_attribute?(name, col) end.map!(&:first) @@ -299,7 +291,6 @@ module ActiveRecord @dynamic_methods_hash = nil @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column @relation = nil - @serialized_column_names = nil @time_zone_column_names = nil @cached_time_zone = nil end diff --git a/activerecord/lib/active_record/properties.rb b/activerecord/lib/active_record/properties.rb index cc1e8b45c1..7fe59ccce4 100644 --- a/activerecord/lib/active_record/properties.rb +++ b/activerecord/lib/active_record/properties.rb @@ -64,6 +64,7 @@ module ActiveRecord # store_listing.price_in_cents # => 1000 def property(name, cast_type) name = name.to_s + clear_properties_cache # Assign a new hash to ensure that subclasses do not share a hash self.user_provided_columns = user_provided_columns.merge(name => connection.new_column(name, nil, cast_type)) end @@ -80,9 +81,7 @@ module ActiveRecord def reset_column_information # :nodoc: super - - @columns = nil - @columns_hash = nil + clear_properties_cache end private @@ -97,6 +96,11 @@ module ActiveRecord existing_columns + new_columns end + + def clear_properties_cache + @columns = nil + @columns_hash = nil + end end end end diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb index 2c26477201..e9b827886a 100644 --- a/activerecord/lib/active_record/type.rb +++ b/activerecord/lib/active_record/type.rb @@ -10,6 +10,7 @@ require 'active_record/type/decimal' require 'active_record/type/decimal_without_scale' require 'active_record/type/float' require 'active_record/type/integer' +require 'active_record/type/serialized' require 'active_record/type/string' require 'active_record/type/text' require 'active_record/type/time' diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb new file mode 100644 index 0000000000..cc7513ca2a --- /dev/null +++ b/activerecord/lib/active_record/type/serialized.rb @@ -0,0 +1,28 @@ +module ActiveRecord + module Type + class Serialized < SimpleDelegator # :nodoc: + attr_reader :subtype + + def initialize(subtype) + @subtype = subtype + super + end + + def type_cast(value) + if value.respond_to?(:unserialized_value) + value.unserialized_value(super(value.value)) + else + super + end + end + + def serialized? + true + end + + def accessor + ActiveRecord::Store::IndifferentHashAccessor + end + end + end +end diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb index 72d27197d5..a5493be8f2 100644 --- a/activerecord/lib/active_record/type/value.rb +++ b/activerecord/lib/active_record/type/value.rb @@ -43,6 +43,10 @@ module ActiveRecord false end + def serialized? + false + end + def klass ::Object end diff --git a/activerecord/test/cases/attribute_methods/serialization_test.rb b/activerecord/test/cases/attribute_methods/serialization_test.rb index 75de773961..2e3bcadab1 100644 --- a/activerecord/test/cases/attribute_methods/serialization_test.rb +++ b/activerecord/test/cases/attribute_methods/serialization_test.rb @@ -14,13 +14,13 @@ module ActiveRecord def test_type_cast_serialized_value value = Serialization::Attribute.new(NullCoder.new, "Hello world", :serialized) - type = Serialization::Type.new(FakeColumn.new) + 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 = Serialization::Type.new(FakeColumn.new) + type = Type::Serialized.new(FakeColumn.new) type.type_cast(value) assert_equal "Hello world", type.type_cast(value) end diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb index c8f9d7cf87..c65a86a6ef 100644 --- a/activerecord/test/cases/serialized_attribute_test.rb +++ b/activerecord/test/cases/serialized_attribute_test.rb @@ -235,16 +235,6 @@ class SerializedAttributeTest < ActiveRecord::TestCase assert_equal [], light.long_state end - def test_serialized_column_should_not_be_wrapped_twice - Topic.serialize(:content, MyObject) - - myobj = MyObject.new('value1', 'value2') - Topic.create(content: myobj) - Topic.create(content: myobj) - 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) |