Rendering a different format in the implementation of a renderer

Yehuda has a nice article on implementing a custom renderer for PDF at

  http://www.engineyard.com/blog/2010/render-options-in-rails-3/

I've tried to follow the example and I'm not sure I see how to get it to
work. If I understand things correctly, the format(s) usable in this
render call

  respond_with(@resource) do |format|
    format.xyz { render :xyz => ... }
  end

are set to xyz. Thus templates (for actions, partials, and layouts) are
looked up based on only this format, e.g. show.xyz.erb.

This is the correct behavior, of course, but there are legitimate
reasons for wanting to override it. For instance, implementing one
renderer in terms of another. In my particular case, I'm trying to get
HTML rendered in order to convert it to PDF.

As I can see no other clean way, I wish ActionView::LookupContext had a
method #with_format(format) { ... } for overriding the format for the
duration of the block. Such a renderer could then look like this

  ActionController.add_renderer(:xyz) do |template, options|
    lookup_context.with_format(:html) do
      html = render(template, options)
      convert_html_to_xyz(html)
     end
  end

Or, even easier, :format could be an option to #render.

Michael

You can do something like this in Rails 3:

render :partial => “foo.html” if you’re in an XML template et al.

Yehuda Katz
Architect | Engine Yard
(ph) 718.877.1325

Yes, I know, but that doesn't help in this particular case. I'm not just
rendering a partial, I want the whole view. Now, render :template =>
"foo.html" does work for the template itself, but that doesn't change
that render (LookupContext, really) still uses the original, "enclosing"
format, to look for a layout template. Say I have

  respond_with(@resource) do |format|
    format.pdf { render :pdf => 'show, :layout => print }
  end

and the renderer does something like this

  ActionController.add_renderer :pdf do |template, options|
    filename = options.delete(:filename)
    options = options.merge(:template => "#{template}.html")
    html = render_to_string(options)
    send_data(convert_html_to_pdf(html))
  end

Here render_to_string finds and uses the HTML template. But as there is
no print.pdf.erb, it doesn't wrap a layout around the content.

Now, then I just rename print.html.erb to print.pdf.erb. That does work,
but only as long as I don't need to include any further partials in the
layout. For those partials, LookupContext enforces that they must have
format PDF. As no such partials exist, and as I don't want to define
them, things blow up because of missing templates.

As I said, I think it would be useful to have a way to force the
rendered format. However, I don't yet understand the architecture well
enough to be entirely sure that this is a good idea.

Michael