The controller’s jobs include:
- work with the request parameters
- determine how to activate the domain logic and data
- respond to requests
The parameters give the controller the information it needs.
At the same time, parameters are often the cause of confusion and complication in a controller. A controller method should be about eight lines of Ruby. But many actions spiral out of control with all kinds of switching based on the input parameters.
Developers commonly refer to
params as a variable, but it isn’t – it is a helper method provided by
ActionController which returns a hash containing the request parameters.
Developers new to Rails often struggle with the nested hashes inside
params. When processing a form,
params might look like this:
It’s a little easier to understand the structure by converting to YAML (using
1 2 3 4 5 6 7 8 9
The outer hash has these keys:
utf8- this marker is in all form submissions to force Internet Explorer to properly encode UTF-8 data
authenticity_token- a security mechanism used by the
protect_from_forgerymethod called in
article- the sub-hash containing the data from our HTML form
commit- the label text of the button that was clicked
action- which action is being executed
controller- which controller is being executed
Given those parameters, asking for
params[:article] will return the nested hash:
create action can use those values to build the
The original hash had a key
"article", but the example accessed it by calling
params[:article] with a symbol. How?
2.1.1 :001> 2.1.1 :002> 2.1.1 :003> 2.1.1 :004>
Hash with key
"article" will not respond to
Within the Rails internals there are almost no hashes. Instead, what look like hashes are actually instances of
It’s named IndifferentAccess because it allows us to do lookups with either string or symbol versions of the keys. Most often we’ll access them using the symbol version.
Symbols vs. Strings
If we can use either symbols or strings, why prefer symbols?
- It’s one fewer character to type
- Strings are for users. They’re things we take in from the form, data we store in the database, output we show in the view.
- Symbols are for programs. They’re used for internal messaging and data structures like traversing a hash
- Symbols use significantly less memory, saving Garbage Collection cycles
Use symbols when you can, strings when you have to.
params in a Simple Action
The most straightforward usage of
params is to lookup a single key and do something with the retrieved value:
1 2 3
params with Mass Assignment
Typically in a
create action we’ll make use of mass-assignment:
1 2 3
That is equivalent, given our example
params, to this:
In this long form, we’re building up a hash with keys
:body, but it’s pointless! When we query for
params[:article] we get back the nested hash. That hash has keys
:body – exactly as we’re building up here. So when we use this form:
1 2 3
We’re passing in a hash of data. This method is preferred because it is shorter to read/write and, more importantly, it doesn’t need alteration if we add new attributes to the model and form.
params Gone Wrong
Here’s one of the common ways that developers abuse parameters and controller actions:
1 2 3 4 5 6 7 8 9
We’re anticipating a parameter named
:order_by and want to sort based on that. Here’s a cleanup of just the Ruby syntax to use a
1 2 3 4 5 6 7
Whenever you’re tempted to write
variable = case #... or
variable = if #..., you should really encapsulate the right side into a method. In the "Mass Assignment" section, we said that sending the parameters down to the model in bulk was a maintenance win because the controller won’t have to change when the model adds attributes.
If changes to one component of the system necessitate changes in another then those objects are coupled. By having this logic for sorting in the controller, we increase the coupling between controller and model which, in the long run, hurts. What if we emulated the idea of proxying to the model?
1 2 3
Imagine we have a class method on
ordered_by. We send the parameter down to the model and let it figure out what that string means in the context of our domain and data – which is exactly the job of the model. The implementation there looks familiar:
1 2 3 4 5 6 7 8 9 10
This isn’t about writing less code, it’s about writing code in the right place. The model is responsible for logic and working with data. Don’t let it leak up into your controllers!
Use the Blogger sample application to complete the exercises in this section. See the Setup Instructions for help.
- Open the
ArticlesControllerin Blogger and find the
createaction. As an experiment, rewrite it setting each form value individually rather than using mass-assignment.
- In the
indexaction, implement handling for an
order_byparameter as modeled in the text. Add an option to sort by
- Add links to the index page which, when clicked, change the sorting to
"word count", or
"published". CHALLENGE: Add links and handling so each of these can be inverted.
- In the
ArticlesController, handle a parameter named
"limit"which will limit how many articles are displayed. If there is no
"limit", display all the articles.
- Push the logic from exercise 4 down to the model, creating a method named
- Rails Guide on Parameters: http://guides.rubyonrails.org/action_controller_overview.html#parameters
- UTF-8 Hacks for Internet Explorer: http://stackoverflow.com/questions/3222013/what-is-the-snowman-param-in-rails-3-forms-for/3348524#3348524