Customisable email templates in Rails made easy using panoramic and liquid-rails

Pratik Bhagwat
Aug 23 · 4 min read
Photo by Persnickety Prints on Unsplash

The Demand

Customisable email templates is the requirement in most of the business applications. The business needs to have a user interface where they can add/edit email templates and have specific variables which should be replaced dynamically based on the recipient of the email.

Implementing this can get very complex and requires a lot of work if the right approach is not followed.

Here are two gems that can help to implement this easier and in a short amount of time.

Panoramic

Rails provide many hidden features which most developers are not aware of. One of the feature is ActionView::Resolver. It allows creating our own renderer for views. Panoramic gem leverages this functionality to use templates saved in the database.

The gem documentation is pretty straightforward and there’s pretty less work to get this feature working. So let’s get started with our implementation.

1. Creating model to store templates

Let’s create a table named `notification_templates` (this name can be used for notifications as well as notifications!) with columns required by the gem.

$ rails g model CreateNotificationTemplates body:text path:string locale:string handler:string partial:boolean format:stringclass CreateNotificationTemplates < ActiveRecord::Migration[5.2]
def change
create_table :notification_templates do |t|
t.text :body
t.string :path
t.string :locale
t.string :handler
t.boolean :partial, default: false
t.string :format
t.timestamps
end
end
end

In model

class NotificationTemplate < ApplicationRecord  store_templatesend

2. Create a mailer

Create required mailer and add one of the gem methods

prepend_view_path: search for templates first in your resolver, then on filesystem

append_view_path: search for templates first on filesystem, then in your resolver

class FeedbackResponseMailer < ApplicationMailer
prepend_view_path NotificationTemplate.resolver
def send_response_mail(params)
@user = User.find(params[:user_id])
mail(to: @user.email, subject: 'Thank you')
end
end

We will use prepend_view_path here so that priority is given to database templates.

And now once we populate the database with the view, we are done.

3. Populate database

Create a NotificationTemplate with following values

#<NotificationTemplate:0x00007fe070fb0038> {
:id => 1,
:body => "Hi <%= @user.name %>, Thanks for your feedback",
:path => "feedback_response_mailer/send_response_mail",
:locale => nil,
:handler => "erb",
:partial => false,
:format => "html"
}

If we now call the mailer we get the email with the name of the user.

Our is foundation is ready. Now let’s move into creating UI to create a template.

Adding WYSIWYG editor

Create the NotificationTemplates controller with basic CRUD actions. To allow formatting of content any WYSIWYG editor like tinymce-rails can be used.

<%= form_with(model: notification_template, local: true) do |form| %>
<div class="form">
<%= form.text_area :body, class: "tinymce", rows: 40, cols: 120 %>
<%= tinymce %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>

The form should look something like this:

This should be enough to get going to create customisable email templates. But wait, there’s a problem. This is not user-friendly, one has to learn ruby to get it working and most importantly this is a big security threat. Any ruby code can get injected into the database and you know what happens next.

Liquid-Rails

liquid-rails gem is the solution to both of these problems. Again the gem documentation is pretty straightforward. This gem allows creating liquid templates with .liquid handler for Rails views.

It allows restricting which variables are available to views and even the helper methods. Let’s see how it’s done.

  1. Install gem ‘liquid-rails’ by adding it into the Gemfile
  2. Modify your mailer to restrict variables
class FeedbackResponseMailer < ApplicationMailer
prepend_view_path NotificationTemplate.resolver
def send_response_mail
@user = User.find(params[:user_id])
mail(to: @user.email, subject: 'Thank you')
end
def liquid_assigns
{ 'user_name' => @user.name }
end
end

3. Update NotificationTemplate to use `liquid` handler and body with liquid format.

notification_template.update(
handler: 'liquid',
body: "<p>Hi {{user_name}}, Thanks for your feedback</p>"
)

The body can also be updated with the UI we created earlier.

Now when we trigger the email, we get an email with the user_name replaced with the value. The UI can have variables customer can enter and the customer can easily understand and use those variables wherever required.

Conclusion

This is the power of Ruby and Rails and gems. A highly complex implementation is implemented with very less code. This article also shows how hidden features in Rails can be so much beneficial to reduce the work and how different gems can be combined the right way to get the functionality working.

Hope you liked this article. Please don’t forget to clap. Thanks for reading.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade