README.md in ahoy_email-1.1.1 vs README.md in ahoy_email-2.0.0
- old
+ new
@@ -1,9 +1,11 @@
# Ahoy Email
First-party email analytics for Rails
+**Ahoy Email 2.0 was recently released** - see [how to upgrade](#upgrading)
+
:fire: For web and native app analytics, check out [Ahoy](https://github.com/ankane/ahoy)
:bullettrain_side: To manage unsubscribes, check out [Mailkick](https://github.com/ankane/mailkick)
[![Build Status](https://github.com/ankane/ahoy_email/workflows/build/badge.svg?branch=master)](https://github.com/ankane/ahoy_email/actions)
@@ -14,52 +16,69 @@
```ruby
gem 'ahoy_email'
```
-And run the generator. This creates a model to store messages.
-
-```sh
-rails generate ahoy_email:install
-rails db:migrate
-```
-
## Getting Started
-There are three main features:
+There are three main features, which can be used independently:
- [Message history](#message-history)
- [UTM tagging](#utm-tagging)
-- [Open & click analytics](#open--click-analytics)
+- [Click analytics](#click-analytics)
## Message History
-Ahoy Email creates an `Ahoy::Message` record for each email sent by default. You can disable history for a mailer:
+To encrypt email addresses, install [Lockbox](https://github.com/ankane/lockbox) and [Blind Index](https://github.com/ankane/blind_index) and run:
+```sh
+rails generate ahoy:messages
+rails db:migrate
+```
+
+If you prefer not to encrypt data, run:
+
+```sh
+rails generate ahoy:messages --unencrypted
+rails db:migrate
+```
+
+Then, add to mailers:
+
```ruby
class CouponMailer < ApplicationMailer
- track message: false # use only/except to limit actions
+ has_history
end
```
-Or by default:
+Use the `Ahoy::Message` model to query messages:
```ruby
-AhoyEmail.default_options[:message] = false
+Ahoy::Message.last
```
-### Users
+Use only and except to limit actions
-Ahoy Email 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.
+```ruby
+class CouponMailer < ApplicationMailer
+ has_history only: [:welcome]
+end
+```
-By default, Ahoy tries `@user` then `params[:user]` then `User.find_by(email: message.to)` to find the user.
+To store history for all mailers, create `config/initializers/ahoy_email.rb` with:
-You can pass a specific user with:
+```ruby
+AhoyEmail.default_options[: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:
+
```ruby
class CouponMailer < ApplicationMailer
- track user: -> { params[:some_user] }
+ has_history user: -> { params[:some_user] }
end
```
The user association is [polymorphic](https://railscasts.com/episodes/154-polymorphic-association), so use it with any model.
@@ -75,53 +94,81 @@
```ruby
user.messages
```
-### Extra Attributes
+### Extra Data
-Record extra attributes on the `Ahoy::Message` model.
+Add extra data to messages. Create a migration like:
-Create a migration to add extra attributes to the `ahoy_messages` table. For example:
-
```ruby
class AddCouponIdToAhoyMessages < ActiveRecord::Migration[6.1]
def change
add_column :ahoy_messages, :coupon_id, :integer
end
end
```
-Then use:
+And use:
```ruby
class CouponMailer < ApplicationMailer
- track extra: {coupon_id: 1}
+ has_history extra: {coupon_id: 1}
end
```
You can use a proc as well.
```ruby
class CouponMailer < ApplicationMailer
- track extra: -> { {coupon_id: params[:coupon].id} }
+ has_history extra: -> { {coupon_id: params[:coupon].id} }
end
```
-## UTM Tagging
+### Options
-Use UTM tagging to attribute a conversion (like an order) to an email campaign. If you use [Ahoy](https://github.com/ankane/ahoy) for web analytics:
+Set global options
-1. Send an email with UTM parameters
-2. When a user visits the site, Ahoy will create a visit with the UTM parameters
-3. When a user orders, the visit will be associated with the order (if [configured](https://github.com/ankane/ahoy#associated-models))
+```ruby
+AhoyEmail.default_options[:user] = -> { params[:admin] }
+```
-Add UTM parameters to links with:
+Use a different model
```ruby
+AhoyEmail.message_model = -> { UserMessage }
+```
+
+Or fully customize how messages are tracked
+
+```ruby
+AhoyEmail.track_method = lambda do |data|
+ # your code
+end
+```
+
+### Data Retention
+
+Delete older data with:
+
+```ruby
+Ahoy::Message.where("created_at < ?", 1.year.ago).in_batches.delete_all
+```
+
+Delete data for a specific user with:
+
+```ruby
+Ahoy::Message.where(user_id: 1).in_batches.delete_all
+```
+
+## UTM Tagging
+
+Use UTM tagging to attribute visits or conversions to an email campaign. Add UTM parameters to links with:
+
+```ruby
class CouponMailer < ApplicationMailer
- track utm_params: true # use only/except to limit actions
+ utm_params
end
```
The defaults are:
@@ -131,91 +178,97 @@
You can customize them with:
```ruby
class CouponMailer < ApplicationMailer
- track utm_params: true, utm_campaign: -> { "coupon#{params[:coupon].id}" }
+ utm_params utm_campaign: -> { "coupon#{params[:coupon].id}" }
end
```
+Use only and except to limit actions
+
+```ruby
+class CouponMailer < ApplicationMailer
+ utm_params only: [:welcome]
+end
+```
+
Skip specific links with:
```erb
<%= link_to "Go", some_url, data: {skip_utm_params: true} %>
```
-## Open & Click Analytics
+## Click Analytics
-While it’s nice to get feedback on the performance of your emails, we discourage the use of open tracking. If you do decide to use open or click tracking, be sure to get consent from your users and consider a short retention period. Check out [this article](https://www.eff.org/deeplinks/2019/01/stop-tracking-my-emails) for more best practices.
+You can track click-through rate to see how well campaigns are performing. Stats can be stored in any data store, and there’s a built-in integration with Redis.
-### Setup
+#### Redis
-Create a migration with:
+Add this line to your application’s Gemfile:
```ruby
-class AddTokenToAhoyMessages < ActiveRecord::Migration[6.1]
- def change
- add_column :ahoy_messages, :token, :string
- add_index :ahoy_messages, :token
+gem 'redis'
+```
- # for opens
- add_column :ahoy_messages, :opened_at, :timestamp
+And create `config/initializers/ahoy_email.rb` with:
- # for clicks
- add_column :ahoy_messages, :clicked_at, :timestamp
- end
-end
+```ruby
+# pass your Redis client if you already have one
+AhoyEmail.subscribers << AhoyEmail::RedisSubscriber.new(redis: Redis.new)
+AhoyEmail.api = true
```
-Create an initializer `config/initializers/ahoy_email.rb` with:
+#### Other
+Create `config/initializers/ahoy_email.rb` with:
+
```ruby
+class EmailSubscriber
+ def track_send(data)
+ # your code
+ end
+
+ def track_click(data)
+ # your code
+ end
+
+ def stats(campaign = nil)
+ # optional, for AhoyEmail.stats
+ end
+end
+
+AhoyEmail.subscribers << EmailSubscriber
AhoyEmail.api = true
-```
+````
-And add to mailers you want to track:
+### Setup
+Add to mailers you want to track
+
```ruby
class CouponMailer < ApplicationMailer
- track open: true, click: true
+ track_clicks campaign: "my-campaign"
end
```
Use only and except to limit actions
```ruby
class CouponMailer < ApplicationMailer
- track click: true, only: [:welcome]
+ track_clicks campaign: "my-campaign", only: [:welcome]
end
```
Or make it conditional
```ruby
class CouponMailer < ApplicationMailer
- track click: -> { params[:user].opted_in? }
+ track_clicks campaign: "my-campaign", if: -> { params[:user].opted_in? }
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](https://www.owasp.org/index.php/Open_redirect).
-
Skip specific links with:
```erb
<%= link_to "Go", some_url, data: {skip_click: true} %>
```
@@ -230,111 +283,70 @@
```ruby
AhoyEmail.default_options[:url_options] = {host: "mydomain.com"}
```
-### Events
+### Stats
-Subscribe to open and click events by adding to the initializer:
+Get stats for all campaigns
```ruby
-class EmailSubscriber
- def open(event)
- # your code
- end
-
- def click(event)
- # your code
- end
-end
-
-AhoyEmail.subscribers << EmailSubscriber.new
+AhoyEmail.stats
```
-Here’s an example if you use [Ahoy](https://github.com/ankane/ahoy) to track visits and events:
+Get stats for a specific campaign
```ruby
-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
+AhoyEmail.stats("my-campaign")
```
-## Data Protection
+## Upgrading
-We recommend encrypting the `to` field (as well as the `subject` if it’s sensitive). [Lockbox](https://github.com/ankane/lockbox) is great for this. Use [Blind Index](https://github.com/ankane/blind_index) if you need to query by the `to` field.
+### 2.0
-Create `app/models/ahoy/message.rb` with:
+Ahoy Email 2.0 brings a number of changes. Here are a few to be aware of:
-```ruby
-class Ahoy::Message < ApplicationRecord
- self.table_name = "ahoy_messages"
- belongs_to :user, polymorphic: true, optional: true
+- The `to` field is encrypted by default for new installations. If you’d like to encrypt an existing installation, install [Lockbox](https://github.com/ankane/lockbox) and [Blind Index](https://github.com/ankane/blind_index) and follow the Lockbox instructions for [migrating existing data](https://github.com/ankane/lockbox#migrating-existing-data).
- encrypts :to
- blind_index :to
-end
-```
+ For the model, create `app/models/ahoy/message.rb` with:
-## Data Retention
+ ```ruby
+ class Ahoy::Message < ActiveRecord::Base
+ self.table_name = "ahoy_messages"
-Delete older data with:
+ belongs_to :user, polymorphic: true, optional: true
-```ruby
-Ahoy::Message.where("created_at < ?", 1.year.ago).in_batches.delete_all
-```
+ encrypts :to, migrating: true
+ blind_index :to, migrating: true
+ end
+ ```
-Delete data for a specific user with:
+- The `track` method has been broken into:
-```ruby
-Ahoy::Message.where(user_id: 1).in_batches.delete_all
-```
+ - `has_history` for message history
+ - `utm_params` for UTM tagging
+ - `track_clicks` for click analytics
-## Reference
+- Message history is no longer enabled by default. Add `has_history` to individual mailers, or create an initializer with:
-Set global options
+ ```ruby
+ AhoyEmail.default_options[:message] = true
+ ```
-```ruby
-AhoyEmail.default_options[:user] = -> { params[:admin] }
-```
+- For privacy, open tracking has been removed.
-Use a different model
+- For clicks, we encourage you to try [aggregate analytics](#click-analytics) to measure the performance of campaigns. You can use a library like [Rollup](https://github.com/ankane/rollup) to aggregate existing data, then drop the `token` and `clicked_at` columns.
-```ruby
-AhoyEmail.message_model = -> { UserMessage }
-```
+ To keep individual analytics, use `has_history` and `track_clicks campaign: false` and create an initializer with:
-Or fully customize how messages are tracked
+ ```ruby
+ AhoyEmail.save_token = true
+ AhoyEmail.subscribers << AhoyEmail::MessageSubscriber
+ ```
-```ruby
-AhoyEmail.track_method = lambda do |data|
- # your code
-end
-```
+ If you use a custom subscriber, `:message` is no longer included in click events. You can use `:token` to query the message if needed.
-## Mongoid
-
-If you prefer to use Mongoid instead of Active Record, create `app/models/ahoy/message.rb` with:
-
-```ruby
-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
-```
+- Users are shown a link expired page when signature verification fails instead of being redirected to the homepage when `AhoyEmail.invalid_redirect_url` is not set
## History
View the [changelog](https://github.com/ankane/ahoy_email/blob/master/CHANGELOG.md)