diff options
-rw-r--r-- | activemodel/lib/active_model/naming.rb | 5 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/serialization.rb | 63 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/write.rb | 19 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/column.rb | 6 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/postgresql/column.rb | 4 | ||||
-rw-r--r-- | activerecord/lib/active_record/model_schema.rb | 9 | ||||
-rw-r--r-- | activerecord/lib/active_record/properties.rb | 10 | ||||
-rw-r--r-- | activerecord/lib/active_record/type.rb | 1 | ||||
-rw-r--r-- | activerecord/lib/active_record/type/serialized.rb | 57 | ||||
-rw-r--r-- | activerecord/lib/active_record/type/value.rb | 15 | ||||
-rw-r--r-- | activerecord/test/cases/attribute_methods/serialization_test.rb | 29 | ||||
-rw-r--r-- | activerecord/test/cases/serialized_attribute_test.rb | 10 | ||||
-rw-r--r-- | guides/source/3_0_release_notes.md | 2 | ||||
-rw-r--r-- | guides/source/engines.md | 10 |
14 files changed, 101 insertions, 139 deletions
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index 2a428687c5..5219de2606 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -218,10 +218,11 @@ module ActiveModel # used to retrieve all kinds of naming-related information # (See ActiveModel::Name for more information). # - # class Person < ActiveModel::Model + # class Person + # include ActiveModel::Model # end # - # Person.model_name # => Person + # Person.model_name.name # => "Person" # Person.model_name.class # => ActiveModel::Name # Person.model_name.singular # => "person" # Person.model_name.plural # => "people" diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 47c6f94ba7..65d910fd46 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -58,51 +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, 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 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 - 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. @@ -116,7 +83,7 @@ module ActiveRecord 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 @@ -132,22 +99,6 @@ module ActiveRecord 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 56441d7324..629bd3bc63 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -26,8 +26,6 @@ module ActiveRecord protected if Module.methods_transplantable? - # See define_method_attribute in read.rb for an explanation of - # this code. def define_method_attribute=(name) method = WriterMethodCache[name] generated_attribute_methods.module_eval { @@ -55,11 +53,11 @@ module ActiveRecord # 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 +66,6 @@ module ActiveRecord 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 +78,10 @@ module ActiveRecord @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 a62617ab47..f66e99c9d1 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, :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/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..4052ac0fa0 --- /dev/null +++ b/activerecord/lib/active_record/type/serialized.rb @@ -0,0 +1,57 @@ +module ActiveRecord + module Type + class Serialized < SimpleDelegator # :nodoc: + attr_reader :subtype, :coder + + def initialize(subtype, coder) + @subtype = subtype + @coder = coder + super(subtype) + end + + def type_cast(value) + if value.respond_to?(:unserialized_value) + value.unserialized_value(super(value.value)) + else + super + 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 + + 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 72d27197d5..9a4adc60cc 100644 --- a/activerecord/lib/active_record/type/value.rb +++ b/activerecord/lib/active_record/type/value.rb @@ -23,10 +23,6 @@ module ActiveRecord 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 @@ -43,10 +39,19 @@ module ActiveRecord false end - def klass + def serialized? + false + end + + 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 75de773961..0000000000 --- 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 = Serialization::Type.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_cast(value) - assert_equal "Hello world", type.type_cast(value) - end - end - end -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) diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md index db34fa401f..2d7c06837b 100644 --- a/guides/source/3_0_release_notes.md +++ b/guides/source/3_0_release_notes.md @@ -308,7 +308,7 @@ More Information: Major re-write was done in the Action View helpers, implementing Unobtrusive JavaScript (UJS) hooks and removing the old inline AJAX commands. This enables Rails to use any compliant UJS driver to implement the UJS hooks in the helpers. -What this means is that all previous `remote_<method>` helpers have been removed from Rails core and put into the [Prototype Legacy Helper](http://github.com/rails/prototype_legacy_helper.) To get UJS hooks into your HTML, you now pass `:remote => true` instead. For example: +What this means is that all previous `remote_<method>` helpers have been removed from Rails core and put into the [Prototype Legacy Helper](http://github.com/rails/prototype_legacy_helper). To get UJS hooks into your HTML, you now pass `:remote => true` instead. For example: ```ruby form_for @post, :remote => true diff --git a/guides/source/engines.md b/guides/source/engines.md index 1321fa3870..e7f024f1fc 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -471,7 +471,7 @@ called `Blorgh::Comment`. Now run the migration to create our blorgh_comments table: ```bash -$ bin/rake db:migrate +$ rake db:migrate ``` To show the comments on an article, edit `app/views/blorgh/articles/show.html.erb` and @@ -682,14 +682,14 @@ engine's models can query them correctly. To copy these migrations into the application use this command: ```bash -$ bin/rake blorgh:install:migrations +$ rake blorgh:install:migrations ``` If you have multiple engines that need migrations copied over, use `railties:install:migrations` instead: ```bash -$ bin/rake railties:install:migrations +$ rake railties:install:migrations ``` This command, when run for the first time, will copy over all the migrations @@ -822,7 +822,7 @@ This migration will need to be run on the application. To do that, it must first be copied using this command: ```bash -$ bin/rake blorgh:install:migrations +$ rake blorgh:install:migrations ``` Notice that only _one_ migration was copied over here. This is because the first @@ -839,7 +839,7 @@ with the same name already exists. Copied migration Run the migration using: ```bash -$ bin/rake db:migrate +$ rake db:migrate ``` Now with all the pieces in place, an action will take place that will associate |