BinaryWebPark

The Presenter Pattern With POROs for Rails Applications

January 03, 2017

The Presenter Pattern With POROs for Rails Applications

When I first started making Rails applications, I ended up with views as shown in the below User Report Page code block after a while.

Ruby on Rails picture from photodune

%h1 User Report Page

- @user.each do |user|
  - if user.exists?
    %p User exists
  - else
    %p User does not exist

I would check whether a user exists or not. If a user existed, I would display certain HTML markup. If you start trying to scale this type of logic over multiple lines, it becomes quite a mess to maintain.

Then I found out about the presenter pattern with POROs (Plain Old Ruby Objects).

What is a presenter pattern and why use it?

A presenter pattern takes your logic out of the views. It helps make your Rails views more maintainable.

Imagine if we started doing something more complicated such as displaying a different message if a user had an attached image file.

%h1 User Report Page

- @user.each do |user|
  - if user.exists? && user.has_image?
    %p User exists
  - elsif user.exists? && !user.has_image?
    %p User exists with no image
  - else
    %p User does not exist

Hopefully, you are starting to see how messy the above can be to maintain. If we used a presenter pattern, our view code might look something like this:

%h1 User Report Page

- present(@user) do |user|
  %p= user.user_report

Notice how much cleaner the view looks! No conditional logic is needed in the view. Because of this, it will be easier to maintain.

Step 1 – Make a BasePresenter class

As a first step, let’s make a BasePresenter class that will house common behavior.

class BasePresenter < SimpleDelegator
  include ActionView::Helpers

  def initialize(model, view)
    @model, @view = model, view
    super(@model)
  end

  def h
    @view
  end
end

Step 2 - Make a Presenter

Next, we make a presenter class that inherits from BasePresenter to encapsulate our “view” logic. In this example, I define a method called userexistsmessage.

This method returns a message with some HTML markup regarding the existence of a User object.

class UserPresenter < BasePresenter
  def user_exists_message
    if @model.user_exists?
      h.content_tag(:h3, "User does not exist")
    else
      h.content_tag(:h3, "User exists")
    end
  end

end

Step 3 - Testing Your Presenter

Finally, if you want to add tests in RSpec, you need to instantiate a view instance of ActionController::Base.new.viewcontext_.

require 'spec_helper'

RSpec.describe UserPresenter do
  let(:view) { ActionController::Base.new.view_context }
  let(:user) { FactoryGirl.create(:user) }

  describe '#user_exists_message' do
    let(:user_presenter) { UserPresenter.new(user, view) }

    it "should say User exists" do
      expect(user_presenter.user_exists_message).to include("User exists")
    end
  end
end

Summary

Use the Ruby language to create presenter patterns to simplify code in your views. The presenter pattern will help you maintain your views over the life of a code base.