Create an Email Drip Campaign Using Rails Vault

This article is part of Build a SaaS with Rails and was previously published on Rails Designer I recently published the Rails Vault gem. The most obvious (and common) use case is to store settings and preferences for users and workspaces (or teams/accounts). It is how I use it often as well, but recently I built a SaaS for someone that needed a basic email drip campaign (or “automated email campaign”, “lifecycle emails” or “nurture campaign”). I have found Rails Vault can work great for this too. I want to share the lowdown of it. I assume you have installed the gem already. Create the vault That is easy! rails generate rails_vault:add User::EmailDripper \ welcome_email_sent_at:datetime \ engagement_email_sent_at:datetime \ reminder_email_sent_at:datetime This will create a vault at app/models/user/email_dripper.rb: # app/models/user/email_dripper.rb class User::EmailDripper

Apr 2, 2025 - 18:24
 0
Create an Email Drip Campaign Using Rails Vault

This article is part of Build a SaaS with Rails and was previously published on Rails Designer

I recently published the Rails Vault gem. The most obvious (and common) use case is to store settings and preferences for users and workspaces (or teams/accounts). It is how I use it often as well, but recently I built a SaaS for someone that needed a basic email drip campaign (or “automated email campaign”, “lifecycle emails” or “nurture campaign”).

I have found Rails Vault can work great for this too. I want to share the lowdown of it. I assume you have installed the gem already.

Create the vault

That is easy!

rails generate rails_vault:add User::EmailDripper \
  welcome_email_sent_at:datetime \
  engagement_email_sent_at:datetime \
  reminder_email_sent_at:datetime

This will create a vault at app/models/user/email_dripper.rb:

# app/models/user/email_dripper.rb
class User::EmailDripper < RailsVault::Base
  vault_attribute :welcome_email_sent_at, :datetime
  vault_attribute :engagement_email_sent_at, :datetime
  vault_attribute :reminder_email_sent_at, :datetime
end

I want to store when an email is sent to the user so they don't get flooded with the same email. Here I want to send three different emails: “welcome_email”, “engagement_email” and “reminder_email”. You can name them however you want.

Email Drip Logic

Next is a class that holds all the logic to determine which email to send to which user.

# app/models/email_dripper.rb
class EmailDripper
  STEPS = {
    2.hours => :welcome_email,
    24.hours => :engagement_email,
    72.hours => :reminder_email
  }

  RECENT_SIGNUP_THRESHOLD = 72.hours

  class << self
    def call
      User.where(created_at: RECENT_SIGNUP_THRESHOLD.ago..).find_each { process(it) }
    end

    private

    def process(user)
      return if user.email_dripper.blank?

      STEPS.each { process_step_for(user, _1, _2) }
    end

    def process_step_for(user, delay, email)
      scheduled_time = user.created_at + delay

      return if scheduled_time.future?
      return if already_sent? email, to: user

      DripMailer.public_send(identifier, user).deliver_now
    end

    def already_sent?(email, to:) = to.email_dripper&.public_send(email).present?
  end
end

The STEPS hash defines a sequence of three emails scheduled at 2, 24, and 72 hours after signup. Feel free to tweak to make sense for your product. The RECENT_SIGNUP_THRESHOLD constant sets a 72-hour window for considering users as recent signups, just to limit the users needed to query. The call method processes all users who signed up within the recent threshold.

Then the process method checks if a user has email dripping enabled and iterates through the campaign's “steps”. The process_step_for method handles individual email steps by checking if it's time to send and hasn't been sent before. The already_sent? method verifies if a specific campaign email has already been sent to the user (see how I like my code to be readable?). The actual email sending is handled through DripMailer using Ruby's public_send.

Then create the the DripMailer class:

# app/mailers/drip_mailer.rb
class DripMailer < ApplicationMailer
  def welcome_email(user)
    mail(
      to: user.email,
      subject: "Welcome to Rails Designer!"
    )
  end

  def engagement_email(user)
    #…
  end

  def reminder_email(user)
    #…
  end
end

Don't forget to create the corresponding views as well:

app/views/drip_mailer/
  ├── welcome_email.html.erb
  ├── engagement_email.html.erb
  └── reminder_email.html.erb

Finally make sure to run EmailDripper.call at a regular interval using a cron. How that is done depends on your app's setup.

And there you have it: an easy-to-use and expandable way to send an email drip campaign to your Rails app's users.