pres
What?
A Presenter is a rendering class. The pres
gem is a lightweight presenter
solution with no runtime gem dependencies.
Pres
provides the following:
Pres::Presenter
is a presenter base class.present
is a convenience method to create presenters.Pres::ViewDelegation
is a delegation module, included in thePresenter
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
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 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
.