Rails parameter parsing can be hard to understand, but it goes beyond that.
Just understanding the “params” object in your controller actions is a little tricky. Ever notice how params[:bob] and params[“bob”] both work? The magic type behind that is called HashWithIndifferentAccess. Yeah, it’s a mouthful. I wish I were making that up, but no.
There’s a lot of interesting code in Rails with interesting Ruby tricks – so let’s de-magic the params object, find some bugs with it and get better at Ruby, all at once.
I like going straight to the source, so here is the current version of the file in ActiveSupport. You can just read, or follow along on GitHub.
It’s a nice readable 176 lines.
What’s It Do?
In Rails you can say params[“people”] or params[:people], and either way you get the same thing back. Simple enough, right? Well, mostly.
You can see on line 7 that it inherits from Hash — Ruby is convenient that way.
The biggest magic is on line 159, in fact, in convert_key(). It just converts any key it sees to a string.
So HashWithIndifferentAccess is just a regular hash, but any symbol key is converted to a string. If you put in a key that was, say, nil, nothing would change.
Fun Ruby Tricks
This class shows one fun ruby trick – aliasing and replacing a method. The old hash update and assignment are renamed to regular_update and regular_writer.
You can also see just how many methods you need to override to make your own hash… Default(), brackets, assignment, update(), key(), fetch(), dup(), merge() and many more. Making your own variant type is a bad idea unless you spend some quality time with the core language documentation and see what actually changes.
But if you do, it can be really useful, like the Rails params object!
What Can Go Wrong?
There are some oddities, like stringify_keys! and symbolize_keys! — what do you do for a HashWithIndifferentAccess? They do nothing… Fair enough.
But right next to convert_key is convert_value()… And it’s interesting – if you assign an array to the hash, you can see that they do a .map! and change the array before assigning it.
That means if you said:
Your array will actually change! That hash inside will suddenly have indifferent access and it will point to a whole different object!
(Why? I spent awhile with git log trying to answer that. Looks like it’s because just mapping the Array won’t give you the right subclass, so they rewrite the innards instead! Map into an unknown Array subclass is an unsolvable problem in Ruby, actually…)
Up To Date?
Nothing stays the same forever. In Rails, not much stays the same for long. So here is a link to the latest HashWithIndifferentAccess in GitHub, whenever you go look for it.
Enjoy Getting Your Hands Dirty?
Do you enjoy code spelunking to learn about Ruby and Rails? I’m writing a book about that. The first couple of chapters are free and awesome — sign up below and download them.