Use helpers in your Rails PDF templates

The Rails PDF plugin is a dandy little thing. It wraps the PDF::Writer library for Ruby, allowing PDF templates to be defined like any other Rails view (with a .rpdf extension, of course).

One thing it doesn’t do is allow access to the parent controller’s helper from within a PDF template. It’s easy enough to patch the plugin to accomplish this, though.

First, open up vendor/plugins/railspdf/lib/railspdf.rb and take a gander. This is the section we’re interested in:

...
class PDFRender < ActionView::Base
  def initialize(action_view)
    @action_view = action_view
  end
  ...

PDF templates get rendered in the context of this class. All we need to do to gain access to the controller's helper here is include the helper from inside the class initializer, like so:

...
class PDFRender < ActionView::Base
  def initialize(action_view)
    @action_view = action_view
    prefix = action_view.controller.class.to_s.gsub(/Controller/, '')
    self.class.send(:include, "#{prefix}Helper".constantize)
  end
  ...

We first calculate the prefix of the helper based on the class of the controller assigned to the view. Then we include the helper in the current class by constantizing the resulting string.

We're almost done. Since ApplicationHelper should be available to every controller and helper in the system, let's include it here for good measure:

...
class PDFRender < ActionView::Base
  include ApplicationHelper
  
  def initialize(action_view)
    @action_view = action_view
    prefix = action_view.controller.class.to_s.gsub(/Controller/, '')
    self.class.send(:include, "#{prefix}Helper".constantize)
  end
  ...

And we're done! Piece of cake. Now we can reference methods from inside our controller's helper. We can, of course, modify this hack somewhat so that the same helper (something like "PdfHelper" maybe) is included for all of our templates. This enables us to isolate our PDF-specific helper methods in a single module.

And before you ask, yes, it's nasty to be modifying the plugin directly. There is a way to inject this patch into the PDFRender class without touching anything in the plugin... but I've gotta leave something for you to figure out! 🙂