Rails Presenters
This article will show you how to use the Rails presenter model.
Published on:June 22, 2015
Introduction
In this article we will show you how to use the presenter model in Ruby on Rails. This article is also the first article to contain a screencast. Feel free to leave feedback on the video below. There is no audio with this first screencast. I need a good quiet setup to do audio. If you WANT audio, please be sure to leave a comment so I know what you guys are looking for. Otherwise I will play around with audio in the near future.
Introducing Presenters
Wondering what presenters are? Presenters are simple classes that sit between the model and the view and provide provide a nice, clean, object oriented way of handling complex display logic. For example, in this article, we will cover a simple scenario. You have a model called Post
that has a column called published_at
which contains a date. We use certain display logic for the published_at
column that is determined by whether it's null or not. If the date is null, you want to consider it to still be in 'draft' form and not ready for public use. However, if published_at
isn't null, you want to display that date in a certain way. Presenters are perfect for this.
Rails Application Setup
The first thing we need to do is create the Post model mentioned earlier. To do this, first, either create a new Rails application or open an existing one. Then open a terminal and run the command listed below to create the post model.
rails g model post title:string body:text published_at:timestamp
Now run a rake db:migrate
to migrate the database.
rake db:migrate
Next, we need to create a controller. For this example we will have a posts controller with an index action. Run the command below to make the controller now.
rails g controller posts index
Now let's add some code to the controller to query the database and return all of the posts. Modify the PostsController
so that it looks like the code listed below.
class PostsController < ApplicationController
def index
@posts = Post.order(published_at: :desc)
end
end
Now that we are done with the controller, we need to make sure we set a site root so that the page loads when we start the Rails server. Modify your routes.rb
file to look like the code listed below.
Rails.application.routes.draw do
get 'posts/index'
root to: "posts#index"
end
Next, create a folder called presenters
inside your app folder. The presenters folder will store your presenters.
mkdir app/presenters
Next, we need to create a new class called BasePresenter
. The BasePresenter class contains some code that will help us tie the presenter to the model and the view. Create a new file called base_presenter.rb
inside the app/presenters
and add in the code listed below.
class BasePresenter < SimpleDelegator
def initialize(model, view)
@model, @view = model, view
super(@model)
end
def h
@view
end
end
Now we can create a presenter for the post model. Create a new file called post_presenter.rb
inside the app/presenters
folder and add in the code listed below.
class PostPresenter < BasePresenter
def title
@model.title.titleize
end
def publication_status
if @model.published_at?
h.time_ago_in_words(@model.published_at)
else
'Draft'
end
end
end
You'll notice that there are 2 methods. The first, title
, takes the title and formats it to Proper Case. The second, publication_status
, takes the published_at field and formats the date. If published_at is null it returns 'Draft' instead. That's the idea behind presenters, they allow you to pull the display logic out of the model and place it in the presenter, making things much cleaner.
Next we will create a helper method called present
. The present
method makes it easy for us to 'present' our presenter's methods to the view. Open up app/helpers/application_helper.rb
and modify it so that it looks like the code listed below.
module ApplicationHelper
def present(model, presenter_class=nil)
klass = presenter_class || "#{model.class}Presenter".constantize
presenter = klass.new(model, self)
yield(presenter) if block_given?
end
end
Great, now finally, let's create our view. Open up the index
view for your PostsController
and modify it so that it looks like the code listed below.
<h1>Posts</h1>
<table>
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>Body</th>
<th>Published On</th>
</tr>
</thead>
<tbody>
<%- @posts.each do |p| %>
<% present(p) do |post| %>
<tr>
<td><%= post.id %></td>
<td><%= post.title %></td>
<td><%= post.body %></td>
<td><%= post.publication_status %></td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
You'll notice the present method being used here. The present method automatically makes those 2 methods we created available to us in addition to our model's existing attributes. Pretty easy to use, right?
Now, before we start our Rails server, let's add some seed data to our database so that we have something to work with. Open up your db/seeds.rb
file and modify it so that it looks like the code listed below.
Post.delete_all
Post.create!(id: 1, title: "Welcome to my site", body: "Welcome! This is an example post", published_at: Time.now - 2.hours)
Post.create!(id: 2, title: "My Second Post", body: "Welcome! This is another example post", published_at: Time.now - 1.hours)
Post.create!(id: 3, title: "Welcome to my site", body: "My third post", published_at: nil)
Now run rake db:seed
to seed the database.
rake db:seed
Now if you start a rails server
and navigate to your development environment's web page, you'll see that the titles are properly formatted along with the dates. That's all there is to it! Presenters are a very powerful tool that can help you make nice, clean code. It's well worth using them in your Rails applications.
Credit for the original code goes to nithinbekal.com, who's code helped reduce the amount of time it took to get this article out the door. Look for more videos and articles coming soon! Please leave feedback listed below. Please note that I have only a limited amount of time to answer questions, so if you have a technical question, the best place to ask is stack overflow (but try and link to your Stack Overflow post from here so we can find it.)
Thanks for reading!