Send and track emails at scale with Rails and Redis
You get:
- A history of every email sent
- Track Sent, Open, Click, Unsubscribe, Success, Bounce and Complaint
- Easy UTM tagging
Works with any STMP provider but has built in Amazon SES callbacks (Success, Bounce and Complaint).
Send and track hundreds of thousands of emails per hour, with minimal resources.
Add this line to your application’s Gemfile:
gem 'email_engine'
Generate and modify the initializer:
rails generate email_engine:install
By default, EmailEngine will mount at root with /emails as the user facing resource and /admin/emails as the admin resource. You can change the route by manually mounting the engine.
mount EmailEngine::Engine => "/messages", as: 'email_engine'
If you use RailsEngine, ActiveAdmin or any other /admin namespaced engine, you'll need to mount EmailEngine first, otherwise the other engine might override the EmailEngine admin routes.
EmailEngine is configured to leverage the CanCan Authorization gem and the existing ability file. The following ability example would grant basic access to all users and restrict access to the EmailEngine admin to only users where user.admin? is true.
def initialize(user)
user ||= User.new
can [:open, :click, :unsubscribe, :bounce, :complaint], EmailEngine::Email
if user.admin?
can :manage, EmailEngine::Email
end
end
The tracking of email headers, content and statistics are all handled through Redis.
Every email has a unique Message Token, which is applied to the email header as "EMAIL-ENGINE-ID". Open, click and unsubscribe links use that Message Token to track usage, and stats are generated.
An admin provides the ability to monitor email performance.
EmailEngine tracks the user a message is sent to - not just the email address. This gives you a full history of messages for each user, even if he or she changes addresses.
By default, EmailEngine tries User.where(email: message.to.first).first
to find the user.
You can pass a specific user with:
class UserMailer < ActionMailer::Base
def welcome_email(user)
# ...
track user: user
mail to: user.email
end
end
An invisible pixel is added right before the </body>
tag in HTML emails.
If the recipient has images enabled in his or her email client, the pixel is loaded and the open time recorded.
Use track open: false
to skip this.
A redirect is added to links to track clicks in HTML emails.
http://chartkick.com
becomes
http://you.io/ahoy/messages/rAnDoMtOkEn/click?url=http%3A%2F%2Fchartkick.com&signature=...
A signature is added to prevent open redirects.
Use track click: false
to skip tracking, or skip specific links with:
<a data-skip-click="true" href="...">Can't touch this</a>
UTM parameters are added to links if they don’t already exist.
The defaults are:
- utm_medium -
email
- utm_source - the mailer name like
user_mailer
- utm_campaign - the mailer action like
welcome_email
Use track utm_params: false
to skip tagging, or skip specific links with:
<a data-skip-utm-params="true" href="...">Break it down</a>
Extra Attributes can be added to the email record.
track extra: {campaign_id: 1}
There are 4 places to set options. Here’s the order of precedence.
class UserMailer < ActionMailer::Base
def welcome_email(user)
# ...
track user: user
mail to: user.email
end
end
class UserMailer < ActionMailer::Base
track utm_campaign: "boom"
end
EmailEngine.track open: false
EmailEngine.configure do |config|
config.send = true
config.open = true
config.click = true
config.unsubscribe = true
config.unsubscribe_method = nil
config.utm_params = true
config.url_options = {}
config.redis_url = "redis://localhost:6379/"
config.store_email_content = true
config.expire_email_content = 1.hour
end
You can use a Proc
for any option.
track utm_campaign: proc{|message, mailer| mailer.action_name + Time.now.year }
Disable tracking for an email
track message: false
Or specific actions
track only: [:welcome_email]
track except: [:welcome_email]
Or by default
EmailEngine.track message: false
Customize domain
track url_options: {host: "mydomain.com"}
Use a different model
EmailEngine.message_model = UserMessage
Everyone is encouraged to help improve this project. Here are a few ways you can help:
- Report bugs
- Fix bugs and submit pull requests
- Write, clarify, or fix documentation
- Suggest or add new features
This gem was inspired by, and evolved from, AhoyEmail written by Andrew Kane.