Using Trailblazer Cells in Action Mailer

Over the past couple of weeks we’ve been looking at building out a registration process using Trailblazer and Ruby on Rails. First we looked at Building out a User Confirmation flow in Trailblazer, and last week we looked at Confirming Users with Trailblazer.

One thing we didn’t look at was adding the confirmation email that is sent to the user to allow them to confirm their email address.

A couple of weeks ago we looked at Getting started with Trailblazer Cells. A Cell is basically a View Model that allows encapsulation for your views. This can often greatly reduce the complexity of messy views!

However, Cells can also be used for your emails. In today’s tutorial we will be looking at Cells and Action Mailer.

Getting up to speed

I’ve already mentioned where we left off from last week in the introduction to this post so I won’t go over those links again. If you have missed the last couple of weeks of posts it might be a good idea to have a quick glance over those posts now.

You will also need to have an understanding of Action Mailer to get the most out of this tutorial. We’ve already covered Getting started with Action Mailer in Ruby on Rails and Testing Active Job and Active Mailer in Ruby on Rails so you can take a look back at those posts if you want to catch up.

A couple of weeks ago I added a UserMailer class and a confirm_membership method for sending the confirmation emails. If you remember back, you can think of the UserMailer as essentially the same as a Controller.

One thing I didn’t do was to generate the actual confirmation link that will be in the email. Each confirmation link is unique and so it’s not something we can just hard code.

Now that we added the route in last week’s tutorial I can generate the link:

class UserMailer < ApplicationMailer  
def confirm_membership(user)  
@user = user  
@url = confirmation_confirm_url(token: @user.confirmation_token)  
mail(to:, subject: "Confirm your Culttt Membership!")  

You might be wondering why I’m not generating the link inside of the Cell. Isn’t the whole point of using Cells to benefit from encapsulation? Normally I would of just generated the url from inside the Cell, but it’s a pain in the arse to do things like that in Rails because generating a url ends up touching a lot of core components of the framework.

Adding the concept helper method

Next we need to add a couple of things to make this thing work.

Firstly I’m going to add the email view:

= concept("confirmation/confirm/cell/email", @user, url: @url)  

As you can see this is really simple because we’re just delegating to the Cell. Here I’m passing the user and the url from the UserMailer.

Next we can generate a preview of the email. This isn’t going to work because the Cell hasn’t be created yet, but we actually stumble across another problem first.

The concept helper function does not exist because Trailblazer only add it to the Controller and Action View. To fix this we can add a new method to the ActionMailer class that the UserMailer inherits from:

class ApplicationMailer < ActionMailer::Base  
default from: ""  
layout ‘mailer’

def concept(name, model=nil, options={}, &block)  
::Cell::Concept.cell(name, model, options, &block)  

Now if you attempt to preview the email again you should now see the correct error because the Cell does not exist.

Creating the Email cell

Next up we can create the email cell. The Cell is basically a View Model and so we can deal with any type of logic stuff in here:

class Email < Trailblazer::Cell  
def show  

def greeting  
model.username or "there"  

def url  
link_to "here", @options[:url], class: "qa-confirmation-link"  

As usual we have the default show method. We also have a couple of methods that we will need in the view. Firstly we have a greeting method that will either return the users username or simply a placeholder string. And secondly we have a url method that will generate the link and add the QA class for us.

Adding the template

Now that we have the Cell in place we can also add the template:

p Hey #{greeting}!

p Click #{url} to confirm your account!  

As you can see this is really simple for now. I hate having to deal with HTML emails and so I’ll kick that can down the road and deal with it another day. This very simple template will do for now.

If you fire up the preview again you should see it working correctly including the greeting and the url.

Adding some tests

Finally we can add a couple of tests to ensure that the HTML is generated correctly:

class EmailTest < Cell::TestCase  
include Rails.application.routes.url_helpers

def setup  
Rails.application.routes.default_url_options[:host] = "localhost:3000"

@default = User::Create::Operation::Default.(user: attributes_for(:user)).model  
@imported = User::Create::Operation::Imported.(user: attributes_for(:imported_user)).model  

test "has confirmation link" do  
url = confirmation_confirm_url(token: @default.confirmation_token)  
html = concept("confirmation/confirm/cell/email", @default, url: url).()

assert_equal(html.find(".qa-confirmation-link")[:href], url)  

test "has default user greeting" do  
html = concept("confirmation/confirm/cell/email", @default, url: "").()

html.must_have_content("Hey #{@default.username}!")  

test "has imported user greeting" do  
html = concept("confirmation/confirm/cell/email", @imported, url: "").()

html.must_have_content("Hey there!")  

In these tests I’m checking that the confirmation link is correct and the greeting is set correctly depending on whether the user has a username or not.

As I’ve mentioned a couple of times now, Trailblazer makes it really easy to write these kinds of tests because we only need to invoke the cell, and not the whole Action Mailer process.


I hate having messy views. Once I start writing HTML I want to make it as clean as possible because I find HTML overwhelming and ugly. That’s why I use Slim!

Cells make it really easy to deal with the sprinkles of logic that you will inevitably need in your views.

They also make testing really easy so we can have more confidence in the contents of email without having to test the email via an integration test.

Using a Cell is today’s example is probably overkill, but hopefully it was a good illustration of what you can do with Cells and Action Mailer.