Throughout this post, we will investigate one source of that "Rails magic" that perplexes developers: how Rails assigns Controller instance variables to view templates.
Take the following for example:
Have you ever wondered how on earth the view template
index.html.erb gets access to the instance variable
@my_index_var ? Well, let's find out.
What is Rails Anyway?
Without jumping too far into Rails source code (yet), recall that the Ruby on Rails source code isn't a massive monolithic code base. Rather, it is a collection of isolated Ruby gems that are strung together to make up the tool we call Rails. Inside the codebase, the responsibilities of template rendering, rack request routing, object relational mapping and more are divided across several key gems that are usually prefixed with
Active* . All these gems work together to deliver the end framework you and I use for web application development. With the primer out of the way, let's begin the journey to expose how controller instance variables are assigned to views.
Our first stop is the gem called
ActionPack defines several important modules including
AbstractController. Thematically the gem revolves around framework controller code, rendering and routing of rack requests.
For our investigation, the first class in
ActionPack to take a look at is
ActionController::Base . This is the base class that all your application controllers will inherit from.
class ApplicationController < ActionController::Base end class Posts < ApplicationController end
ActionController::Base is the core of a web request in Rails. It gives controllers the ability to define actions for requests, and have requests routed to those actions in order to render a template or redirecting somewhere else.
ActionController::Base inherits from a parent class
ActionController::Metal which in turn inherits from
ActionController::Metal isn't very interesting so we won't spend too much time on it. The in-line source comments describes it as:
... the simplest possible controller, providing a valid Rack interface without the additional niceties provided by ActionController::Base
Circling back to
ActionController::Base, the class itself doesn't actually define many interesting methods. Instead
ActionController::Base is a composite of various modules such as
Cookies that are loaded into the class at require time. One module in deserving of our focus is
AbstractController:: module provides
ActionController a variety of handy methods including the
render method we know and love.
class Posts < ApplicationController def new render 'new' end end
In addition to
render, the module also contains a method called
view_assigns . The
view_assigns method is pretty quirky:
Basically, it calls the Ruby base
instance_variables which returns an Array of all the currently defined instance variables for an object and creates a hash mapping their names to their values. Now, recall that
AbstractController::Rendering is a module that is mixed in the class
ActionController::Base which is what your concrete controller implementation inherits from. This means that if
view_assigns is invoked from your controller all the currently defined instance variables will be assigned to this hash. Interesting.... We've discovered how the instance variables are captured but how does the controller connect to the view?
Returning to our previous note about Rails being built from "Action" type gems, view and templating logic live in a fun little gem called
ActionView is responsible for understanding how to render different template engines like embedded ruby, and HTML. To draw the lines of responsibility a bit more clearly,
ActionController can tell us what to render, but, it does not know how to render it. That's
The base class for
ActionView is somewhat anti-climatically named
ActionView::Base. The class itself does quite a bit of serious business. The job of hierarchal template rendering doesn't sound like a laughing matter (but then again maybe it is? DHH seems to have a lot of fun on Twitter). Anyway, when an
ActionView is instantiated it eventually calls an important method named assign with a payload called
assign method is responsible for taking the
new_assigns argument iterating through it to define instance variables using the
instance_variable_set method. These instance variables are named, assigned and set in the template object using the key and value pairs in the
new_assigns hash. This means that the template object rendered will have access to these variables immediately after being instantiated. Does this look familiar?
Ok so that all makes sense but how and where does the
new_assigns payload come from?
ActionView Take Two
In the same
ActionView gem lives a module called
ActionView::Rendering where a view's "context" is built before it is rendered to the screen. In specific, the
ActionView::Rendering module has a public hook method called
render_to_body which is invoked by
ActionController when the
render method is called. Under the hood
render_to_body calls a private method
_render_template does quite a number of things but the important line for us to close in on is the invocation of the method
view_context method is the glue that wraps everything together here:
It instantiates a new
ActionView::Base object (
view_context_class) and passes it an
assigns hash consisting of... a call to the
view_assigns method from
This means that a hash of the currently defined instance variables for the controller that called render will be passed along as an argument to the
initialize method. Nice! We've found the source. Here is the the above words in image form:
Phew... quite the journey but we've pulled the curtains back on one of the more opaque parts of the Ruby on Rails framework and have made it through in one piece.