Rails Params: Where do they come from?

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:

Why is 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.

Rack

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 env variable (Ruby hash) that Rails can understand.
  • Rails takes that env variable, 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.

Notice line 19

After submitting a test form request, I was in.

Snooping around in pry revealed some interesting things:

  1. params is a method on the ActionController::StrongParameter class.
  2. While params appears to be a hash, it is actually an instance of the ActionController::Parameters class.

If you are unfamiliar withActionController::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:

params.require(:post).permit!

Inside of 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 ActionController::Parameters class.

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.

ActionController::Base inherits from ActionController::StrongParameters

We have access to params because our controllers are descendants of ActionController::StrongParameters and inherit all of its instance methods!

Woohoo! Mystery Solved!

Right?

Not quite.

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?

My reaction to “request.parameters”

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. Essentially, ActionController::Metal is a barebones version of ActionController::Base and is one of the initial ancestors of our Rails app.

ActionController::Base ancestor list

If you look at its source code, you will find several references to request as well as it’s own set of params methods.

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 binding.pry.

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.

And this is only what would fit in my terminal screen.

While this log is impressive and monstrous, there only a few lines that we need to worry about in regards to params and ActionController::Metal.

Relevant lines highlighted in blue
ActionController::Metal#dispatch
ActionController::Metal::action

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

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 #parameters.

This is where request.parameters originates from!

We did it!

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 env object and passes it to Rails.
  • Rails passes that env object around internally until it makes it’s way to ActionController::Metal::action.
  • ActionController::Metal uses that env object to create an instance of ActionDispatch::Request and store it in its @_request instance variable, available via the request getter method.
  • ActionDispatch::Request parses and formats the parameters from the original HTTP request.
  • ActionController::StrongParameters uses the #parameters method supplied by the ActionDispatch::Request instance object to store the formatted parameters in the @_params instance variable.
  • The data in that@_params instance variable is available to us and our controllers via params.

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!