pres
What?
A Presenter is a rendering class that wraps a model. Presenters are an alternative to an unorganized mass of helper methods in your Rails application.
The pres gem is a lightweight presenter solution.
How and Why?
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. This causes method
bloat. pres instead injects the ViewContext as a dependency into the
Presenter class, and uses method_missing to delegate to ViewContext methods.
So pres produces small classes that contain and delegate to an existing object
that handles server-side rendering.
Presenter vs. Decorator
Decorators add methods to a model. pres encourages you to whitelist model methods
via delegation.
Install
Add it to your Gemfile:
gem "pres"
Include the Presents module:
class ApplicationController
include Presents
end
Add app/presenters to your application's autoload paths in application.rb:
config.autoload_paths += %W( #{ config.root }/app/presenters )
Example Usage
Create a presenter class in app/presenters:
class DogePresenter < 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
content_tag(: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 your controller with present:
class DogesController
def show
end
private
helper_method :doge
def doge
@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
Collection Example
Create a presenter class in app/presenters:
class DogePresenter < Presenter
# same as above
end
Wrap your model object in your controller with present:
class DogesController
def index
end
private
helper_method :doges
def doges
@doges ||= present(Doge.all)
end
end
Use the presenter objects in doges/index.haml.html
This renders "doges/_doge.html.haml" for each item, as usual:
= render @doges
Or use each:
- doges.each do |doge|
= doge.name_header
Present with options
Use keyword arguements (or an options hash) to pass additional options to a Presenter:
class UserPresenter
def initialize(object, view_context, cool: false)
super
@cool = cool
end
end
user = User.new
present(user, cool: true)
=> #<UserPresenter object: #<User> ...>
Render a custom Presenter
By default, a presenter class corresponding to the model class name is
constructed. To specify a different class, pass the presenter: key, followed
by any additional arguments:
user = User.new
present(user, presenter: NiceUserPresenter, cool: true)
=> #<NiceUserPresenter object: #<User> ...>
Render a collection
present(User.first(5))
=> [#<UserPresenter object: #<User> ...>]
Creating presenters in views
You should create presenters in your controllers. If you would like to create
a presenter in your view code, make the present method visible to your views:
class ApplicationController
include Presents
helper_method :present
end
More Goodness
Presenters are objects
You can mix in common methods.
module Shared
def truncated_name(length: 40)
truncate object.name, length: length
end
end
class DogePresenter < Presenter
include Shared
end
You can override methods without resorting to convoluted method names:
class DogePresenter < Presenter
include Shared
def truncated_name(length: 60)
# whoa this one is different!
super(length: length)
end
end
Presenters can create other presenters
class DogePresenter < Presenter
def cats
present(object.cats)
end
end
= render doge.cats