This blog post is part of a series on strong parameters. Read Part 1 here.
Rails often feels like a black box, with all of the complex logic abstracted away and hidden from view. This leaves behind a clean, convention-abiding framework where form follows function. But another, perhaps less-intentional side effect of all the “metaprogramming away” of this framework’s complexities is a frequent lack on clarity of what’s going on behind the scenes.
In my year of working with Rails, I’ve come to appreciate and respect so many things about it. In fact, I think that there’s something incredibly approachable about Ruby as one’s first programming language, with Rails as its corresponding framework. And of course, there’s something truly unique and welcoming about the Ruby and Rails programming community. All of these factors combined make it so easy for beginners of such different backgrounds to start building applications very quickly.
However, once you get past the intial stage of building applications with the basic CRUD operations, or if you’re trying to build something a bit more complex with added functionality, or trying to integrate with another framework, you eventually hit a wall where you realize that that you’re not completely sure how something works. This can be a hard wall to climb over, especially given the fact that you can start building things very quickly with Rails. Some people have heavily critiqued this aspect of the framework, arguing that it’s detrimential to abstract away so much of what’s actually going on, which makes it difficult for people to understand what their code is truly doing under the hood. I’ve peronally encountered this “wall of abstraction” a few different times, but each time I found some piece of logic that worked differently than I thought it would, it only lead me to learn something new about the framework. Most recently, that lead me down a wild goose chase into the source code for ActionController::Parameters
— a class that I didn’t even know existed!
Quack Like A Hash
There’s a well-known saying that Rubyists often attribute to duck typing: if it looks like a duck, and quacks like a duck, it probably is a duck. In other words, if an object behaves like another object, it doesn’t matter that it’s of a certain class or “type” (or species!) of another object, just so long as it can respond to the correct method calls to implement the behavior of that object.
This saying actually comes from a concept referred to as the “duck test”, which is a form of abductive reasoning, which is based on the idea that one can identify an unknown object by observing its habitual characteristics. However, this can be a little bit tricky because sometimes, just because something quacks like a duck and waddles like a duck doesn’t mean that we should stop questioning what it actually is and just assume that it’s a duck!
Let me explain with an example. Whenever we send or receive data from the server, it appears that everything is being sent as a Hash
. If we look at our GET
requests or POST
and PUT
requests using HTTP protocol, our data usually looks like some variation on a theme of something like the following:
1 2 3 4 5 6 |
|
If it acts like a hash, and quacks like a hash, it must be a hash, right? Well, not exactly. Last week we learned that params
in our controllers are actually instances of ActionController::Parameters
. But this data that we’re sending back and forth still looks like its a plain old Ruby Hash
. It also seems to behave like a hash, right? We can do something like this
1 2 3 |
|
and access a key in this so-called “hash” the same way we would with any other Hash
data structure. So, what’s different about ActionController::Parameters
?
Well, let’s find out. We’ll start by opening up the source code for this class, which lives inside of the ActionController
module. The first thing we’ll notice is this:
1 2 3 4 |
|
Interesting! The Parameters
class subclasses from ActiveSupport::HashWithIndifferentAccess
. If we were really curious about what type of object this class inherits from, we could trace that back to see that HashWithIndifferentAccess
simply inherits from the class Hash
. So, what makes HashWithIndifferentAccess
different from plain old Ruby hashes?
Well, the Rails guides answers this question pretty well:
HashWithIndifferentAccess
implements a hash where keys:foo
and"foo"
are considered to be the same. Internally symbols are mapped to strings when used as keys in the entire writing interface. You are guaranteed that the key is returned as a string. This class is intended for use cases where strings or symbols are the expected keys and it is convenient to understand both as the same. For example theparams
hash in Ruby on Rails.
Awesome, this answers our question and more! This subclass gives us the flexibility to access the keys in a params
“hash” by either a string or a symbol. The HashWithIndifferentAccess
class still responds to the majority of the same methods that a Ruby Hash
instance does, but with some added functionality, which can be especially handy if we’re dealing with JSON responses.
Protip: we can easily create HashWithIndifferentAccess
instances by creating a Ruby Hash
instance and calling with_indifferent_access
on it. This method is available to us since core extensions has the with_indifferent_access
method defined on the Hash
class by default:
1 2 3 4 5 6 7 |
|
So, parameters aren’t quite a hash, but they quack pretty much exactly like how a hash would quack. But there must be a good reason why they are different classes, right? Let’s investigate further.
Waddle Like A Param
On a very basic level, ActionController::Parameters
could be simplified to hashes with some serious restrictions and permissions. Because ActionController::Parameters
inherits from ActiveSupport::HashWithIndifferentAccess
, we can fetch values from our params “hash” using a symbol :key
or a string "key"
. But there are a few things that we can’t do so easily, and that’s where the functionality of ActionController::Parameters
really starts to come into play and begins to make a lot more sense.
We can easily create a new instance of ActionController::Parameters
. In fact, we do it within the context of our controllers all the time!
1 2 3 4 5 6 7 |
|
Except usually we wrap it in a _params
suffixed method like this:
1 2 3 |
|
Here is one example of the added functionality of this class. When we create a new instance of a ActionController::Parameters
class, it is by default, not permitted.
1 2 3 4 |
|
The permitted?
method actually doesn’t do anything more than return an attribute on an instance of a ActionController::Parameters
object, called @permitted
:
1 2 3 |
|
So the question is, where does this attribute get set? Well, there are two places that can set this attribute to true
. The first place is a method that we’re already familiar with: the permit
method! As we learned last week, this method calls params.permit!
as its last line, after it filters out any paramters that aren’t permitted scalar values (think Symbol
, String
, or Hash
). However, we can also just call permit!
on an instance of ActionController::Parameters
ourselves.
1 2 3 4 5 6 7 |
|
The permit!
method sets the @permitted
attribute to true
, and returns self
, which in this case is just the instance of the params class. This method is quite useful for mass assignment, since it effectively removes the need to list all the permitted values. It can be handy in a controller that is used only by admins that should have control over — and can be trusted with — updating all attributes of an object:
1 2 3 4 5 6 7 8 9 |
|
However, this doesn’t really make sense to use unless you are sure that you want to allow all the values to the params hash to be whitelisted. Proceed with caution!
Heavy Metal Controllers
Now that we know a little bit more about ActionController::Parameters
and what kinds of ducks — oops, I mean objects — they really are, there’s one question that we probably still have floating around in our heads: where on earth does our params come from during the request-response cycle…and how does it get set on a controller?
To answer this question, we must trace back how a parameter comes in from a request, and then how it is set on a controller. It isn’t magic (even though it seems like that’s the case!). In reality, it’s actually happening in a cool place called ActionController::Metal
. Yes, that’s a thing, and it happens to be the very class that ActionController::Base
inherits from. By default, we should always inherit from ActionController::Base
, because it gives us a ton of functionality and modules that help speed things up.
But even ActionController::Base
has to come from somewhere, right? And that somewhere happens to be ActionController::Metal
, which is the simplest possible controller that we can create in Rails, which provides little more than a Rack interface. Here’s a very, very simplified version of what that class looks like in the Rails source code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
As a request goes out or a response comes in, these values are set as instances on the controller itself! So if there was any doubt in your mind, everything in Ruby is an object — even controllers are just objects with instance variables and attr_readers and attr_accessors.
So, where do our parameters get set? Well, they also live in an instance attribute called @_params
, which is set by a params method. The parameters come in from a request, and are beautifully memoized in a method that looks like this:
1 2 3 |
|
Pretty amazing, right? That black box of ActionController
and strong parameters doesn’t seem so intimiating any more, does it?
It’s a little strange that we always talk about and use params.require
and the permit
method so often in Rails, particularly when we are first starting out, but we don’t necessarily question where it comes from! Or how it is set or being used, for that matter! But now we know. And we’ll never mistake our strong params for a simple Ruby hash again.
Or a duck, for that matter.
Rails 5 Update
I recently learned that there’s a significant change coming to ActionController::Parameters
with the soon-to-be-released Rails 5! Namely, another Rubyist brought this to my attention over the interwebz:
@ThePracticalDev Nice post! Just a friendly change advisory, AC::Parameters composes an object and no longer inherits from HWIA in Rails 5.
— Jon Atack (@jonatack) December 1, 2015
It turns out that ActionController::Parameters
will soon return an object, rather than a Hash
instance, which also means that it will no longer Hash will no longer inherit from ActiveSupport::HashWithIndifferentAccess
as this post explains. Check out this great blog post by Eileen Uchitelle, a developer at Basecamp, to learn more about how ActionController::Parameters
will be changing with the next release of Rails.
tl;dr?
- An instance of
ActionController::Parameters
behaves a lot like a hash that can be accessed with either symbol or string keys. This class has some added methods likepermitted?
andpermit!
which check and set the@permitted
instance attribute on a params object. - Curious to learn more about how strong params handles nested attributes? Check out this great blog post by Pat Shaughnessy.
- New to
ActionController::Metal
? Head over to the Rails docs to learn more and read this helpful StackOverflow post.