Sharing HTML and PDF code with the facade pattern

In the system I wrote for my former company, several pages needed to be available in both HTML and PDF formats.  As usual, there was a tradeoff to consider:

If we exported the HTML document directly to a PDF using wicked_pdf, then there was less code to write, but the resulting PDF wouldn’t look as good.

If we wrote the PDF code separately from the HTML using Prawn, then the PDFs looked nicer, but then we had twice as much code to maintain.

We went the latter route, and ended up with a lot of code that felt redundant:

  # in HTML view
  - if @order.items.active.any?
    h3 Active items
    table
        tr
          th Item SKU
          th Item quantity
      - @order.items.active.each do |item|
        tr
          td = item.sku
          td = item.quantity


  # in Prawn PDF
  if @order.items.active.any?
    text Active items
    table [["Item SKU", "Item quantity"] + @order.items.active.map do |item|
      [
        item.sku,
        item.quantity
      ]
    end
  end

Some of this duplication can be mitigated, though.  We’ll do this using the Facade Pattern:

class ReportFacade
  def initialize(order)
    @order = order
  end

  def item_section_header
    "Active Items"
  end

  def show_items?
    items.any?
  end

  def items
    @order.items.active
  end
end

This centralizes the logic for:

1. If the section should be shown at all: @order.items.active.any?
2. What items should be shown: @order.items.active
3. What the section header text should be: "Active Items"

Now, in our controller, we switch

  def show
    @order = Order.find(params[:id])
  end

To:

  def show
    order = Order.find(params[:id])
    @report_facade = ReportFacade.new(order)
  end

And access @report_facade in our views.

  # in HTML view
  - if @report_facade.show_items?
    h3 = item_section_header
    table
        tr
          th Item SKU
          th Item quantity
      - @report_facade.items.each do |item|
        tr
          td = item.sku
          td = item.quantity

  # in Prawn PDF

  if @report_facade.show_items?
    text item_section_header
    table [["Item SKU", "Item quantity"] + @report_facade.items.map do |item|
      [
        item.sku,
        item.quantity
      ]
    end
  end

The advantage here is that it is harder for these two formats to get out of sync with one another. Changing the header in the ReportFacade object will change it in both the HTML and the PDF versions.

The disadvantage is that it makes for even more code overall, and when trying to understand the HTML or PDF code, you have to constantly reference the ReportFacade class.

It’s not so clear to me whether this is an overall win or not. What do you think? Is this worth doing?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s