Ahoy Email
First-party email analytics for Rails
:fire: For web and native app analytics, check out Ahoy
:bullettrain_side: To manage email subscriptions, check out Mailkick
Installation
Add this line to your application’s Gemfile:
gem "ahoy_email"
Getting Started
There are three main features, which can be used independently:
Message History
To encrypt email addresses with Lockbox, install Lockbox and Blind Index and run:
rails generate ahoy:messages --encryption=lockbox
rails db:migrate
To use Active Record encryption (Rails 7+, experimental), run:
rails generate ahoy:messages --encryption=activerecord
rails db:migrate
If you prefer not to encrypt data, run:
rails generate ahoy:messages --encryption=none
rails db:migrate
Then, add to mailers:
class CouponMailer < ApplicationMailer
has_history
end
Use the Ahoy::Message
model to query messages:
Ahoy::Message.last
Use only and except to limit actions
class CouponMailer < ApplicationMailer
has_history only: [:welcome]
end
To store history for all mailers, create config/initializers/ahoy_email.rb
with:
AhoyEmail.[:message] = true
Users
By 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] }
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.
Extra Data
Add extra data to messages. Create a migration like:
class AddCouponIdToAhoyMessages < ActiveRecord::Migration[7.2]
def change
add_column :ahoy_messages, :coupon_id, :integer
end
end
And use:
class CouponMailer < ApplicationMailer
has_history extra: {coupon_id: 1}
end
You can use a proc as well.
class CouponMailer < ApplicationMailer
has_history extra: -> { {coupon_id: params[:coupon].id} }
end
Options
Set global options
AhoyEmail.[:user] = -> { params[:admin] }
Use a different model
AhoyEmail. = -> { UserMessage }
Or fully customize how messages are tracked
AhoyEmail.track_method = lambda do |data|
# your code
end
Data Retention
Delete older data with:
Ahoy::Message.where("sent_at < ?", 1.year.ago).in_batches.delete_all
Delete data for a specific user with:
Ahoy::Message.where(user_id: 1, user_type: "User").in_batches.delete_all
UTM Tagging
Use UTM tagging to attribute visits or conversions to an email campaign. Add UTM parameters to links with:
class CouponMailer < ApplicationMailer
utm_params
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
utm_params utm_campaign: -> { "coupon#{params[:coupon].id}" }
end
Use only and except to limit actions
class CouponMailer < ApplicationMailer
utm_params only: [:welcome]
end
Skip specific links with:
<%= link_to "Go", some_url, data: {skip_utm_params: true} %>
Click Analytics
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.
Database
Run:
rails generate ahoy:clicks
rails db:migrate
And create config/initializers/ahoy_email.rb
with:
AhoyEmail.subscribers << AhoyEmail::DatabaseSubscriber
AhoyEmail.api = true
Redis
Add 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 = true
Other
Create 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 = true
Usage
Add to mailers you want to track
class CouponMailer < ApplicationMailer
track_clicks campaign: "my-campaign"
end
If 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]
end
Or make it conditional
class CouponMailer < ApplicationMailer
track_clicks campaign: "my-campaign", if: -> { params[:user].opted_in? }
end
You can also use a proc
class CouponMailer < ApplicationMailer
track_clicks campaign: -> { "coupon-#{action_name}" }
end
Skip specific links with:
<%= link_to "Go", some_url, data: {skip_click: true} %>
By default, unsubscribe links are excluded. To change this, use:
AhoyEmail.[:unsubscribe_links] = true
You can specify the domain to use with:
AhoyEmail.[:url_options] = {host: "mydomain.com"}
Stats
Get stats for a campaign
AhoyEmail.stats("my-campaign")
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:
git clone https://github.com/ankane/ahoy_email.git
cd ahoy_email
bundle install
bundle exec rake test