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
private
def create_verification_hash
unless self.verification_hash.nil?
self.verification_hash = Digest::SHA1.hexdigest("–" + rand(10) + "–" + name + Time.now.to_s "–")
end
end
# 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
end
end
def self.down
drop_table :contents
end
end
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
end
end
def self.down
drop_table :mail_checks
end
end
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 )
end
end
end
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)
else
savedurl= Content.find_by_url(url)
end
url_for( :controller=>"campaign",
:action=>"redirect",
:content_verification_hash => savedurl.verification_hash,
:verification_hash => @dbcontact.verification_hash,
:host => // HERE YOUR APPLICATION HOST //,
nly_path => false
)
end
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 @dbcontact.name instead of the name of the receiver (this rule must be extended to all the attributes of our contacts)
- Use ‘url()’ helper ( http://someurl.com —> url(‘http://someurl.com’) )
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 SENDER=info@myowndomain.com 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"
return
else
@user.mail_checks.create(
:content => @content.url
)
redirect_to @content.url
return
end
redirect_to // PUT A DEFAULT REDIRECT URL HERE
end
# 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.