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.
Thank you for this tutorial. I have been finding it difficult to understand these concepts. I have even read tons of tutorials including Trailblazer framework, but your explanation seems to be very explanatory. Do you have other resources to recommend so that I can fully grasp this service-oriented architecture in rails?
Thanks for your comment ๐
I haven’t done anything with SOA so I can’t recommend any resources for that. Good luck with your search!
Thanks for your effort, very useful article, keep going
Thank you ๐
Hello Jesus,
Thank you for nice article. I’m surprised that you use modules to create service objects.
I’ve learned that it should be a class. Another thing is that service objects call standardized methods like “call”, “execute”, “run”.(depends of project or organization standard). In my opinion service object should look something like this:
https://gist.github.com/mPanasiewicz/6c929ae289836540508d571e5d9b2892
In my opinion Service object should have single responsibility.
Hi,
Like I mention in the introduction, there are different ways to implement this, I’m not a big fan of the
call
/run
method pattern because it doesn’t help add any meaning to what you’re doing & it feels very mechanical to me.But feel free to use whatever works for you, I’m just proposing one way to do it ๐
Thanks for your comment.
Hi Jesus,
Thanks for the tutorial. Two things I’d like to share;
1) You can use
module_function
to create module functions for the named methods. That way you don’t need to callself
2) There’s a great talk on Service Objects here: https://www.youtube.com/watch?v=dSiE9N_f0h0
I’ve been using the Interactor gem mentioned in the talk on a project and it’s great.
Hi David!
Thanks for sharing that talk & about
module_function
, I also likeextend self
to get the same result ๐Thanks for posting this, a nice solid read. One question, what are your thoughts on creating a wrapper for the Twitter Client, rather than directly accessing it in this service?
Can you share a code example with a gist?
Hi Jesus, I’m learning a lot from your short pots/videos! Gracias ๐
About the presenter pattern, I don’t like two things:
* Another object per each model – I love rails but sometimes it’s frustrating to have too many layers (objects, files and, folders) to make a simple view.
* Hidden object calls inside an abstract variable called “presenter” for all views (harder reading and refactoring)
Since we already have Helpers, why should we use them to do the presenter’s job?
Keep posting great articles! =b
Hi Jรณni,
thanks for your comment!
What I like about presenters is that it makes the presentation layer feel a little bit more object-oriented, helpers alone don’t do that.
Thank you for this tutorial. I am reading your book ruby deep dive, is very good. Do you have some similiar book for Rails?
Hey Jhony,
thanks for your feedback! I’m working on an online version of a Rails book with quizes & stuff.
It’s a complex topic so I want to make sure it’s easy to understand. When I have news about it I’ll let you know.