Cleaner View Rendering in Rails

Published February 11, 2015

We've been using the "Presenter pattern" atClearSight for some time now. Since everyone seems to have gotten used to the idea of only using one instance variable per controller action, we decided to take it one step further.

We avoid convenience methods such as current_user in the view and provide all data through the presenter in simple data structures rather than full ActiveRecord objects. It makes mocking and testing much easier if there isn't a reliance on exterior state or objects.

We also want to avoid setting any instance variables at all and instead provide a local variable via the locals hash.

Here's how we've accomplished it through POROs (plain old Ruby objects). We're pretty happy with this and excited about how clean it makes our code.

The Controller

To clean up the render action (no ugly locals hash), we provide a class method called build that returns the right hash:

class MyController < ApplicationController
        def index
          render :index, MyIndexPresenter.build({
            people: User.all,
            current_user: current_user,
          })
        end
      end
      

The Presenter

Each presenter is something like this:

class MyIndexPresenter < AppPresenter
        def people
          properties[:people].map do |person|
            {
              profile_url: "/user/#{person.id}",
              display_name: "#{person.first_name} #{person.last_name}",
            }
          end
        end

        def current_user_name
          properties[:current_user].display_name
        end
      end
      

Simple, direct, to the point. It exposes some convenient methods we can use in our view, referring to the properties hash which is set on initialization. It explicitly provides hashes for any collections (like this group of users above), giving the view only the data it needs in the format it needs.

The View

We use Slim, and our view might look something like this:

header title = presenter.current_user_name div ul - presenter.people.each do |person| li = link_to person[:display_name], person[:profile_url] `}

The presenter local variable has exposed two methods to us, so we use those. In most cases, these are hashes or strings or integers -- no complex objects at all.

The AppPresenter

The AppPresenter is a PORO class that gives you just a few methods.

class AppPresenter
        def self.build(args={})
          new(args).render_options
        end

        def initialize(args={})
          @properties = args
        end

        def properties
          @properties ||= {}
        end

        def render_options
          { locals: { presenter: self } }
        end
      end
      

The build method just conveniently callsnew(args).render_options for us, so we can render cleanly in our controller (see above). It hangs onto properties that you pass into it, and the render_options method passes back to Rails's built-in render method the option hash it expects; mainly the locals: directive which tells it to expose the presenter to the view.

What do you think of this pattern? Pros, cons? Rails developers never have opinions about this stuff, right? ;-) Let me know on Twitter!