Why do we need design patterns?
The problem is that Rails architecture, Model-View-Controller, gives you a basic structure to put your code in.
But this isn’t enough.
Your views grow large & full of logic when their goal is to present information.
Your controllers hold details beyond what’s necessary for the controller to do its essential work.
What’s the solution?
We have created two solutions to solve these problems, in the form of design patterns.
- The presenter pattern
- The service object pattern
Not everyone agrees on how to exactly implement them, but I’ll give you the version that works for me.
Let’s explore these patterns!
How to Use Presenters in Rails
Views are for presentation, that means HTML, CSS, & ERB (Embedded Ruby).
There should be no ActiveRecord
queries in views.
And most logic should be left out if you want your views to be as clean & easy to work with as possible.
By “logic” I mean making decisions with if statements & ternary operators
The question now is…
How?
Your first tool to handle logic in views is to use helpers.
Helpers are great whenever you have a global formatting method that you use in many views.
For example:
Rendering Markdown, showing dates in a specific format, removing specific words from text, etc.
Like this:
module DateHelper def date_as_month_and_year(date) date.strftime("%B %Y") end end
You can save this code under the app/helpers
folder & the date_helper.rb
file.
Here’s a tip:
Always pass input into helper methods via arguments, never rely on instance variables.
This will save you a lot of trouble.
Helper methods have limitations, especially if you use them for every formatting need in your views.
They tend to build up & lack any kind of organization.
Solution coming up!
Replace Complex Conditionals & Formatting Methods With Presenter Object
Let’s say you have a view like this one:
<p> Post title: <%= post.title.gsub("forbidden word", "") %> <%= link_to "Read post", post, class: "w-75 p-3 text-#{post.draft? ? "orange" : "green"} border-#{post.draft? ? "orange" : "green"}" %> </p>
Pretty short view, right?
But it feels very complex with these ternary operators & the duplicated code.
Not good!
Let’s create a presenter class to solve this.
Here’s how:
class PostPresenter def initialize(post) @post = post end def title_without_forbidden_words @post.title.gsub("forbidden word", "") end def css_color @post.draft? ? "orange" : "green" end end
Save this under app/presenters/post_presenter.rb
, create the presenters
folder if you don’t have it.
Now you can change the view.
Like this:
<% presenter = PostPresenter.new(post) %> <p> Post title: <%= presenter.title_without_forbidden_words %> <%= link_to "Read post", post, class: "w-75 p-3 text-#{presenter.css_color} border-#{presenter.css_color}" %> </p>
There you go!
- We removed all the logic from the view
- We added meaningful names for the formatting & decision-making operations
- We can reuse this class in other views, without duplicating code
That’s how you use presenters in Rails 🙂
How to Use Service Objects
Your controllers should only tell others what to do, they shouldn’t have any knowledge about how to send a Tweet, charge a customer or generate PDF files.
These operations should be delegated to a service object.
A service object, as I define it, is a Ruby module which encapsulates the logic for completing an action.
Example:
module TwitterService def self.send_welcome_message(twitter_handle) client.update("@#{twitter_handle} welcome to 'Oranges & Apples', we hope you enjoy our juicy fruit!") end def self.client @client ||= Twitter::REST::Client.new do |config| config.consumer_key = "..." config.consumer_secret = "..." config.access_token = "..." config.access_token_secret = "..." end end end
The convention is to save this under an app/services
folder, and a file like twitter_service.rb
.
How do you use this?
Because Rails autoloads everything from app/
, this code will be available in your controllers.
Example:
class UsersController def create # ... TwitterService.send_welcome_message(user.twitter_handle) end end
That’s the service object pattern in action.
Summary
You have learned two helpful Rails patterns that will help you improve your project’s code quality when used wisely!
It’s your turn now to apply them 🙂
Thanks for reading.