Rails 对 Hash 做了扩展, 其中之一是 HashWithIndifferentAccess .

他的作用是消除 Hash 键的 Symbol 和 String 的差异(统一转换为 String).

require "active_support/core_ext/hash/indifferent_access"

origin_hash = {a: {b: {c: "CcC"}}}

rails_hash = ActiveSupport::HashWithIndifferentAccess.new origin_hash
# {"a"=>{"b"=>{"c"=>"CcC"}}}

rails_hash['new_key'] = "new value"
# {"a"=>{"b"=>{"c"=>"CcC"}}, "new_key"=>"new value"}

rails_hash[:new_key] = "update value"
# {"a"=>{"b"=>{"c"=>"CcC"}}, "new_key"=>"update value"}

HashWithIndifferentAccess 不会自动注入到 Hash 中, 只是在需要用的地方把 Hash 转为 HashWithIndifferentAccess.

需要注意的是: 如果构造函数的入参 origin_hash 同时中含有 "key":key, 并不能保证最终得到留下的是哪个值, 因为 each_pair 方法不能保证遍历的顺序.

activesupport/lib/active_support/hash_with_indifferent_access.rb

def initialize(constructor = {})
  if constructor.respond_to?(:to_hash)
    super()
    update(constructor)

    hash = constructor.to_hash
    self.default = hash.default if hash.default
    self.default_proc = hash.default_proc if hash.default_proc
  else
    super(constructor)
  end
end

   
def update(other_hash)
  if other_hash.is_a? HashWithIndifferentAccess
    super(other_hash)
  else
    other_hash.to_hash.each_pair do |key, value|
      if block_given? && key?(key)
        value = yield(convert_key(key), self[key], value)
      end
      regular_writer(convert_key(key), convert_value(value))
    end
    self
  end
end

常见的例子如 Parameters :

 params
=> <ActionController::Parameters {"utf8"=>"✓", 
"authenticity_token"=>"dFQ+iub6El78XUyjgzYgKkwdCOMCu+e/WhJ5f7styIXTHzu/JFbLCZE02o+CSbYQTPyJcjquXTbj8fpBaLL/GQ==", 
"commit"=>"Create User", "controller"=>"users", "action"=>"create"} permitted: false>
[2] pry(#<UsersController>)> params.class
=> ActionController::Parameters
[3] pry(#<UsersController>)> params[:utf8]
=> "✓"
[4] pry(#<UsersController>)> params["utf8"]
=> "✓"