Introduction to Presenters: Simplifying View Logic in Ruby

In this article, we’ll explore the concept of Presenters and how they can simplify view logic in Ruby on Rails, especially when conditionals are overused.

A Presenter acts as an intermediary between the Model and the View, encapsulating the formatting and manipulation of the data for proper display in the interface.

Let’s see a simple example of how to use a Presenter.

Suppose we have a User model with a name attribute. Instead of dealing directly with the User object in the view, we can create a Presenter called UserPresenter:

1
2
3
4
5
6
7
8
9
class UserPresenter
  def initialize(user)
    @user = user
  end

  def formatted_name
    @user.name.upcase
  end
end

In the view, we can use the UserPresenter instead of the User directly:

1
<%= @user_presenter.formatted_name %>

This way, we isolate the name formatting logic in the Presenter, keeping the view free from any data manipulation.

Moreover, Presenters can also be helpful in grouping multiple related objects. For example, on a page that displays user information and their orders, we can create a UserOrdersPresenter to encapsulate the order-related information.

Let’s add the example of the UserOrdersPresenter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class UserOrdersPresenter
  def initialize(user)
    @user = user
  end

  def formatted_name
    @user.name.upcase
  end

  def recent_orders
    @user.orders.where('created_at >= ?', 1.week.ago)
  end

  def total_orders
    @user.orders.count
  end
end

In the above example, we created a UserOrdersPresenter that encapsulates information related to the user’s orders. In addition to the formatted_name method we saw earlier, we added two new methods: recent_orders and total_orders.

In the view, we can use the UserOrdersPresenter to display the relevant information:

1
2
3
4
5
6
7
8
<h1><%= @user_orders_presenter.formatted_name %></h1>

<p>Total orders: <%= @user_orders_presenter.total_orders %></p>

<h2>Recent orders:</h2>
<% @user_orders_presenter.recent_orders.each do |order| %>
  <p><%= order.name %> - <%= order.total %></p>
<% end %>

This way, we can simplify the view by accessing the methods of the UserOrdersPresenter instead of dealing directly with the User model and its relationships.

Presenters allow us to group related data and logic into a single object, making the view more cohesive and easier to understand. They are a great option when we need to present complex information in a clear and organized manner.

Why use a Presenter instead of putting everything in the Model?

In Ruby on Rails, utilizing a Presenter, such as the UserPresenter, instead of placing all the logic directly in the Model, aims to maintain Separation of Concerns (SoC) and promote cleaner and more organized code.

The Model in Ruby on Rails represents the business layer of the application, responsible for handling business logic, validations, data persistence, and interactions with the database. Including presentation logic in the Model can compromise code cohesion and readability.

By using a Presenter, like the UserPresenter, you create a separate class dedicated solely to the presentation logic of the data.

This approach offers several benefits, including:

Separation of concerns: The Model is responsible for business aspects, while the Presenter takes care of presentation logic. This makes the code more modular, facilitating maintenance and understanding.

Code reusability: With a dedicated Presenter, you can reuse the same presentation logic in different views or contexts without having to replicate the code in each place.

Ease of testing: Presenters can be tested in isolation without dealing with the complexity of the database or infrastructure. This makes tests simpler and faster to execute.

Improved readability: By using Presenters, you can keep views cleaner and focused solely on data presentation, without the need to add conditional logic or complex data manipulation directly in the views.

In summary, utilizing Presenters help maintain more organized application architecture, following the principles of Single Responsibility and Separation of Concerns. It facilitates maintenance, improves code readability, and promotes a more modular approach to handling data presentation logic.