HowTo: track the effects of your email campaign

When it’s time to send an email to all of your subscribers we always need to maximize the information we can get back from our campaign. Here are a few tricks that can help us in this operation:

Step one: the receivers list

First of all we need to set-up a table containing the list of the users; to do this we can use an existing table in our application or set up a new one; we’re going to follow this way and create it among with a new rails application.

rails campaign

cd campaign

ruby script/generate scaffold_resource user name:string email:string verification_hash:string

.. set up your database.yml …

rake db:migrate

ruby script/server

As you may can notice I’ve created a table with just the fields we’re going to use in this how-to, but you can add as much user information as you want.

Before continue we need to hide the ‘verification_hash’ fields from our scaffold views; this is beacuse we’re going to create an ‘after_save’ filter that fills the verification_hash automatically; here’s the filter:

# inside app/models/user.rb

before_save :create_verification_hash


def create_verification_hash

unless self.verification_hash.nil?

self.verification_hash = Digest::SHA1.hexdigest("–" + rand(10) + "–" + name + "–")



# inside config/environment.rb

require ‘digest/sha1′

After you have created the users list you can start fill it with the receivers. You can do it by using the rails scaffold we have created in the code list above (http://localhost:3000/users) or you can use a SQL import command or wathever you like.

Then it’s time to set up ActionMailer, to do this you have to write a few configuration lines in your environment.rb; you could user either a smtp server or sendmail, for more information on how to complete the configuration please follow the wiki article on ActionMailer ; when you’re done you can run the following command:

ruby script/generate mailer email

And create the app’s mail handler.

Step two: track the contents

We’re going to create a table called ‘contents’ that will contains all the url of our emails; we don’t need a scaffold ‘cause we’re not going to fill this table by hand but we’ll create an helper that will ‘catch’ all the urls in an email template and adds them to our table.

Next we need to create a table that links contents and users; I’ve called that ‘mail_checks’ but maybe there are better names; in this table we’re going to store all the clicks the users make on the contents.

ruby script/generate model content

ruby script/generate model mail_check

Now we need to open ‘002_create_contents.rb’ and write:

class CreateContents < ActiveRecord::Migration

def self.up

create_table :contents do |t|

t.column :url, :string

t.column :verification_hash, :string

t.column :campaign_code, :string



def self.down

drop_table :contents



In ‘003_create_mail_checks.rb’ we have to write:

class CreateMailChecks < ActiveRecord::Migration

def self.up

create_table :mail_checks do |t|

t.column :contact_id, :integer

t.column :content, :string

t.column :created_on, :datetime



def self.down

drop_table :mail_checks



And then migrate: (remeber also to add ‘has_many’ and ‘belongs_to’ to the models according the filelds created)

rake db:migrate

We need to repeat the same ‘before_save’ filter inside ‘models/content.rb’ in order to let the istances of the Content model fill their verification_hash; when done we can continue to the next chapter.

Step three: the e-mail

What we’re looking for is a rake task that lets you specify the name of the e-mail you want to send, the subject and the sender, to do this first we need to create a structure that let us handle multiple e-mails without creating a method in our ActionMailer class for each of them:

# inside models/email.rb

class Email < ActionMailer::Base

helper :application

Dir.glob(File.join(RAILS_ROOT,"app","views","email","*.rhtml")).collect{|a| a.split("/").last.split(".").first}.each do |e|

define_method(e) do |sender,email,sbj,dbcontact|

recipients email

from sender

subject sbj

# Email body substitutions go here

body ( :dbcontact => dbcontact )




As you may noticed I’ve included the ‘application._helper.rb’ in this ActionMailer; by doing this I can now use all the helpers in that file inside my email views; in detail I can create a set of helpers to parse and store the urls contained in the .rhtml template:

# inside app/helpers/application_helper.rb

def url_for_mail(url)

if Content.find_by_url(url).nil?

savedurl = Content.create(:url=>url)


savedurl= Content.find_by_url(url)


url_for( :controller=>"campaign",


:content_verification_hash => savedurl.verification_hash,

:verification_hash => @dbcontact.verification_hash,


:o nly_path => false



We’re almost done: three things remain:

  • Modify the email template in order to use this helper while generating urls,
  • Create a rake task to send an email,
  • Create the redirect action.

Step four: final adjustements

Now we can create our first email message with this rules:

  • Use instead of the name of the receiver (this rule must be extended to all the attributes of our contacts)
  • Use ‘url()’ helper ( —> url(‘’) )

You can find the rake task here and it must be put inside the lib/tasks directory of our campaign application, you can invoke it with this command:

rake campaign:send SUBJECT="Monthly newsletter" MAILNAME="you_rhtml_email_name"

Last but not least we need to create a controller called campaign and an action called redirect:

# inside app/controllers/campaing_controller.rb

def redirect

@user = Contact.find_by_verification_hash(params[:verification_hash])

@content = Content.find_by_verification_hash(params[:content_verification_hash])

if @user.nil? or @content.nil?

render :text=>"Redirect not allowed"




:content => @content.url


redirect_to @content.url





# inside config/routes.rb

map.connect ‘vc/:verification_hash/:content_verification_hash’, :controller => ‘campaign’, :action=>’redirect’

Ok, now you’re reday to start your first 100% tracked e-mail campaign, each time a user clicks on a link inside your e-mail it will be stored inside the ‘mail_checks’ table; a few final considerations:

Using a SHA – Digest as ‘verification_hash’ is a secure but not-very-nice-looking way, a better solution could be use a more small self-generate digest.
You can add as many report as you want by querying the mail_checks table.

Leave a Comment