pres

Gem Version Build Status

What?

A Presenter is a rendering class. The pres gem is a lightweight presenter solution with no runtime gem dependencies.

Pres provides the following:

  1. Pres::Presenter is a presenter base class.
  2. present is a convenience method to create presenters.
  3. Pres::ViewDelegation is a delegation module, included in the Presenter base class.

How and Why?

Presenters are an alternative to an unorganized mass of helper methods in your ruby application.

Rails' ViewContext contains convenience methods for views, such as link_to, url_for, truncate, number_to_currency, etc. It's the thing that makes Rails views nice to work with.

Other presenter libraries mix in all the methods from the Rails ViewContext to make it easy to call those methods in the Presenter class. pres instead injects the ViewContext as a dependency into the Presenter class, and uses method_missing to delegate to ViewContext methods. pres produces small classes that contain and delegate to an existing object that handles server-side rendering.

Install

Add it to your Gemfile:

gem "pres"

Setup with Rails

Include the Pres::Presents module:

class ApplicationController
  include Pres::Presents
end

Usage

The quickest way to get started is to use the Pres::Presenter base class.

Create a presenter class in app/presenters:

class DogePresenter < Pres::Presenter
  # explicitly delegate methods to the model
  delegate :name, to: :object

  def know_your_meme_link
    # Rails helpers are available via the view context
    link_to "Know your meme", "http://knowyourmeme.com/memes/doge"
  end

  def name_header
    # object is the Doge used to initialize the presenter
    (:h1, object.name)  
  end

  def signed_in_status
    # controller methods are accessible via the view context
    if signed_in?
      "Signed in"
    else
      "Signed out"
    end
  end  
end

Wrap your model object in a presenter in your controller with present:

class DogesController
  def show
    @doge = present(Doge.find(params[:id]))
  end
end

Use the presenter object in doges/show.haml.html

= @doge.name_header
.status
  You are #{@doge.signed_in_status}
.links
  .meme-link= @doge.know_your_meme_link

Collections

Create a presenter class in app/presenters:

class DogePresenter < Pres::Presenter
  # same as above
end

Build an array of presenters in your controller with present:

class DogesController
  def index
    @doges = present(Doge.all)
  end
end

Use the presenter objects in doges/index.haml.html

This renders "doges/_doge.html.haml" for each item, following rails' usual conventions:

= render @doges

Or use each:

- @doges.each do |doge|
  = doge.name_header

Present with options

Pass additional options to a Presenter as a hash. The presenter class exposes the options hash as a method:

user = User.new

# These two lines are the same:
# 1. explicit
presenter = UserPresenter.new(user, view_context, something: 123)
# 2. using #present
presenter = present(user, something: 123)
=> #<UserPresenter object: #<User> ...>

presenter.options[:something]
=> 123

Use a custom presenter class

By default, a presenter class corresponding to the model class name is constructed in present. For example, if you present a User, a UserPresenter class is constructed. An error is raised if the presenter class does not exist. To specify a different class, use the presenter: key.

user = User.new
present(user, presenter: UserEditPresenter, cool: true)
=> #<UserEditPresenter object: #<User> ...>

You may also define a custom presenter class on any class you want to present:

class User
  def presenter_class
    MyPresenter
  end
end

present(User.new)
# => #<MyPresenter object: #<User> ...>

Create presenters in views

You can create a presenter in your view code. First make the present method visible to your views by declaring it a helper_method:

class ApplicationController
  include Pres::Presents
  helper_method :present
end

Then you can create presenters inline in a view:

- present(@doge) do |doge|
  = doge.name_header

You can override methods as usual:

class DogePresenter < Pres::Presenter
  include Shared

  def truncated_name(length: 60)
    # override
    super(length: length)
  end
end

Presenters can create other presenters

Presenters can wrap child objects in presenters of their own.

class DogePresenter < Pres::Presenter
  def cats
    present(object.cats)
  end  
end
= render doge.cats

Using mixins instead of inheritance

If you don't want to inherit from Pres::Presenter, you can include Pres::ViewDelegation and implement your own initializer (so the present helper will work).

This technique is useful if you would like to delegate all methods in a model by default, instead of whitelisting methods on the wrapped model explicitly. Delegating everything to the model by default is how the draper gem works, for example.

class DogePresenter < DelegateClass(Doge)
  include Pres::ViewDelegation

  def initialize(object, view_context, options = {})
    super(object)
    @view_context = view_context
  end
= doge.name

see DelegateClass

Updating to version 1.0

Modules and classes have been moved into the Pres namespace with version 1.0. Change your code references to Pres::Presents and Pres::Presenter.

References