Project

moist

0.0
The project is in a healthy, maintained state
Opinionated drip campaign framework.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
 Project Readme

Moist

Drip engine, for Ruby on Rails.

Why the name?

Because drip was taken and moist was available.

There is also a measurable amount of people who hate the word moist. Which results in a poor adoption strategy. In turn, fewer stars. Conclusion: recruiters won't bother me?

Goals?

Should you use this?

Maybe, here's the gist:

# Create a campaign:
::Moist::Campaign.create(name: "Abandoned cart", slug: "cart")

# Add `moist` to mailer:
class AbandonedCartMailer < ActionMailer::Base
  moist :you_forgot_something, campaign: :cart, step: 1, delay: 1.hour 
  def you_forgot_something(cart)
    # ... 
  end
  
  moist :selling_out_soon, campaign: :cart, step: 2, delay: 4.hours 
  def selling_out_soon(cart)
    # ... 
  end
end 

# Add to a drip campaign:
::Moist::Campaign.subscribe(cart, user: cart.user).to(:cart)

# Remove from a drip campaign:
::Moist::Campaign.unsubscribe(cart, user: cart.user).from(:cart)

Installation

Add this line to your application's Gemfile:

gem 'moist'

And then execute:

$ bundle

Bring in the initializer and migrations:

$ rails g moist:setup

And do the migrate:

$ rails db:migrate 

Setup Moist

How to use Moist to create an abandoned cart drip campaign.

1. First, create a Moist::Campaign.

Moist::Campaign.create(name: "Abandoned cart, slug: "cart")`

The slug used here will be referenced later. Note it.

2. Use moist in your mailer to create steps.

You'll need to define @moist_subscriber and @moist_user in each mailer. This tells Moist what Moist::Mailing to associate with the mailer.

class AbandonedCartMailer < ActionMailer::Base
  moist :you_forgot_something, campaign: :cart, step: 1, delay: 1.hour 
  def you_forgot_something(cart)
    @cart = cart 
    @moist_subscriber = cart 
    @moist_user = cart.user 
    mail(subject: "You forgot something...", to: @cart.user.email)
  end

  moist :free_shipping_if_you_order_now, campaign: :cart, step: 2, delay: 2.days
  def free_shipping_if_you_order_now(cart)
    @cart = cart 
    @moist_subscriber = cart 
    @moist_user = cart.user 
    mail(subject: "Free shipping? Yep. Complete your purchase right meow!", to: @cart.user.email)
  end
end 

In these mailer methods, make sure you assign a @moist_subscriber and a @moist_user. If you don't, bad things will happen.

3. Add @cart to the campaign

Probably a background job.

class AbandonedCartJob < ApplicationJob
  TIME_TO_LIVE = 1.hour 
  def perform
    Cart.where(checked_out: false).where('updated_at < ?', TIME_TO_LIVE.ago).each do |cart|
      ::Moist::Campaign.subscribe(cart, user: cart.user).to(:cart)
    end 
  end 
end 

4. Run the scheduler

This manages the mailers and the drips.

class MoistSchedulerJob < ApplicationJob
  def perform
    ::Moist::Scheduler.run 
  end
end 

5. Done!

All done. Some nice-to-haves:

class Cart 
  has_moist_campaigns
end

Gives you

@cart.moist_campaigns
class User 
  acts_as_moist_user
end 

Gives you:

@user.moist_subscriptions

Data models

Three core concepts:

Campaign

This is just an object for reference.

CampaignSubscriber

This joins a Campaign to a subscriber (polymorphic anything), and a user. In theory, a user can have many moist_campaign_subscribers, which would relate it to objects that belong to a user.

This is useful if you have an Order object and a Subscription object that you want to create different campaigns for.

Mailing

Takes your ActionMailer moist calls and turns them into database records. They have send_at and sent_at columns, in addition to information about the mailers they belong to.

Contributing

Just do it.

Todo

  • Logo, duh
  • Web UI examples
  • Ability to pause a campaign?
  • Conversion stuff
  • Handle updating steps
  • Use block for handling delay

License

The gem is available as open source under the terms of the MIT License.