Description
A history of emails sent to each user: - Open and click tracking - Easy UTM tagging - Works with any email service.
Ahoy Email alternatives and similar gems
Based on the "Email" category.
Alternatively, view Ahoy Email alternatives based on common mentions on social networks and blogs.
-
Roadie
Roadie tries to make sending HTML emails a little less painful by inlining stylesheets and rewriting relative URLs for you inside your emails. -
MailForm
Send e-mail straight from forms in Rails with I18n, validations, attachments and request information. -
Truemail
Configurable framework agnostic plain Ruby email validator. Verify email via Regex, DNS and SMTP. Be sure that email address valid and exists. -
Maily
A Rails Engine to manage, test and navigate through all your email templates of your app, being able to preview them directly in your browser.
Get performance insights in less than 4 minutes
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest. Visit our partner's website for more details.
Do you think we are missing an alternative of Ahoy Email or a related project?
README
Ahoy Email
:postbox: Email analytics for Rails
You get:
- A history of emails sent to each user
- Easy UTM tagging
- Optional open and click tracking
:bullettrain_side: To manage unsubscribes, check out Mailkick
:fire: To track visits and events, check out Ahoy
Installation
Add this line to your application’s Gemfile:
gem 'ahoy_email'
And run the generator. This creates a model to store messages.
rails generate ahoy_email:install
rails db:migrate
How It Works
Message History
Ahoy creates an Ahoy::Message
record for each email sent by default. You can disable history for a mailer:
class CouponMailer < ApplicationMailer
track message: false # use only/except to limit actions
end
Or by default:
AhoyEmail.default_options[:message] = false
Users
Ahoy records the user a message is sent to - not just the email address. This gives you a history of messages for each user, even if they change addresses.
By default, Ahoy tries @user
then params[:user]
then User.find_by(email: message.to)
to find the user.
You can pass a specific user with:
class CouponMailer < ApplicationMailer
track user: -> { params[:some_user] }
end
The user association is polymorphic, so use it with any model.
To get all messages sent to a user, add an association:
class User < ApplicationRecord
has_many :messages, class_name: "Ahoy::Message", as: :user
end
And run:
user.messages
Extra Attributes
Record extra attributes on the Ahoy::Message
model.
Create a migration to add extra attributes to the ahoy_messages
table. For example:
class AddCouponIdToAhoyMessages < ActiveRecord::Migration[6.0]
def change
add_column :ahoy_messages, :coupon_id, :integer
end
end
Then use:
class CouponMailer < ApplicationMailer
track extra: {coupon_id: 1}
end
You can use a proc as well.
class CouponMailer < ApplicationMailer
track extra: -> { {coupon_id: params[:coupon].id} }
end
UTM Tagging
Automatically add UTM parameters to links.
class CouponMailer < ApplicationMailer
track utm_params: true # use only/except to limit actions
end
The defaults are:
utm_medium
-email
utm_source
- the mailer name likecoupon_mailer
utm_campaign
- the mailer action likeoffer
You can customize them with:
class CouponMailer < ApplicationMailer
track utm_params: true, utm_campaign: -> { "coupon#{params[:coupon].id}" }
end
Skip specific links with:
<%= link_to "Go", some_url, data: {skip_utm_params: true} %>
Opens & Clicks
Setup
Additional setup is required to track opens and clicks.
Create a migration with:
class AddTokenToAhoyMessages < ActiveRecord::Migration[6.0]
def change
add_column :ahoy_messages, :token, :string
add_column :ahoy_messages, :opened_at, :timestamp
add_column :ahoy_messages, :clicked_at, :timestamp
add_index :ahoy_messages, :token
end
end
Create an initializer config/initializers/ahoy_email.rb
with:
AhoyEmail.api = true
And add to mailers you want to track:
class CouponMailer < ApplicationMailer
track open: true, click: true # use only/except to limit actions
end
How It Works
For opens, an invisible pixel is added right before the </body>
tag in HTML emails. If the recipient has images enabled in their email client, the pixel is loaded and the open time recorded.
For clicks, a redirect is added to links to track clicks in HTML emails.
https://chartkick.com
becomes
https://yoursite.com/ahoy/messages/rAnDoMtOkEn/click?url=https%3A%2F%2Fchartkick.com&signature=...
A signature is added to prevent open redirects.
Skip specific links with:
<%= link_to "Go", some_url, data: {skip_click: true} %>
By default, unsubscribe links are excluded. To change this, use:
AhoyEmail.default_options[:unsubscribe_links] = true
You can specify the domain to use with:
AhoyEmail.default_options[:url_options] = {host: "mydomain.com"}
Events
Subscribe to open and click events by adding to the initializer:
class EmailSubscriber
def open(event)
# your code
end
def click(event)
# your code
end
end
AhoyEmail.subscribers << EmailSubscriber.new
Here’s an example if you use Ahoy to track visits and events:
class EmailSubscriber
def open(event)
event[:controller].ahoy.track "Email opened", message_id: event[:message].id
end
def click(event)
event[:controller].ahoy.track "Email clicked", message_id: event[:message].id, url: event[:url]
end
end
AhoyEmail.subscribers << EmailSubscriber.new
Data Protection
We recommend encrypting the to
field (as well as the subject
if it’s sensitive). Lockbox is great for this. Use Blind Index if you need to query by the to
field.
Create app/models/ahoy/message.rb
with:
class Ahoy::Message < ApplicationRecord
self.table_name = "ahoy_messages"
belongs_to :user, polymorphic: true, optional: true
encrypts :to
blind_index :to
end
Data Retention
Delete older data with:
Ahoy::Message.where("created_at < ?", 1.year.ago).in_batches.delete_all
Delete data for a specific user with:
Ahoy::Message.where(user_id: 1).in_batches.delete_all
Reference
Set global options
AhoyEmail.default_options[:user] = -> { params[:admin] }
Use a different model
AhoyEmail.message_model = -> { UserMessage }
Or fully customize how messages are tracked
AhoyEmail.track_method = lambda do |data|
# your code
end
Mongoid
If you prefer to use Mongoid instead of ActiveRecord, create app/models/ahoy/message.rb
with:
class Ahoy::Message
include Mongoid::Document
belongs_to :user, polymorphic: true, optional: true, index: true
field :to, type: String
field :mailer, type: String
field :subject, type: String
field :sent_at, type: Time
end
Upgrading
1.0
Breaking changes
- UTM tagging, open tracking, and click tracking are no longer enabled by default. To enable, create an initializer with:
AhoyEmail.api = true
AhoyEmail.default_options[:open] = true
AhoyEmail.default_options[:click] = true
AhoyEmail.default_options[:utm_params] = true
- Only sent emails are recorded
- Proc options are now executed in the context of the mailer and take no arguments
# old
user: ->(mailer, message) { User.find_by(email: message.to) }
# new
user: -> { User.find_by(email: message.to) }
- Invalid options now throw an
ArgumentError
AhoyEmail.track
was removed in favor ofAhoyEmail.default_options
- The
heuristic_parse
option was removed and is now the default
History
View the changelog
Contributing
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
To get started with development and testing:
git clone https://github.com/ankane/ahoy_email.git
cd ahoy_email
bundle install
rake test