aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/attribute_methods/serialization.rb
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2011-11-30 22:31:52 +0000
committerJon Leighton <j@jonathanleighton.com>2011-11-30 23:18:40 +0000
commit7895182d0fce11131024305f53d0cbb32817e65c (patch)
tree5781756f8fdd6cabd552d32631433c656cc51cc9 /activerecord/lib/active_record/attribute_methods/serialization.rb
parent4f20eb5908895ac1f074cc1e2bc60f58e2d50a03 (diff)
downloadrails-7895182d0fce11131024305f53d0cbb32817e65c.tar.gz
rails-7895182d0fce11131024305f53d0cbb32817e65c.tar.bz2
rails-7895182d0fce11131024305f53d0cbb32817e65c.zip
omg computer science!
Implement a mini state machine for serialized attributes. This means we do not have to deserialize the values upon initialization, which means that if we never actually access the attribute, we never have to deserialize it.
Diffstat (limited to 'activerecord/lib/active_record/attribute_methods/serialization.rb')
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb52
1 files changed, 34 insertions, 18 deletions
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index ac65ef94f9..bc7e9d7a94 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -10,6 +10,26 @@ module ActiveRecord
self.serialized_attributes = {}
end
+ class Attribute < Struct.new(:coder, :value, :state)
+ def unserialized_value
+ state == :serialized ? unserialize : value
+ end
+
+ def serialized_value
+ state == :unserialized ? serialize : value
+ end
+
+ def unserialize
+ self.state = :unserialized
+ self.value = coder.load(value)
+ end
+
+ def serialize
+ self.state = :serialized
+ self.value = coder.dump(value)
+ end
+ end
+
module ClassMethods
# If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
# then specify the name of that attribute using this method and it will be handled automatically.
@@ -42,7 +62,7 @@ module ActiveRecord
if serialized_attributes.include?(attr_name)
generated_attribute_methods.module_eval(<<-CODE, __FILE__, __LINE__)
def _#{attr_name}
- @attributes_cache['#{attr_name}'] ||= @attributes['#{attr_name}']
+ @attributes['#{attr_name}'].unserialized_value
end
alias #{attr_name} _#{attr_name}
CODE
@@ -50,31 +70,27 @@ module ActiveRecord
super
end
end
-
- def cacheable_column?(column)
- serialized_attributes.include?(column.name) || super
- end
end
def set_serialized_attributes
- sattrs = self.class.serialized_attributes
-
- sattrs.each do |key, coder|
- @attributes[key] = coder.load @attributes[key] if @attributes.key?(key)
+ self.class.serialized_attributes.each do |key, coder|
+ if @attributes.key?(key)
+ @attributes[key] = Attribute.new(coder, @attributes[key], :serialized)
+ end
end
end
def type_cast_attribute(column)
- coder = self.class.serialized_attributes[column.name]
-
- if column.text? && coder
- unserialized_object = coder.load(@attributes[column.name])
+ if column.text? && self.class.serialized_attributes.include?(column.name)
+ @attributes[column.name].unserialized_value
+ else
+ super
+ end
+ end
- if @attributes.frozen?
- unserialized_object
- else
- @attributes[column.name] = unserialized_object
- end
+ def type_cast_attribute_for_write(column, attr_name, value)
+ if column && coder = self.class.serialized_attributes[column.name]
+ Attribute.new(coder, value, :unserialized)
else
super
end