From 6de785795d17805f136c85374b161945168303bf Mon Sep 17 00:00:00 2001 From: Joshua Wood Date: Mon, 16 Sep 2024 09:01:12 -0700 Subject: [PATCH] Format README markdown --- README.md | 300 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 209 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 667b643..e13dd5e 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ # Heya 👋 + ![Test](https://github.com/honeybadger-io/heya/workflows/Test/badge.svg) [![Gem Version](https://badge.fury.io/rb/heya.svg)](https://badge.fury.io/rb/heya) [![Maintainability](https://api.codeclimate.com/v1/badges/a6416e63ffc426715857/maintainability)](https://codeclimate.com/github/honeybadger-io/heya/maintainability) -Heya is a campaign mailer for Rails. Think of it like ActionMailer, but for timed email sequences. It can also perform other actions like sending a text message. +Heya is a campaign mailer for Rails. Think of it like ActionMailer, but for +timed email sequences. It can also perform other actions like sending a text +message. ## Getting started + Getting started with Heya is easy: 1. [Install the gem](#installing-the-heya-gem) @@ -13,63 +17,73 @@ Getting started with Heya is easy: 3. [Run the scheduler](#running-the-scheduler) ### Prerequisites -Heya was built to work with PostgreSQL. Pull requests are welcome to support more databases. + +Heya was built to work with PostgreSQL. Pull requests are welcome to support +more databases. ### Installing the Heya gem + 1. Add this line to your application's Gemfile: - ```ruby - gem "heya", github: "honeybadger-io/heya" - ``` + ```ruby + gem "heya", github: "honeybadger-io/heya" + ``` 2. Then execute: - ```bash - bundle install - rails generate heya:install - rails db:migrate - ``` + ```bash + bundle install + rails generate heya:install + rails db:migrate + ``` - This will: +This will: - 1. Copy Heya's migration files to *db/migrate* - 1. Copy Heya's default initializer to *config/initializers/heya.rb* - 1. Create the file *app/campaigns/application_campaign.rb* - 1. Run local migrations +1. Copy Heya's migration files to _db/migrate_ +1. Copy Heya's default initializer to _config/initializers/heya.rb_ +1. Create the file _app/campaigns/application_campaign.rb_ +1. Run local migrations -
Note: Heya doesn't store a copy of your user data; instead, it reads from your existing User model (it never writes). If you have a different user model, change the user_type configuration option in config/initializers/heya.rb. +
+Note: Heya doesn't store a copy of your user data; instead, it + reads from your existing User model (it never writes). If you + have a different user model, change the user_type configuration + option in config/initializers/heya.rb. ```ruby # config/initializers/heya.rb Heya.configure do |config| - config.user_type = "MyUser" +config.user_type = "MyUser" end ``` +
### Creating your first campaign + 1. Create a campaign: - ```bash - rails generate heya:campaign Onboarding welcome:0 - ``` + ```bash + rails generate heya:campaign Onboarding welcome:0 + ``` 2. Add a user to your campaign: - ```ruby - OnboardingCampaign.add(user) - ``` + ```ruby + OnboardingCampaign.add(user) + ``` - Add the following to your `User` model to send them the campaign - when they first signup: +Add the following to your `User` model to send them the campaign +when they first sign up: - ```ruby - after_create_commit do - OnboardingCampaign.add(self) - end - ``` +```ruby +after_create_commit do + OnboardingCampaign.add(self) +end +``` ### Running the scheduler + To start queuing emails, run the scheduler task periodically: ```bash @@ -95,7 +109,9 @@ end ### Bonus: tips for working with email in Rails -
Use MailCatcher to see emails sent from your dev environment +
+Use MailCatcher to see + emails sent from your dev environment ```ruby # config/environments/development.rb @@ -114,8 +130,11 @@ Rails.application.configure do config.action_mailer.smtp_settings = {host: "localhost", port: 1025} end ``` +
-
Use Maildown to write your emails in Markdown +
+Use Maildown to + write your emails in Markdown ```sh $ bundle add maildown @@ -127,12 +146,16 @@ $ rails generate heya:campaign Onboarding welcome ☝️ Notice how only one template was generated; Maildown automatically builds the HTML and text variants from the markdown file. +
-
Use ActionMailer::Preview to preview emails as you write them +
+Use ActionMailer::Preview + to preview emails as you write them Heya's campaign generator generates previews for campaigns at `(test|spec)/mailers/previews/*_campaign_preview.rb`. To see them, open -http://localhost:3000/rails/mailers/. If you didn't use the generator, you +. If you didn't use the generator, you can still build your own preview: ```ruby @@ -150,12 +173,13 @@ class OnboardingCampaignPreview < ActionMailer::Preview end ``` +
## Configuration You can use the following options to configure Heya (find this file in -*config/initializers/heya.rb*): +_config/initializers/heya.rb_): ```ruby Heya.configure do |config| @@ -179,7 +203,10 @@ end ## Campaigns ### Creating campaigns -Heya stores campaigns in *app/campaigns/*, similar to how Rails stores mailers in *app/mailers/*. To create a campaign, run the following command inside your Rails project: + +Heya stores campaigns in _app/campaigns/_, similar to how Rails stores mailers +in _app/mailers/_. To create a campaign, run the following command inside your +Rails project: ```bash rails generate heya:campaign Onboarding first second third @@ -187,10 +214,10 @@ rails generate heya:campaign Onboarding first second third This will: -1. Create the file *app/campaigns/onboarding_campaign.rb* -1. Create the directory *app/views/heya/campaign_mailer/onboarding_campaign/* -1. Create email templates inside of *app/views/heya/campaign_mailer/onboarding_campaign/* -1. Create an ActionMailer preview at *(test|spec)/mailers/previews/onboarding_campaign_preview.rb* +1. Create the file _app/campaigns/onboarding_campaign.rb_ +1. Create the directory _app/views/heya/campaign_mailer/onboarding_campaign/_ +1. Create email templates inside of _app/views/heya/campaign_mailer/onboarding_campaign/_ +1. Create an ActionMailer preview at _(test|spec)/mailers/previews/onboarding_campaign_preview.rb_ Here's the campaign that the above command generates: @@ -214,16 +241,26 @@ end ``` #### Steps -The `step` method defines a new step in the sequence. When you add a user to the campaign, Heya completes each step in the order that it appears. -The default time to wait between steps is *two days*, calculated from the time the user completed the previous step (or the time the user entered the campaign, in the case of the first step). +The `step` method defines a new step in the sequence. When you add a user to the +campaign, Heya completes each step in the order that it appears. -Each step has several options available (see the section [Creating messages](#creating-messages)). +The default time to wait between steps is _two days_, calculated from the time +the user completed the previous step (or the time the user entered the campaign, +in the case of the first step). + +Each step has several options available (see the section [Creating +messages](#creating-messages)). ### Creating messages -Messages are defined inside Heya campaigns using the `step` method. When you add a user to a campaign, Heya completes each step in the order that it appears. -**The most important part of each step is its name, which must be unique to the campaign.** The step's name is how Heya tracks which user has received which message, so it's essential that you don't change it after the campaign is active (if you do, Heya will assume it's a new message). +Messages are defined inside Heya campaigns using the `step` method. When you add +a user to a campaign, Heya completes each step in the order that it appears. + +**The most important part of each step is its name, which must be unique to the +campaign.** The step's name is how Heya tracks which user has received which +message, so it's essential that you don't change it after the campaign is active +(if you do, Heya will assume it's a new message). Here's an example of defining a message inside a campaign: @@ -234,9 +271,12 @@ class OnboardingCampaign < ApplicationCampaign end ``` -In the above example, Heya will send a message named `:welcome` one day after a user enters the campaign, with the subject "Welcome to my app!" +In the above example, Heya will send a message named `:welcome` one day after a +user enters the campaign, with the subject "Welcome to my app!" -The `wait` option tells Heya how long to wait before sending each message (the default is two days). There are a few scheduling options that you can customize for each step: +The `wait` option tells Heya how long to wait before sending each message (the +default is two days). There are a few scheduling options that you can customize +for each step: | Option Name | Default | Description | | :---------- | :-------------------------------- | :------------------------------------------------------- | @@ -247,16 +287,18 @@ The `wait` option tells Heya how long to wait before sending each message (the d Heya uses the following additional options to build the message itself: -| Option Name | Default | Description | -|-------------|--------------|----------------------------| -| `subject` | **required** | The email's subject | -| `from` | Heya default | The sender's email address | -| `layout` | Heya default | The email's layout file | -| `to` | See below | The recipient's name & email address | -| `bcc` | `nil` | BCC when sending emails | +| Option Name | Default | Description | +| ----------- | ------------ | -------------------------------------- | +| `subject` | **required** | The email's subject | +| `from` | Heya default | The sender's email address | +| `layout` | Heya default | The email's layout file | +| `to` | See below | The recipient's name & email address | +| `bcc` | `nil` | BCC when sending emails | | `headers` | `{}` | Headers to include when sending emails | -You can change the default options using the `default` method at the top of the campaign. Heya applies default options to each step which doesn't supply its own: +You can change the default options using the `default` method at the top of the +campaign. Heya applies default options to each step which doesn't supply its +own: ```ruby class OnboardingCampaign < ApplicationCampaign @@ -274,7 +316,8 @@ end #### Customizing the `to` field -You can customize the `to` field by passing a callable object, which Heya will invoke with the user. For instance: +You can customize the `to` field by passing a callable object, which Heya will +invoke with the user. For instance: ```ruby class OnboardingCampaign < ApplicationCampaign @@ -284,19 +327,23 @@ class OnboardingCampaign < ApplicationCampaign end ``` -It is recommended to rely on `ActionMailer::Base.email_address_with_name` so that sanitization is correctly applied. +It is recommended to rely on `ActionMailer::Base.email_address_with_name` so +that sanitization is correctly applied. If the `to` param is not provided, Heya will default to: 1. `user#first_name` 1. `user#name` -If the `user` object doesn't respond to these methods, it will fallback to a simple `user.email` in the `to` field. +If the `user` object doesn't respond to these methods, it will fallback to a +simple `user.email` in the `to` field. #### Quality control option -You may wish to apply quality control to individual steps of a campaign. For example, when adding a new step to an existing campaign it is -a good idea to inspect real-time results in production. You can do this by using the `bcc:` step option, which would look like this: +You may wish to apply quality control to individual steps of a campaign. For +example, when adding a new step to an existing campaign it is a good idea to +inspect real-time results in production. You can do this by using the `bcc:` +step option, which would look like this: ```ruby class OnboardingCampaign < ApplicationCampaign @@ -329,7 +376,9 @@ end #### Translations for email subjects (I18n) -If you don't pass a `subject` to the `step` method, Heya will try to find it in your translations. The performed lookup will use the pattern `..subject` to construct the key. +If you don't pass a `subject` to the `step` method, Heya will try to find it in +your translations. The performed lookup will use the pattern +`..subject` to construct the key. ```ruby # app/campaigns/onboarding_campaign.rb @@ -346,7 +395,8 @@ en: subject: "Heya!" ``` -To define parameters for interpolation, define a `#heya_attributes` method on your user model: +To define parameters for interpolation, define a `#heya_attributes` method on +your user model: ```ruby # app/models/user.rb @@ -390,7 +440,9 @@ Step blocks receive two optional arguments: `user` and `step`, and are processed in a background job alongside other actions. ### Adding users to campaigns -Heya leaves *when* to add users to campaigns completely up to you; here's how to add a user to a campaign from anywhere in your app: + +Heya leaves _when_ to add users to campaigns completely up to you; here's how to +add a user to a campaign from anywhere in your app: ```ruby OnboardingCampaign.add(user) @@ -402,24 +454,33 @@ To remove a user from a campaign: OnboardingCampaign.remove(user) ``` -Adding users to campaigns from Rails opens up some interesting automation possibilities--for instance, you can start or stop campaigns from `ActiveRecord` callbacks, or in response to other events that you're already tracking in your application. [See here for a list of ideas](#automation-ideas). +Adding users to campaigns from Rails opens up some interesting automation +possibilities--for instance, you can start or stop campaigns from `ActiveRecord` +callbacks, or in response to other events that you're already tracking in your +application. [See here for a list of ideas](#automation-ideas). -Because Heya stacks campaigns by default (meaning it will never send more than one at a time), you can also queue up several campaigns for a user, and they'll receive them in order: +Because Heya stacks campaigns by default (meaning it will never send more than +one at a time), you can also queue up several campaigns for a user, and they'll +receive them in order: ```ruby WelcomeCampaign.add(user) OnboardingCampaign.add(user) EvergreenCampaign.add(user) ``` -*Note: you can customize the priority of campaigns via Heya's configuration.* -If you want to send a user two campaigns simultaneously, you can do so with the `concurrent` option: +_Note: you can customize the priority of campaigns via Heya's configuration._ + +If you want to send a user two campaigns simultaneously, you can do so with the +`concurrent` option: ```ruby FlashSaleCampaign.add(user, concurrent: true) ``` -When you remove a user from a campaign and add them back later, they'll continue where they left off. If you want them to start over from the beginning, use the `restart` option: +When you remove a user from a campaign and add them back later, they'll continue +where they left off. If you want them to start over from the beginning, use the +`restart` option: ```ruby TrialConversionCampaign.add(user, restart: true) @@ -427,7 +488,8 @@ TrialConversionCampaign.add(user, restart: true) #### Automation ideas -Using `ActiveSupport::Notifications` to respond to lifecycle events (which could be sent from your Stripe controller, for instance): +Using `ActiveSupport::Notifications` to respond to lifecycle events (which could +be sent from your Stripe controller, for instance): ```ruby ActiveSupport::Notifications.subscribe("user.trial_will_end") do |*args| @@ -452,7 +514,10 @@ end ``` ### Customizing who gets what -Heya can send individual messages to certain users using the `segment` option. The following campaign will send the message to inactive users--active users will be skipped: + +Heya can send individual messages to certain users using the `segment` option. +The following campaign will send the message to inactive users--active users +will be skipped: ```ruby class ActivationCampaign < ApplicationCampaign @@ -460,7 +525,8 @@ class ActivationCampaign < ApplicationCampaign end ``` -When you're checking the value of a single method on the user, the segment can be simplified to the symbol version: +When you're checking the value of a single method on the user, the segment can +be simplified to the symbol version: ```ruby class ActivationCampaign < ApplicationCampaign @@ -469,7 +535,11 @@ end ``` #### Segmenting specific campaigns -You can also narrow entire campaigns to certain users using the `segment` method. For instance, if you have a campaign with a specific goal such as performing an action in your app, then you can send the campaign only to the users who haven't performed the action: + +You can also narrow entire campaigns to certain users using the `segment` +method. For instance, if you have a campaign with a specific goal such as +performing an action in your app, then you can send the campaign only to the +users who haven't performed the action: ```ruby class UpgradeCampaign < ApplicationCampaign @@ -481,9 +551,12 @@ class UpgradeCampaign < ApplicationCampaign end ``` -If they upgrade half way through the campaign, Heya will stop sending messages and remove them from the campaign. +If they upgrade half way through the campaign, Heya will stop sending messages +and remove them from the campaign. -Likewise, you can require that users meet conditions to continue receiving a campaign. Here's a campaign which sends messages only to trial users--non-trial users will be removed from the campaign: +Likewise, you can require that users meet conditions to continue receiving a +campaign. Here's a campaign which sends messages only to trial users--non-trial +users will be removed from the campaign: ```ruby class TrialCampaign < ApplicationCampaign @@ -496,6 +569,7 @@ end ``` #### Segmenting all campaigns + Heya campaigns inherit options from parent campaigns. For example, to make sure unsubscribed users never receive an email from Heya, create a `segment` in the `ApplicationCampaign`, and then have all other campaigns inherit from it: @@ -529,14 +603,14 @@ for additional details. ### Extending Campaign Mailers with Macros -The campaign generator does not create a Mailer class for campaigns. In order to enhance a campaign -with a macro from another gem (such as for adding analytics), you can do so by extending the -`Heya::ApplicationMailer` class. +The campaign generator does not create a Mailer class for campaigns. In order to +enhance a campaign with a macro from another gem (such as for adding analytics), +you can do so by extending the `Heya::ApplicationMailer` class. - Create a new file at `app/mailers/heya/application_mailer.rb` - Add the following to it: -``` +```ruby module Heya class ApplicationMailer < ActionMailer::Base macro_to_add_to_all_campaign_mailers @@ -545,71 +619,111 @@ end ``` ### Campaigns FAQ + **What happens when:** +
I reorder messages in an active campaign? -Heya sends the next *unsent* message *after the last message the user received*. When you move a message, the users who last received it will be moved with it, and continue from that point in the campaign. Heya skips messages which the user has already seen. +Heya sends the next _unsent_ message _after the last message the user received_. +When you move a message, the users who last received it will be moved with it, +and continue from that point in the campaign. Heya skips messages which the user +has already seen. +
I add a message to an active campaign? -Users who have already received a message *after* the new message will *not* receive the message. +Users who have already received a message _after_ the new message will _not_ +receive the message. +
I remove a message from an active campaign? -Users who last received the message will be moved up to the previously received message, and continue from that point in the campaign. Heya skips messages which the user has already seen. +Users who last received the message will be moved up to the previously received +message, and continue from that point in the campaign. Heya skips messages which +the user has already seen. +
I rename a message in an active campaign? -**Renaming a message is equivalent to removing the message and adding a new one.** Users who are waiting to receive an earlier message in the campaign will receive the new message. Users who last received the old message will also receive the new one since it has replaced its position in the campaign. +**Renaming a message is equivalent to removing the message and adding a new +one.** Users who are waiting to receive an earlier message in the campaign will +receive the new message. Users who last received the old message will also +receive the new one since it has replaced its position in the campaign. +
A user skips a message based on its conditions? -Heya waits the defined wait time for every message in the campaign. If a user doesn't match the conditions, Heya skips it. If the *next* message's wait time is less than or equal to the skipped message's, it sends it immediately. If the next wait period is longer, it sends it after the new wait time has elapsed. +Heya waits the defined wait time for every message in the campaign. If a user +doesn't match the conditions, Heya skips it. If the _next_ message's wait time +is less than or equal to the skipped message's, it sends it immediately. If the +next wait period is longer, it sends it after the new wait time has elapsed. +
I delete an active campaign? -Heya will immediately stop sending the campaign; the campaign's data will remain until you manually delete it. If you restore the file before deleting the campaign's data, Heya will resume sending the campaign. +Heya will immediately stop sending the campaign; the campaign's data will remain +until you manually delete it. If you restore the file before deleting the +campaign's data, Heya will resume sending the campaign. +
I add a user to multiple campaigns? -By default, Heya sends each user one campaign at a time. It determines the order of campaigns using the campaign `priority`. When you add a user to a higher priority campaign, the new campaign will begin immediately. Once completed, the next highest priority campaign will resume sending. +By default, Heya sends each user one campaign at a time. It determines the order +of campaigns using the campaign `priority`. When you add a user to a higher +priority campaign, the new campaign will begin immediately. Once completed, the +next highest priority campaign will resume sending. To send a campaign concurrent to other active campaigns, use the `concurrent` option. +
I add a user to a campaign they already completed? -When you add a user to a campaign that they previously completed, Heya sends new messages which were added *to the end of the campaign*. Skipped messages will *not* be sent. To resend all messages, use the `restart` option. +When you add a user to a campaign that they previously completed, Heya sends new +messages which were added _to the end of the campaign_. Skipped messages will +_not_ be sent. To resend all messages, use the `restart` option. +
**Less frequently asked questions:** +
Can the same message be delivered twice? -Nope, not without restarting the campaign using the `restart` option (which will resend all the messages). +Nope, not without restarting the campaign using the `restart` option (which will +resend all the messages). +
Can the same campaign be sent twice? -Yep. When you add a user to a campaign that they previously completed, Heya sends new messages which were added *to the end of the campaign*. Skipped messages will *not* be sent. To resend all messages, use the `restart` option. +Yep. When you add a user to a campaign that they previously completed, Heya +sends new messages which were added _to the end of the campaign_. Skipped +messages will _not_ be sent. To resend all messages, use the `restart` option. +
Can I resend a campaign to a user? -Yep. Use the `restart` option to resend a campaign to a user (if they are already in the campaign, the campaign will start over from the beginning). +Yep. Use the `restart` option to resend a campaign to a user (if they are +already in the campaign, the campaign will start over from the beginning). +
Can I send a user two campaigns at the same time? -Yep. By default, Heya sends campaigns ain order of `priority`. Use the `concurrent` option to send campaigns concurrently. +Yep. By default, Heya sends campaigns ain order of `priority`. Use the +`concurrent` option to send campaigns concurrently. +
## Upgrading Heya + Heya adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), and should be considered **unstable** until version 1.0.0. Always check [CHANGELOG.md](./CHANGELOG.md) prior to upgrading (breaking changes will always @@ -617,18 +731,21 @@ be called out there). Upgrade instructions for breaking changes are in [UPGRADING.md](./UPGRADING.md). ## Roadmap + See [here](https://github.com/honeybadger-io/heya/projects/1) for things we're considering adding to Heya. ## Contributing + 1. Fork it. 2. Create a topic branch `git checkout -b my_branch` -3. Make your changes and add an entry to [CHANGELOG.md](CHANGELOG.md). +3. Make your changes and add an entry to [CHANGELOG.md](./CHANGELOG.md). 4. Commit your changes `git commit -am "Boom"` 5. Push to your branch `git push origin my_branch` 6. Send a [pull request](https://github.com/honeybadger-io/heya/pulls) ## Releasing + 1. `gem install gem-release` 2. `gem bump -v [version] -t -r` 3. Update unreleased heading in [CHANGELOG.md](./CHANGELOG.md) (TODO: automate @@ -636,4 +753,5 @@ considering adding to Heya. 4. `git push origin main --tags` ## License + Heya is licensed under the [LGPL](./LICENSE).