From d5354af1149496e1a6ebccc5434dbba3bde86265 Mon Sep 17 00:00:00 2001
From: Jeremy Kemper <jeremy@bitsweat.net>
Date: Wed, 30 May 2012 23:18:08 -0700
Subject: Fix backward compatibility with stored Hash values. Wrap coders to
 convert serialized values to indifferent access.

---
 activerecord/lib/active_record/store.rb    | 34 ++++++++++++++++++++++++++++--
 activerecord/test/cases/store_test.rb      | 29 +++++++++++++------------
 activerecord/test/fixtures/admin/users.yml |  3 +++
 3 files changed, 51 insertions(+), 15 deletions(-)

(limited to 'activerecord')

diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index fdd82b489a..d70e02e379 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -38,7 +38,7 @@ module ActiveRecord
 
     module ClassMethods
       def store(store_attribute, options = {})
-        serialize store_attribute, options.fetch(:coder, ActiveSupport::HashWithIndifferentAccess)
+        serialize store_attribute, IndifferentCoder.new(options[:coder])
         store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
       end
 
@@ -47,7 +47,7 @@ module ActiveRecord
           define_method("#{key}=") do |value|
             initialize_store_attribute(store_attribute)
             send(store_attribute)[key] = value
-            send("#{store_attribute}_will_change!")
+            send :"#{store_attribute}_will_change!"
           end
 
           define_method(key) do
@@ -71,5 +71,35 @@ module ActiveRecord
           send :"#{store_attribute}=", ActiveSupport::HashWithIndifferentAccess.new
         end
       end
+
+    class IndifferentCoder
+      def initialize(coder_or_class_name)
+        @coder =
+          if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
+            coder_or_class_name
+          else
+            ActiveRecord::Coders::YAMLColumn.new(coder_or_class_name || Object)
+          end
+      end
+
+      def dump(obj)
+        @coder.dump self.class.as_indifferent_hash(obj)
+      end
+
+      def load(yaml)
+        self.class.as_indifferent_hash @coder.load(yaml)
+      end
+
+      def self.as_indifferent_hash(obj)
+        case obj
+        when ActiveSupport::HashWithIndifferentAccess
+          obj
+        when Hash
+          obj.with_indifferent_access
+        else
+          HashWithIndifferentAccess.new
+        end
+      end
+    end
   end
 end
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index 3a5d84df9f..79476ed2a4 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -3,8 +3,10 @@ require 'models/admin'
 require 'models/admin/user'
 
 class StoreTest < ActiveRecord::TestCase
+  fixtures :'admin/users'
+
   setup do
-    @john = Admin::User.create(:name => 'John Doe', :color => 'black', :remember_login => true, :height => 'tall', :is_a_good_guy => true)
+    @john = Admin::User.create!(:name => 'John Doe', :color => 'black', :remember_login => true, :height => 'tall', :is_a_good_guy => true)
   end
 
   test "reading store attributes through accessors" do
@@ -52,18 +54,19 @@ class StoreTest < ActiveRecord::TestCase
   end
 
   test "convert store attributes from Hash to HashWithIndifferentAccess saving the data and access attributes indifferently" do
-    @john.json_data = { :height => 'tall', 'weight' => 'heavy' }
-    assert_equal true, @john.json_data.instance_of?(Hash)
-    assert_equal 'tall', @john.json_data[:height]
-    assert_equal nil, @john.json_data['height']
-    assert_equal nil, @john.json_data[:weight]
-    assert_equal 'heavy', @john.json_data['weight']
-    @john.height = 'low'
-    assert_equal true, @john.json_data.instance_of?(HashWithIndifferentAccess)
-    assert_equal 'low', @john.json_data[:height]
-    assert_equal 'low', @john.json_data['height']
-    assert_equal 'heavy', @john.json_data[:weight]
-    assert_equal 'heavy', @john.json_data['weight']
+    user = Admin::User.find_by_name('Jamis')
+    assert_equal 'symbol',  user.settings[:symbol]
+    assert_equal 'symbol',  user.settings['symbol']
+    assert_equal 'string',  user.settings[:string]
+    assert_equal 'string',  user.settings['string']
+    assert_equal true,      user.settings.instance_of?(ActiveSupport::HashWithIndifferentAccess)
+
+    user.height = 'low'
+    assert_equal 'symbol',  user.settings[:symbol]
+    assert_equal 'symbol',  user.settings['symbol']
+    assert_equal 'string',  user.settings[:string]
+    assert_equal 'string',  user.settings['string']
+    assert_equal true,      user.settings.instance_of?(ActiveSupport::HashWithIndifferentAccess)
   end
 
   test "convert store attributes from any format other than Hash or HashWithIndifferent access losing the data" do
diff --git a/activerecord/test/fixtures/admin/users.yml b/activerecord/test/fixtures/admin/users.yml
index 6f11f2509e..e2884beda5 100644
--- a/activerecord/test/fixtures/admin/users.yml
+++ b/activerecord/test/fixtures/admin/users.yml
@@ -5,3 +5,6 @@ david:
 jamis:
   name: Jamis
   account: signals37
+  settings:
+    :symbol: symbol
+    string: string
-- 
cgit v1.2.3