Search code examples
ruby-on-railsrubyactivesupport

Hash with indifferent access


I have a non-Rails project in which I am loading some settings from a YAML file:

config = YAML::load File.open("#{LOG_ROOT}/config/database.yml")

I can only access this hash like config["host"], config["username"] etc.

I want indifferent access so I can use both :host and "host".

The reason is, that one of the gems in the project to which I am passing this hash seems to be accessing it using symbols and it fails currently.

What is the best way to create a hash with indifferent access in this scenario?


Solution

  • Let the config hash return the value for the stringified version of the key:

    config = {"host"=>"value1", "Username"=>"Tom"}
    config.default_proc = proc{|h, k| h.key?(k.to_s) ? h[k.to_s] : nil}
    p config[:host] #=> "value1"
    

    The default_proc runs everytime when a key is not found in the hash. Note this is only half of indifferent access: config["host"] will result in nil if the key :host is present. If that has to work too:

    config.default_proc = proc do |h, k|
       case k
         when String then sym = k.to_sym; h[sym] if h.key?(sym)
         when Symbol then str = k.to_s; h[str] if h.key?(str)
       end
    end
    

    See the comments about limitations of this approach (tltr: separate values for :a and 'a' are possible, does not take into account Hash.delete and others).