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.

···

--
Posted via http://www.ruby-forum.com/.

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!

···

On Fri, Jan 3, 2014 at 1:19 PM, Warren Zhang <lists@ruby-forum.com> wrote:

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.

--
Posted via http://www.ruby-forum.com/\.

--
Ryan Cook
720.319.7660

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.

···

--
Posted via http://www.ruby-forum.com/.

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?

···

--
Posted via http://www.ruby-forum.com/.

Hi, Ryan,

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

Warren

···

--
Posted via http://www.ruby-forum.com/.

Hey Andrew,

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

···

--
Posted via http://www.ruby-forum.com/.

Hi, Abinoam,

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

···

--
Posted via http://www.ruby-forum.com/.

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.

···

On 14-01-03, 14:06, Warren Zhang wrote:

I don't see how the code "tells" the format object to perform the
default action or XML action.

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:

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.

···

On 14-01-03, 15:48, Warren Zhang wrote:

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. Class: Proc — Documentation for core (3.0.2)

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?

···

On Fri, Jan 3, 2014 at 4:48 PM, Warren Zhang <lists@ruby-forum.com> wrote:

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?

--
Posted via http://www.ruby-forum.com/\.

--
Ryan Cook
720.319.7660

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.

···

On Fri, Jan 3, 2014 at 7:06 PM, Warren Zhang <lists@ruby-forum.com> wrote:

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.

--
Posted via http://www.ruby-forum.com/\.

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*

*sirbunicolaecezar@gmail.com <sirbunicolaecezar@gmail.com> | 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 Sat, Jan 4, 2014 at 1:48 AM, Warren Zhang <lists@ruby-forum.com> wrote:

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?

--
Posted via http://www.ruby-forum.com/\.

Really?

···

On Jan 3, 2014, at 16:26, Sîrbu Nicolae-Cezar <sirbunicolaecezar@gmail.com> wrote:

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

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" <sirbunicolaecezar@gmail.com 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*

*sirbunicolaecezar@gmail.com <sirbunicolaecezar@gmail.com> | 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.

--
Posted via http://www.ruby-forum.com/\.