A presenter pattern helps to separate business logic (model) and presentation logic (presenter)
A presenter is meant to be used from the views. If there are methods that need to be used from services or commands, these methods should be placed in the model, or another class instead.
We chose Keynote gem to implement presenters:
# frozen_string_literal: true
source 'https://rubygems.org'
gem 'keynote'
Considered alternatives:
It decorates a Rails model, that should be passed when creating the presenter.
# frozen_string_literal: true
class UserPresenter < Keynote::Presenter
presents :user # (1)
def full_name
[user.title, user.first_name, user.middle_name, user.last_name].compact.join(' ')
end
def city_state_zip
[user.city, user.state, user.zip_code].compact.join(', ')
end
def formatted_phone
number_to_phone(user.phone, area_code: true) # (2)
end
end
- (1) Decorates a user
- (2) Rails view helpers are available on the presenters
This presenter is not decorating any model or class. It can be understood as a collection of related and encapsulated methods to be used in the views, with the ability to create HTML from ruby code.
# frozen_string_literal: true
class NavbarPresenter < Keynote::Presenter
def section_for(step, steps: Array(step), url: step)
build_html do
li class: pill_class(steps) do
link_to(url) do
build_html do
span class: 'nav-steps__step-number'
span t(".#{step}"), class: 'nav-steps__step-title'
span class: 'icon-check'
end
end
end
end
end
def active?(section)
# ...
end
private
def pill_class(sections)
# ...
end
end
Use the present
helper method (provided by Keynote) to create presenters.
<div class="main-content">
<ul class="nav nav-steps">
<%= present(:navbar).section_for(:personal_info) %>
...
</ul>
<div class="main-content__user">
<%= present(@user).full_name %>
</div>
</div>
k
method alias insted of present
, although is now discouraged ️️
Yes, this is the recommended approach:
<%= present(@user).full_name %>
<%= present(@user).formatted_phone %>