Published by Dan Cunning on Feb 21, 2015

Email Digests

Instead of emailing users on every action, combine all notifications over a certain period of time into a single email.

This article builds on top of the Mailing Lists functionality.

Email digests contain all messages since the previous digest. We'll generate a daily digest using the whenever gem which manages scheduled tasks defined inside config/schedule.rb

# app/mailers/groups_mailer.rb
class GroupsMailer < ApplicationMailer
  def digest(membership, timeframe)
    @membership = membership
    @group = membership.group
    @messages = @group.messages.where(created_at: timeframe).order(:discussion_id, :created_at)

    mail(
      to: @membership.user.email,
      from: %("#{@group.name}" <#{@group.email}>),
      subject: "[#{@group.name}] Message Digest",
    )
  end
end
<!-- app/views/groups_mailer/digest.html.erb -->
<%= @messages.each do |message| %>
  <p>Subject: <%= message.discussion.subject %></p>
  <p>From: <%= message.from.name %></p>
  <p>Timestamp: <%= message.created_at.to_fs(:timestamp) %></p>
  <%= simple_format message.content %>
  <hr>
<% end %>

<p><%= link_to 'Unsubscribe', unsubscribe_url(id: @membership.token) %></p>
# lib/tasks/groups.rake
namespace :groups do
  namespace :digest do
    task send: :environment do
      Groups.find_each do |group|
        now = Time.zone.now
        last_sent = group.digest_last_sent_at || (now - 1.week)
        timeframe = (last_sent..now)

        next if group.messages.where(created_at: timeframe).empty?

        group.memberships.where(receives_digest: true).find_each do |membership|
          GroupsMailer.digest(membership, timeframe).deliver_later
        end

        group.digest_last_sent_at = now
        group.save(validate: false)
      end
    end
  end
end
# config/schedule.rb
every 1.day, at: "4:30 am" do
  rake "groups:digest:send"
end