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/.