Customisable email templates in Rails made easy using panoramic and liquid-rails
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.
- Install gem ‘liquid-rails’ by adding it into the Gemfile
- 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.