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