Wham Bam, Thanks, Params

Diana Liao
7 min readMar 26, 2021

How “params” is generated, used, and is stronger than a plain hash

Image by Arek Socha from Pixabay

So you’ve gotten started with Ruby and you’re ready to bring your code to life with Rails! You’ve gotten the basics of a Model-View-Controller (MVC) design down, your RESTful routes are set, and the server’s up and running. The index view was a breeze, but now you’re trying to render a view of a single instance of your model and you recall the standard syntax for that route (for a cat, because, why not):

get ‘cats/:id’, to: ‘cats#show’, as: ‘cat’

Huh, where’s that :id coming from and where does it go? This route syntax is indicating to your application that the :id will dynamically be determined by the value in the URL, which corresponds to the primary key of the instance of Cat from the database you want to see. For example:

http://<server-domain>/cats/42

would bring up information on a cat with an :id of 42.

But how does that :id get passed from the URL to the controller to retrieve the right cat? Nested in the HTTP request is an object, which contains the cat’s id number in a key-value pair. This allows us to find the cat with code like:

class CatsController < ApplicationController  def show
@cat = Cat.find(params[:id])
end
end
photo of a cat, captioned “where did params come from?”
Plot twist, it’s a database of photos of the same cat. My foster cat.

Where did the params hash come from?

The params is a hash-like object which allows users, whether they know it or not, to send information along with requests to the server. (These parameter objects are not quite hashes, but more on that later.) Some of the more common ways params will be sent to your controller are through the URL itself, a query parameter, or a form. Let’s go over those briefly.

The URL

As described in the example above, the params may contain information from the URL in plain sight, like when it identified the primary key of the Cat object the user wanted to find.

Query parameters

As you travel around the web, take note of any question marks that appear in the URLs (or Uniform Resource Locators) of the pages you are browsing. The “?” delimits the boundary between the resource and the querying string, a key-value pair that is communicated through the params.

For example, when you use a search engine, you are sending search terms to run some sort of query. You can see some evidence of this in the results page. If you go to DuckDuckGo and enter “cat photos”, the page you get back will have something like /?q=cat+photos in the URL.

Query parameters are not limited to searching. You may also often see UTM parameters (e.g. ?utm_source=) in your address bar, which can track if a user has reached a site through an e-mail campaign or a different website, or any number of things! Parameters are everywhere, and contain a lot of useful information.

As a quick use example, as the administrator of animal databases, let’s say you branched out from cats and now work with dogs. You entered these dogs into the database as they came (…or used the Faker gem to generate some) and when grabbing a list of them through Dog.all, they display in primary key order.

But what if a user wanted to sort them alphabetically? You could make a new page called index_a_to_z_.html.erb and use a differently ordered list, possibly involving new routes or at the very least, a new view.

Or you could create a link to reload the index page, with parameters, and use some logic in your controller index action to display a different order.

The link in your index view and index action in your dogs controller could look something like this:

<-- dogs/index.html.erb --><%= link_to "See the Dogs in Alphabetical Order", dogs_path(:sort =>'alpha') %>---# dogs_controller.rbdef index
if params[:sort] == 'alpha'
@dogs = Dog.order(:name)
# set instance variable to a list ordered by 'name'
else
@dogs = Dog.all
end
end

Result: http://localhost:3000/dogs?sort=alpha

animated demonstration of reloaded index page with dogs in alphabetical order

Form submissions

Another direct way you may encounter params early on is when you implement a form on your web interface. When a user enters information in a “Contact Us” form, their name and any other information they entered are sent to the controller (and probably ultimately written to your database) via params. As params behave like hashes, information can be infinitely nested and hold quite a lot!

A user wants to add another dog to the database. Let’s take a look at an simple example form and the resulting params object. To add a dog, the user must enter the dog’s name and choose the dog’s type, as predetermined by theType table.

