Browse files

fixed issue with ActiveRecord serialize object as JSON

fixes #15594
  • Loading branch information...
1 parent 2082bc7 commit b1e30b3da11d850368b34d62c5ecae9146229e32 @jenncoop jenncoop committed Jul 4, 2014
View
40 activerecord/CHANGELOG.md
@@ -1,3 +1,43 @@
+* Restore 4.0 behavior for ActiveRecord `serialize` object as `JSON`.
+
+ With 4.1.x, `serialize` started returning a string when `JSON` was passed as the second attribute. It will now return a hash as per previous versions (prior to 4.1.x)
+
+ Example: An ActiveRecord model named `Post` with a `comment` column that uses the serialize method to convert a `Comment` object to `JSON`.
+
+ class Post < ActiveRecord::Base
+ serialize :comment, JSON
+ end
+
+ class Comment
+ include ActiveModel::Model
+ attr_accessor :category, :text
+ end
+
+ post = Post.create!
+ post.comment = Comment.new(category: "Animals", text: "This is a comment about squirrels.")
+ post.save!
+
+ # 4.0
+ post.comment # => returns a Hash
+
+ # 4.1 before
+ post.comment # => returns a String
+
+ # 4.1 now
+ post.comment # => returns a Hash
+
+ When `JSON` is used with `serialize`, ActiveRecord will use the new
+ `ActiveRecord::Coders::JSON` coder which delegates
+ `ActiveRecord::Coders::JSON.dump/load` to
+ `ActiveSupport::JSON.encode/decode`.
+
+ To keep the previous behaviour, supply a custom coder instead
+ (see the following [gist](https://gist.github.com/jenncoop/8c4142bbe59da77daa63) for an example).
+
+ Fixes #15594.
+
+ *Jenn Cooper*
+
* Fixed error in `reset_counters` when associations have `select` scope.
(Call to `count` generates invalid SQL.)
View
1 activerecord/lib/active_record.rb
@@ -95,6 +95,7 @@ module ActiveRecord
module Coders
autoload :YAMLColumn, 'active_record/coders/yaml_column'
+ autoload :JSON, 'active_record/coders/json'
end
module AttributeMethods
View
7 activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -52,7 +52,12 @@ module ClassMethods
def serialize(attr_name, class_name_or_coder = Object)
include Behavior
- coder = if [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
+ # When ::JSON is used, force it to go through the Active Support JSON encoder
+ # to ensure special objects (e.g. Active Record models) are dumped correctly
+ # using the #as_json hook.
+ coder = if class_name_or_coder == ::JSON
+ Coders::JSON
+ elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
class_name_or_coder
else
Coders::YAMLColumn.new(class_name_or_coder)
View
13 activerecord/lib/active_record/coders/json.rb
@@ -0,0 +1,13 @@
+module ActiveRecord
+ module Coders # :nodoc:
+ class JSON # :nodoc:
+ def self.dump(obj)
+ ActiveSupport::JSON.encode(obj)
+ end
+
+ def self.load(json)
+ ActiveSupport::JSON.decode(json)
+ end
+ end
+ end
+end
View
15 activerecord/test/cases/serialized_attribute_test.rb
@@ -3,10 +3,12 @@
require 'models/reply'
require 'models/person'
require 'models/traffic_light'
+require 'models/post'
require 'bcrypt'
class SerializedAttributeTest < ActiveRecord::TestCase
fixtures :topics
+ fixtures :posts
MyObject = Struct.new :attribute1, :attribute2
@@ -77,6 +79,19 @@ def test_serialized_attribute_calling_dup_method
assert_equal({ :foo => :bar }, t.content_before_type_cast)
end
+ def test_serialized_json_attribute_returns_unserialized_value
+ Topic.serialize :content, JSON
+ my_post = posts(:welcome)
+
+ t = Topic.new(content: my_post)
+ t.save!
+ t.reload
+
+ assert_instance_of(Hash, t.content)
+ assert_equal(my_post.id, t.content["id"])
+ assert_equal(my_post.title, t.content["title"])
+ end
+
def test_serialized_attribute_declared_in_subclass
hash = { 'important1' => 'value1', 'important2' => 'value2' }
important_topic = ImportantTopic.create("important" => hash)

0 comments on commit b1e30b3

Please sign in to comment.