How does respond_to work in the Rails controllers?

by tokumine

The respond_to bloc party

This is one of the first questions I’m asked whenever I introduce someone to Ruby/Rails from another language.

WTF is going on?!?! (aka the rails respond_to block)

respond_to do |format|
  format.html
  format.xml  { render :xml => @mah_blogz }
end

The key thing to understand is that respond_to is a method attached to your controllers superclass: ActionController, and we are passing in as an argument something called a block:

respond_to (BLOCK STARTS HERE) do |format|
  format.html
  format.xml  { render :xml => @mah_blogz }
end (BLOCK ENDS HERE)

WTF is a block?

It’s like the innards of a function. Read this great post by Eli Bendersky.

Digging into Rails source: respond_to (edited for clarity)

def respond_to(&block)
  responder = Responder.new(self)
  block.call(responder)
  responder.respond
end

What’s the “&” prefixing the block argument?

We are passing respond_to a ruby block. That block is converted to a Proc due to the “&” prefix to the block argument. The functionality of this prefixed “&” is a Ruby feature. Procs are callable, anonymous functions.

So what’s with the |format| stuff back in the controller?

|format| defines that the block takes one argument (referred to inside the block as ‘format’).

This means that inside the respond_to method, we end up with a Proc that takes one argument. This argument has the .xml and .html methods called on it. What do we pass into the Proc as an argument when we call it from inside respond_to? We pass in an instance of the Responder class.

So we end up calling .html and .xml on an instance of the responder class as it is passed into the block (that’s been converted to a Proc) inside the respond_to method… Phew.

So here we are

I’ll leave you to dig further, but basically from here the Responder instance handles .html and .xml via method_missing, depending on mime types your rails app can process. This allows you to configure other mime types from your rails app by registering them in your initialisers.

Advertisements