Note: I’m currently using Rails 4.0.0 and the branch of Rails I was browsing through on Github was
4-0-stable. The code you see in this post may look different than the code on your machine if you try to replicate it in the newest version of Rails.
I love Rails. I only started using it a couple weeks ago but even in that short time period I can see why it is a massively successful framework. The easiest way for me to describe it is programming magic.
Run a single command line binary and you have a project skeleton. Run a scaffolding command and you have a working app. It’s like nothing I’ve ever seen.
While Rails’s magic is extremely convenient, it can sometimes be too magical. One of the first Rails conventions that peaked my curiosity bug was
params. Having used Sinatra before, I am familiar with
params. However, it never occurred to me why it was always available without ever needing to be passed in. I can’t explain why this bit of magic nagged at me more than others, it just did.
Whenever I need to implement a
show Controller action,
params is there with the
id. Whenever I am mass assigning a new
ActiveRecord object, there is
params to the rescue with all data I need. It’s just always available at my beck and call — no questions asked.
Many of you may be rolling your eyes right now and asking, “Who cares? It’s convenient and works. Don’t worry about it.” But I do worry about it. I was one of those kids who would push all the buttons on the new TV remote to find out what everything did; much to the chagrin of my parents. I wanted to know how things worked so I could use it to my advantage.
params was my TV remote — I needed to find out how it worked and where it came from.
Before I go any further: this will not be a post about what
params does, how to use it, or what data is passed inside of it. There are a number of guides and blog posts out there that can explain it much better than me. My question was more specific:
params accessible to the actions in Rails controllers and how does it get there?
If that sounds like something you want to know, keep reading.
To understand how Rails interprets parameters, you’ll have to understand the basics of Rack and how Rails interacts with it. According to its website:
Rack provides a minimal interface between web servers that support Ruby and Ruby frameworks.
To use Rack, provide an “app”: an object that responds to the call method, taking the environment hash as a parameter, and returning an Array with three elements:
1. The HTTP response code
2. A Hash of headers
3. The response body, which must respond to each
Rack is basically the go between for web servers and your Rails app.
- It takes the request from the server, translates it into an
envvariable (Ruby hash) that Rails can understand.
- Rails takes that
envvariable, does what it needs with it, and returns a simple array back to Rack with the status code, headers and response body.
- Rack takes that array, translates it back into a proper HTTP response and gives it to the browser to display.
Now that we’ve covered Rack, let’s get into some Rails.
What is “params”?
In order to better understand where
params originates from, we need to better understand what it is. Is it a hash? Method? Something else?
The best way to find this out is to throw a
binding.pry into a controller action and try to reverse engineer the situation by peeling back Rails’s layers. I chose to put the
binding.pry in my
Posts#create since that is where I use
params most heavily.
After submitting a test form request, I was in.
Snooping around in pry revealed some interesting things:
paramsis a method on the
paramsappears to be a hash, it is actually an instance of the
If you are unfamiliar with
ActionController::StrongParameters, it is a class that grants us more security and restrictions over which parameters from
params we will allow in mass assignment. If you’ve ever used this syntax before, you can thank
ActionController::StrongParameters’s source code, all the way at the bottom, you will find
params in all its programmatical glory.
As you can see, pry was correct.
params is a getter method on the
ActionController::StrongParameters class that returns the instance variable
@_params, which is an instance of the
At first glance, this may not seem like much, but this explains why
params is available to us in our controllers without prior reference. A quick check in your pry console will tell you why.
We have access to
params because our controllers are descendants of
ActionController::StrongParameters and inherit all of its instance methods!
Woohoo! Mystery Solved!
While this explains the mystery of the omnipresent
params, it doesn’t explain how it got there. If you’re like me, you want to know the whole story. For example, what was this
request.parameters? Where had that come from?
Where Params Comes From
Note: This next section gets very Rails technical. If you get confused, don’t worry, you’re not alone. It took me several hours of debugging and reading Rails code to figure out the next steps. The following information is the result of those hours.
After more snooping in Rails, I came upon
ActionController::Metal is a barebones version of
ActionController::Base and is one of the initial ancestors of our Rails app.
If you look at its source code, you will find several references to
request as well as it’s own set of
At this point I should mention that, in order to better dive into the depths of Rails, I included a stack trace logger to complement my
The reason I mention this is because if you output this stack trace to the console, you will see all the methods invoked when Rails receives a
env variable from Rack — and there are A LOT. If you want to see everything that happens behind the scenes, take a look at this.
While this log is impressive and monstrous, there only a few lines that we need to worry about in regards to
What this stack trace is saying is that after Rails has been initiated with the
env variable, that variable gets passed all around Rails. At some point, it makes it’s way to the
ActionController::Metal::action class method. Inside this method,
ActionController::Metal initializes a new instance of itself and invokes
#dispatch, which sets
@_request to a new instance of the
ActionDispatch::Request is an interface Rails uses in order to interact with the HTTP Request object (
env) that originated from Rack. One of the methods this API provides is
This is where
request.parameters originates from!
To Wrap Things Up
That was a mountain of jargon I just unloaded onto you, so let me condense the steps down.
- User makes a request to the browser (GET/POST/etc).
- Browser sends HTTP request to Rack where it translates the request into an
envobject and passes it to Rails.
- Rails passes that
envobject around internally until it makes it’s way to
envobject to create an instance of
ActionDispatch::Requestand store it in its
@_requestinstance variable, available via the
ActionDispatch::Requestparses and formats the parameters from the original HTTP request.
#parametersmethod supplied by the
ActionDispatch::Requestinstance object to store the formatted parameters in the
- The data in that
@_paramsinstance variable is available to us and our controllers via
Now that is quite the mouthful.
I hope you found this deep dive the Rails parameters informative and useful. If you noticed any inaccuracies in this post, please let me know and I will dutifully change them. Thanks for reading!