32
votes

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?

2
Even if you're not using rails is there any reason why you just couldn't require 'active_support/core_ext/hash/indifferent_access', and there's your solution? - Casper
I haven't installed active_support gem and was wondering should I be installing it just to get this feature. - Josnidhin
If you don't want to, copy/paste the code into your own hash with indifferent access class. I haven't looked at Rails' solution, but I've written one myself before, can't be more than 50 LOC. - Joshua Cheek
I thought about that too..checking here to see if there is a better way. - Josnidhin
I am new to ruby so thought why not check with the community just in case I have missed something. I am open to copying. - Josnidhin

2 Answers

53
votes

You lose nothing except a few kB of disk space by installing the Active Support gem. In your code, you require only the function you want:

require 'active_support/core_ext/hash/indifferent_access'

That way, you can be sure you are not getting anything else to mess up your namespace.

19
votes

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).