Words and Code

One writer’s journey from words to code.

Peeking Under the Hood of ActionController Parameters, Part 2

#technicaltuesdays, computer science, rails, ruby


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
{ order:
  {
      total: 100.00,
      number: 'ABC123'
  }
}

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
if params[:order].present?
  @order.create(order_params)
end

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
module ActionController
  class Parameters < ActiveSupport::HashWithIndifferentAccess
  end
end

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 the params 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
 rails c
Loading development environment (Rails 4.1.4)
irb(main):001:0> h = {a: 'hi', b: 'hello'}
.with_indifferent_access
=> {"a"=>"hi", "b"=>"hello"}
irb(main):002:0> h.class
=> ActiveSupport::HashWithIndifferentAccess

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
permitted_params = params.require(:order).permit(:total)

permitted_params
# => {"total"=>"100.00"}

permitted_params.class
# => ActionController::Parameters

Except usually we wrap it in a _params suffixed method like this:

1
2
3
def order_params
  params.require(:order).permit(:total)
end

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
params = ActionController::Parameters.new

params.permitted?
# => false

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
def permitted?
  @permitted
end

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
params = ActionController::Parameters.new
params.permitted?
# => false

params.permit!
params.permitted?
# => true

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
# def blog_params
  # params.require(:blog).permit(:title, 
      # :author, :published_at, :tags, 
      # :excerpt, :image, :image_caption, :external_url)
# end

def blog_params
  params.require(:blog).permit!
end

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
class Metal < AbstractController::Base
  # Very truncated from the Rails source code!
  
  attr_internal :headers, :response, :request

  def initialize
      @_headers = {"Content-Type" => "text/html"}
      @_status = 200
      @_request = nil
      @_response = nil
      @_routes = nil
      super
  end
end

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
def params
  @_params ||= request.parameters
end

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:

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 like permitted? and permit! 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.