Respond_to do |format| confusion

As a new comer for ruby, I feel confused for this snippet:

respond_to do |format|
  format.html # index.html.erb
  format.xml  { render :xml => @posts }
end

I understand that if format is .html, it will give html response and if
format is .xml, it will give out xml response.

So this block “do |format| … end” is case switch like in C++. I have
difficulty to understand though in ruby, if the user passing .html, as a
sequential execution (notice in ruby this is not a case switch
statement), what prevent ruby from executing “format.xml { render :xml
=> @posts }” line?

To my feeling (though I know I am wrong for sure), these two lines will
always be executed no matter what kind of format is passed in:

  format.html # index.html.erb
  format.xml  { render :xml => @posts }

Please help.

Hi Warren,

Short answer:

The code inside the {}s from this line (“format.xml { render :xml =>
@posts }”) is only executed if the request wants an XML response. The
{}s
are common shorthand for passing a block/proc/lambda to a method call
and
an alternative to the do/end longhand.

Long answer:

The code you are reading, as far as I know, is something commonly found
in
Rails HTTP controllers. Your interpretation of the functionality is
correct: if the request wants HTML, render the HTML template. Otherwise,
if
it wants XML, render the “@posts” data as XML. But, this is NOT a Ruby
case/when/then/default statement. Instead, the respond_to method yields
a
format object to the block. Once inside the block, the code tells the
format object to perform the default action (render the HTML template)
if
the request wants an HTML response. Also, it tells the format object
that,
if the request wants an XML response, to execute the given lambda.

So, it seems the confusion is around understanding that the use of
blocks/procs/lambdas gives the respond_to method the ability to only
execute the correct rendering code given to the format object based on
what
type of response the request wants.

I hope that is helpful!

Hi, Ryan,

Thank you very much for your quick reply. I still have problem to
understand though.

Post the code snippet again here:

respond_to do |format|
  format.html # index.html.erb
  format.xml  { render :xml => @posts }
end

My understanding is: respond_to method will take this block as argument,
within respond_to method, it will call this block code, the
implementation of respond_to is:

module ActionController
module MimeResponds
module InstanceMethods

   def respond_to(*types, &block)
     raise ArgumentError, "respond_to takes either types or a

block, never both" unless types.any? ^ block
block ||= lambda { |responder| types.each { |type|
responder.send(type) } }
responder = Responder.new(self)
block.call(responder)
responder.respond
end

   class Responder
     # ...
   end

 end

end
end

Notice here “block.call(responder)”, I believe this “responder” is the
“format” parameter used in

do |format|
  format.html # index.html.erb
  format.xml  { render :xml => @posts }
end

But this doesn’t explain only one of format.html or format.xml is
executed. No matter what object is passed into this block as parameter
for “format”, these two statement will both be executed. Unless, ruby
implements something like:

do |format|
if format == “.html”
format.html
if format == “.xml”
format.xml {render :xml => @posts }

According to what you said: the respond_to method yields
a format object to the block. Once inside the block, the code tells the
format object to perform the default action (render the HTML template)
if the request wants an HTML response. Also, it tells the format object
that, if the request wants an XML response, to execute the given lambda.

I don’t see how the code “tells” the format object to perform the
default action or XML action.

On 14-01-03, 14:06, Warren Z. wrote:

I don’t see how the code “tells” the format object to perform the
default action or XML action.

The answer is a bit more about rails than ruby itself, but:

The format (Responder object) has methods that correspond to registered
mime types (xml, html, json, or you can add your own). None of these is
considered a “default” but the request.format is determined from either:

  • Accepts header (parsed by Rack I believe)
  • route-specific :format option in config/routes.rb
    (e.g. traditionally /:controller/:action/:id.:format )

What those methods do is:

  • If no block is passed (like format.html) then configure the
    renderer to process the template file that matches the name of the
    action, e.g. /views/posts/show.html.erb
  • If a block is passed, then configure the renderer to call the block
    for the response body.

The actual render call doesn’t happen until later. You could call it
manually in your method (render “posts/show”), but when you don’t then
the controller will see that it wasn’t called, and call the appropriate
responder method instead.

So, it’s a bit of rails magic, and nothing like a case statement.

Hi,

Why is it that important for you to understand how that code works,
instead
of just taking it as it is?

Respect,
Cezar

Cu respect,
Sirbu Nicolae-Cezar

*removed_emai[email protected] [email protected] | web
developer
| *+4 (0767) 508 109
NOTICE: This email and any file transmitted are confidential and/or
legally
privileged and intended only for the person(s) directly addressed. If
you
are not the intended recipient, any use, copying, transmission,
distribution, or other forms of dissemination is strictly prohibited. If
you have received this email in error, please notify the sender
immediately
and permanently delete the email and files, if any.

Both statements are interpreted sequentially as you would expect. But,
let’s dissect the second one:

format.xml { render :xml => @posts }

Here’s what we’re seeing: call the “xml” method on the “format” object
with
a single block parameter containing the code “render :xml => @posts

The magic here is that the “xml” method is passed a block; a Ruby Proc
object. http://rubydoc.info/stdlib/core/Proc

Since the method is receiving a block/Proc, it can hold onto it for use
later, such as if the Controller determines that the request asked for
an
XML response.

See below:

class Printer
attr_accessor :code

def initialize(&code)
@code = code
end

