From 940c135175cba6e6611cb67a93b9370da4e81fd9 Mon Sep 17 00:00:00 2001 From: Andrey Voronkov Date: Tue, 22 May 2012 18:08:36 +0400 Subject: Convert Hash to HashWithIndifferentAccess in ActiveRecord::Store. In order to make migration from 3.x apps easier, we should try to convert Hash instances to HashWithIndifferentAccess, to allow accessing values with both symbol and a string. This is follow up to changes in 3c0bf043. --- activerecord/lib/active_record/store.rb | 37 ++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb index ce2ea85ef9..fdd82b489a 100644 --- a/activerecord/lib/active_record/store.rb +++ b/activerecord/lib/active_record/store.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/hash/indifferent_access' + module ActiveRecord # Store gives you a thin wrapper around serialize for the purpose of storing hashes in a single column. # It's like a simple key/value store backed into your record when you don't care about being able to @@ -13,9 +15,6 @@ module ActiveRecord # You can set custom coder to encode/decode your serialized attributes to/from different formats. # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+. # - # String keys should be used for direct access to virtual attributes because of most of the coders do not - # distinguish symbols and strings as keys. - # # Examples: # # class User < ActiveRecord::Base @@ -23,8 +22,12 @@ module ActiveRecord # end # # u = User.new(color: 'black', homepage: '37signals.com') - # u.color # Accessor stored attribute - # u.settings['country'] = 'Denmark' # Any attribute, even if not specified with an accessor + # u.color # Accessor stored attribute + # u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor + # + # # There is no difference between strings and symbols for accessing custom attributes + # u.settings[:country] # => 'Denmark' + # u.settings['country'] # => 'Denmark' # # # Add additional accessors to an existing store through store_accessor # class SuperUser < User @@ -35,24 +38,38 @@ module ActiveRecord module ClassMethods def store(store_attribute, options = {}) - serialize store_attribute, options.fetch(:coder, Hash) + serialize store_attribute, options.fetch(:coder, ActiveSupport::HashWithIndifferentAccess) store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors end def store_accessor(store_attribute, *keys) keys.flatten.each do |key| define_method("#{key}=") do |value| - send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash) - send(store_attribute)[key.to_s] = value + initialize_store_attribute(store_attribute) + send(store_attribute)[key] = value send("#{store_attribute}_will_change!") end define_method(key) do - send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash) - send(store_attribute)[key.to_s] + initialize_store_attribute(store_attribute) + send(store_attribute)[key] end end end end + + private + def initialize_store_attribute(store_attribute) + case attribute = send(store_attribute) + when ActiveSupport::HashWithIndifferentAccess + # Already initialized. Do nothing. + when Hash + # Initialized as a Hash. Convert to indifferent access. + send :"#{store_attribute}=", attribute.with_indifferent_access + else + # Uninitialized. Set to an indifferent hash. + send :"#{store_attribute}=", ActiveSupport::HashWithIndifferentAccess.new + end + end end end -- cgit v1.2.3