First-party email analytics for Rails
🔥 For web and native app analytics, check out Ahoy
🚄 To manage unsubscribes, check out Mailkick
Add this line to your application’s Gemfile:
gem "ahoy_email"There are three main features, which can be used independently:
To encrypt email addresses with Lockbox, install Lockbox and Blind Index and run:
rails generate ahoy:messages --encryption=lockbox
rails db:migrateTo use Active Record encryption (Rails 7+, experimental), run:
rails generate ahoy:messages --encryption=activerecord
rails db:migrateIf you prefer not to encrypt data, run:
rails generate ahoy:messages --encryption=none
rails db:migrateThen, add to mailers:
class CouponMailer < ApplicationMailer
has_history
endUse the Ahoy::Message model to query messages:
Ahoy::Message.lastUse only and except to limit actions
class CouponMailer < ApplicationMailer
has_history only: [:welcome]
endTo store history for all mailers, create config/initializers/ahoy_email.rb with:
AhoyEmail.default_options[:message] = trueBy default, Ahoy Email 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
has_history user: -> { params[:some_user] }
endThe 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
endAnd run:
user.messagesAdd extra data to messages. Create a migration like:
class AddCouponIdToAhoyMessages < ActiveRecord::Migration[6.1]
def change
add_column :ahoy_messages, :coupon_id, :integer
end
endAnd use:
class CouponMailer < ApplicationMailer
has_history extra: {coupon_id: 1}
endYou can use a proc as well.
class CouponMailer < ApplicationMailer
has_history extra: -> { {coupon_id: params[:coupon].id} }
endSet 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
endDelete older data with:
Ahoy::Message.where("created_at < ?", 1.year.ago).in_batches.delete_allDelete data for a specific user with:
Ahoy::Message.where(user_id: 1, user_type: "User").in_batches.delete_allUse UTM tagging to attribute visits or conversions to an email campaign. Add UTM parameters to links with:
class CouponMailer < ApplicationMailer
utm_params
endThe defaults are:
utm_medium-emailutm_source- the mailer name likecoupon_mailerutm_campaign- the mailer action likeoffer
You can customize them with:
class CouponMailer < ApplicationMailer
utm_params utm_campaign: -> { "coupon#{params[:coupon].id}" }
endUse only and except to limit actions
class CouponMailer < ApplicationMailer
utm_params only: [:welcome]
endSkip specific links with:
<%= link_to "Go", some_url, data: {skip_utm_params: true} %>You can track click-through rate to see how well campaigns are performing. Stats can be stored in your database, Redis, or any other data store.
Run:
rails generate ahoy:clicks
rails db:migrateAnd create config/initializers/ahoy_email.rb with:
AhoyEmail.subscribers << AhoyEmail::DatabaseSubscriber
AhoyEmail.api = trueAdd this line to your application’s Gemfile:
gem "redis"And create config/initializers/ahoy_email.rb with:
# pass your Redis client if you already have one
AhoyEmail.subscribers << AhoyEmail::RedisSubscriber.new(redis: Redis.new)
AhoyEmail.api = trueCreate config/initializers/ahoy_email.rb with:
class EmailSubscriber
def track_send(data)
# your code
end
def track_click(data)
# your code
end
def stats(campaign)
# optional, for AhoyEmail.stats
end
end
AhoyEmail.subscribers << EmailSubscriber
AhoyEmail.api = trueAdd to mailers you want to track
class CouponMailer < ApplicationMailer
track_clicks campaign: "my-campaign"
endIf storing stats in the database, the mailer should also use has_history
Use only and except to limit actions
class CouponMailer < ApplicationMailer
track_clicks campaign: "my-campaign", only: [:welcome]
endOr make it conditional
class CouponMailer < ApplicationMailer
track_clicks campaign: "my-campaign", if: -> { params[:user].opted_in? }
endYou can also use a proc
class CouponMailer < ApplicationMailer
track_clicks campaign: -> { "coupon-#{action_name}" }
endSkip 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] = trueYou can specify the domain to use with:
AhoyEmail.default_options[:url_options] = {host: "mydomain.com"}Get stats for a campaign
AhoyEmail.stats("my-campaign")Ahoy Email 2.0 brings a number of changes. Here are a few to be aware of:
-
The
tofield is encrypted by default for new installations. If you’d like to encrypt an existing installation, install Lockbox and Blind Index and follow the Lockbox instructions for migrating existing data.For the model, create
app/models/ahoy/message.rbwith:class Ahoy::Message < ActiveRecord::Base self.table_name = "ahoy_messages" belongs_to :user, polymorphic: true, optional: true encrypts :to, migrating: true blind_index :to, migrating: true end
-
The
trackmethod has been broken into:has_historyfor message historyutm_paramsfor UTM taggingtrack_clicksfor click analytics
-
Message history is no longer enabled by default. Add
has_historyto individual mailers, or create an initializer with:AhoyEmail.default_options[:message] = true
-
For privacy, open tracking has been removed.
-
For clicks, we encourage you to try aggregate analytics to measure the performance of campaigns. You can use a library like Rollup to aggregate existing data, then drop the
tokenandclicked_atcolumns.To keep individual analytics, use
has_historyandtrack_clicks campaign: falseand create an initializer with:AhoyEmail.save_token = true AhoyEmail.subscribers << AhoyEmail::MessageSubscriber
If you use a custom subscriber,
:messageis no longer included in click events. You can use:tokento query the message if needed. -
Users are shown a link expired page when signature verification fails instead of being redirected to the homepage when
AhoyEmail.invalid_redirect_urlis not set
View the changelog
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:
git clone https://github.com/ankane/ahoy_email.git
cd ahoy_email
bundle install
bundle exec rake test