def print(string)
@code.call string
end
end

my_printer =
lambda do | string |
puts string
end

p = Printer.new &my_printer

puts p.code.inspect

p.print “Ryan”

In this code, I create a block called “my_printer” and pass it to a
Printer
object. But, the code inside the block doesn’t get executed until I call
the “print” method on my printer object.

Are things getting more clear?

Hi, Ryan,

It definitely gets clearer. Thank you very much for your time to answer
my entry level question. Really appreciated.

Warren

Andrew,

Thank you for your explanation. I am vaguely understand what you are
talking about.

This “magic” rails is doing is most difficult part for me. I found few
literacy explain rail “magic” well. I am reading “agile web development
with rail” and rails online documents. They both mentioned that for
example like

respond_to do |format|
  format.html # index.html.erb
  format.xml  { render :xml => @posts }
end

controller will respond to user either html or xml with the right format
provided by request url. That’s it. They don’t explain why these two
statements will not be executed sequentially or anything behind the
scene. I have big difficult time to understand since I am coming from a
c++, python world.

How can I accrue my knowledge on this “magic”? Any in depth
books/documents I can refer to?

Hey Andrew,

Thank you for your pointer. I will take a look at it tonight.

On 14-01-03, 15:48, Warren Z. wrote:

They don’t explain why these two
statements will not be executed sequentially or anything behind the
scene.

Both of those methods “format.html” and “format.xml” are called
sequentially (probably), but they don’t necessarily call the renderer
immediately at that time. I can think of a few ways without looking at
the source…

How can I accrue my knowledge on this “magic”? Any in depth
books/documents I can refer to?

I remember seeing this, it might be what you’re looking for:

http://rebuilding-rails.com/

First, it’s important to understand blocks/procs/lambdas/closures. These
let us pass blocks of code that don’t actually get executed until (or
unless) they’re needed. It’s one of the most fundamental and powerful
pieces of ruby.

The second part is metaprogramming. This can make code very complicated
to follow, and Rails uses it a lot.

Hi, Cezar,

Thank you for your suggestion. :slight_smile: But for me, I don’t want to be a
novice but an expert. So I guess it will take efforts to understand
things inside out. I learn hard lesson on “just taking it as it is”.

For example, I take TCP/IP 3-way hands-shake as it is but eventually it
comes out to bite me when I am not solid understanding the piggy banking
of sequence number while I am using wireshark/tcpdump. Anyway, when it
comes real life, if you want to be an ace on anything, knowing things
inside out is very important.

“Sîrbu Nicolae-Cezar” <[email protected] wrote in post
#1132171:

Hi,

Why is it that important for you to understand how that code works,
instead
of just taking it as it is?

Respect,
Cezar

Cu respect,
Sirbu Nicolae-Cezar

*[email protected] [email protected] | web
developer
| *+4 (0767) 508 109
NOTICE: This email and any file transmitted are confidential and/or
legally
privileged and intended only for the person(s) directly addressed. If
you
are not the intended recipient, any use, copying, transmission,
distribution, or other forms of dissemination is strictly prohibited. If
you have received this email in error, please notify the sender
immediately
and permanently delete the email and files, if any.

On Jan 3, 2014, at 16:26, Srbu Nicolae-Cezar
[email protected] wrote:

Why is it that important for you to understand how that code works, instead of
just taking it as it is?

Really?

Dear Warren,

It’s simpler than it looks. Trust me!

When I pass a block to respond_to it just “register” each renderer for
later rendering.
So, when I say:
format.xml { render :xml => @posts }
The block is not run imediatly. It’s just “saved” for later.

Can you understand the code bellow?
I did this humble piece of code just for you to see clearly why they
are not run in sequence (as this is your main doubt).

#!/usr/bin/env ruby

class ExplainRespondTo

def request(render_format)
@render_format = render_format
index
end

def index
@posts = [“1st post”, “2nd post”]
respond_to do |format|
format.html { render :html_tag => @posts } # I’ve put a “tag” here

format.xml { render :xml_tag => @posts } # …Just to not
misundertand with the format.xml
end
end

def respond_to
renderer = Renderer.new
yield renderer
# When I yield render to the block received by respond_to
# format (block variable) will be pointing to the renderer

# Now that I have yield that block on this, I call #render
# and #render will actually "render" (or actually choose how to 

render)
renderer.actually_render(@render_format)
end

def render(options)
format, contents = options.first
# Here I could delegate the actual rendering to specific class
# based on the format requested
# in this example, I just tag the content
contents.each do |content|
puts “<#{format}>#{content}</#{format}>”
end
end
end

class Renderer
attr_reader :renderers

def initialize
@renderers = Hash.new
end

This will be called as format.html inside respond_to block

def html(&block)
# Look, how we can save the block for later usage
@renderers[:html] = block
end

This will be called as format.xml inside respond_to block

def xml(&block)
@renderers[:xml] = block
end

def actually_render(render_format)
# This should return the block saved with format.html or format.xml
renderer_proc = @renderers[render_format]
# Now we just call it
renderer_proc.call
end
end

e = ExplainRespondTo.new
e.request(:html)
e.request(:xml)
e.request(:json) # This is not defined in our example. So, error!

Abinoam Jr.

Hi, Abinoam,

Your example serves me very well. I now have profound understanding on
block/proc/lambda. Thanks.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs