Rails’ HashWithIndifferentAccess
A Ruby Hash is a very powerful collection type to use in Ruby. When working in Rails sometimes you’ll have symbols as keys or sometimes they will be strings. If you use a normal Hash these will store as different keys for the same name.
sample = Hash.new sample[:a] = 1 sample["a"] = 2 sample # => {:a=>1, "a"=>2}
Rails has another version of Hash that will map strings and symbols as the same kind of key: HashWithIndifferentAccess . So you don’t have to worry about any possible mixup, especially when handling Rails form parameters.
sample2 = HashWithIndifferentAccess.new sample2[:a] = 1 sample2["a"] = 2 sample2 # => {"a"=>2}
When you use Rails it add additional methods to both the traditional Hash Object and HashWithIndifferentAccess.
Lets say you’re writing a helper method for a form that updates an object and you want to allow people to overwrite the form_for options.
# some_helper.rb def form_for_update_helper(options = {}) options = HashWithIndifferentAccess.new(options) defaults = { method: :patch, remote: true, authenticity_token: true } options.reverse_merge!(defaults) end # controller for form def view @thing = Thing.find(thing_params[:id]) @form_for_options = form_for_update_helper end # some view partial <%= form_for(@thing, **@form_for_options.symbolize_keys) do |f| %>
reverse_merge! will add any key value pairs that don’t exist within the Hash you are calling it on. In the helper method above we first convert any incoming Hash to a HashWithIndifferentAccess type. We then define our defaults for this Hash and we update it with reverse_merge! . In the controller we may add any extra options if we’d like; such as form_for_update_helper( url: comment_path ). And in the view the double star is the keyword expansion method. So the **@form_for_options.symbolize_keys changes into the parameters method: :patch, remote: true, authenticity_token: true for out form_for method.
It’s important to use the symbolize_keys method when expanding into keyword args as the HashWithIndifferentAccess defaults its keys to Strings and that will not work as form_for parameters.
If you’d like to see this code in use I’ve implemented it in Dynaspan 0.1.2 beta1 . There are plenty of other cool things you can do with this, but I’ve shown what I like most with HashWithIndifferentAccess . If you’re interested in more details on Ruby Collection Types feel free to read my other post.
Summary
I love this collection type simply for its simplicity and less error prone convenience. Know any other cool things you can do with HashWithIndifferentAccess ? Then feel free to comment about it below.
Please feel free to comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan!
God Bless!
-Daniel P. Clark
Image by Michael Coghlan via the Creative Commons Attribution-ShareAlike 2.0 Generic License