<!-- dogs/new.html.erb --><h1>Add a Doggo!</h1><%= form_with model: @dog do |f| %>
<%= f.label "Name of doggo" %>
<%= f.text_field :name %><br>
<%= f.label "Type of doggo" %>
<%= f.collection_select :type_id, Type.all, :id, :name %><br>
<%= f.submit %>
<% end %>

(If you’re curious, here is the resulting HTML that the Action View form helpers above generated. The form won’t submit anything, but you can look at the HTML and visualize the form better!)

If a user enters a name of “Hamstar”, chooses “smol pupperino”, and hits that submit button, here are the params that are returned to the server (the authenticity_token will differ):

params #=> #<ActionController::Parameters {"authenticity_token"=>"U2K2Nnolg8eGv0ayjnf_1yDUEu1kczoWibSNonUlhGviadr9Ia3uoYnomugheL_A9JtdmpGq3Q7dKnNakIZneA", "dog"=>{"name"=>"Hamstar", "type_id"=>"2"}, "commit"=>"Create Dog", "controller"=>"dogs", "action"=>"create"} permitted: false>

Nestled in there are the values you need to create the dog!

You can also play around with hidden_tag/ hidden_field form helpers and buttons (tiny forms), too, to get and pass through the information you seek in your controller methods.

Is params a hash? How do we use params data?

Technically, paramsis not a hash, though it looks like one. As you can see above, params is actually an instance ofActionController::Parameters. We can interact with params largely like a hash and in fact, up until Rails 5.0, ActionController::Parameters did inherit from the Hash class. As a different class, hashes have some familiar methods that params does not, such as #flatten, #each_key, #each_value, and #map.

But params has a few tricks of its own. Here are all the methods available to params that a hash can’t do.

:permitted?, :to_unsafe_h, :to_unsafe_hash, :permit!, :converted_arrays, :permit, :require, :parameters, :permitted=, :fields_for_style?, :method_missing, :always_permitted_parameters, :always_permitted_parameters=, :init_with, :required

There is a lot here having to do with safety and permissions! That’s because params are the conduit through which the unsafe internet/absent-minded user can send data to your controllers and possibly ultimately your database. And for best practices, you should use…

Strong Params! 🦾

You should be glad that params isn’t exactly a hash because there are two important methods to learn to use on them: #require and #permit. If you recall a little while ago, the params had this little value at the end: permitted: false. If you tried to use params[:dog] directly as you would with a hash to provide attributes to #create a new dog, you would have hit an “ActiveModel::ForbiddenAttributesError” (given standard settings). To allow form data to be used, you would have to do something like this:

# dogs_controller.rbdef dog_params
params.require(:dog).permit(:name, :type_id)
end

The #require method verifies that the params contains a key of and information about a dog. If params comes in without this dog key, most likely it’s form data from elsewhere, or data that isn’t useful to you to create or update an instance of theDogclass. The #permit method specifies which keys within dog to allow the values of. You could allow a user to change a dog’s name, for example, but not their type.

With these two methods performed on params, you can use it directly in creating your dog with :name and :type_id as attributes.

dog_params #=> #<ActionController::Parameters {"name"=>"Hamstar", "type_id"=>"2"} permitted: true>---new_dog = Dog.create(dog_params)

But other than that small feature, treat params as you would any other hash!

…one more factoid before you leave

Now this is for coding cocktail party chatter - did you notice that one of the params-only methods was #to_unsafe_hash? This would turn params into a hash with indifferent access, which you can do anything to that you would with any old piece of nested data. Use your power wisely.

Photo by Elias Castillo on Unsplash

I hope sharing what I may understand about params has helped you in some way. Please let me know if anything seems off, and then we’ll discuss and learn more!

Thanks to those involved in this exasperated Stack Overflow question on what params are and this post on params’ status as a hash or not, who all helped me sleep at night whether they know it or not.

--

--

Diana Liao

Software developer with social justice roots. I love cats, Star Trek, and singing to inspire the downtrodden.