aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activemodel/lib/active_model/naming.rb5
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb63
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/column.rb4
-rw-r--r--activerecord/lib/active_record/model_schema.rb9
-rw-r--r--activerecord/lib/active_record/properties.rb10
-rw-r--r--activerecord/lib/active_record/type.rb1
-rw-r--r--activerecord/lib/active_record/type/serialized.rb57
-rw-r--r--activerecord/lib/active_record/type/value.rb15
-rw-r--r--activerecord/test/cases/attribute_methods/serialization_test.rb29
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb10
-rw-r--r--guides/source/3_0_release_notes.md2
-rw-r--r--guides/source/engines.md10
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