sdbondi
8/28/2011 - 4:14 AM

Customizing ActionMailer delivery methods

Customizing ActionMailer delivery methods

module ProjectName
  class QueuedDeliveryMethod
    def deliver!(mail)
      # we're actually tracking the mail in the DB, but you can store it anywhere ready for passing to Resque
      # if you don't care about analyzing your sent email data, and you're using delayed_job, this would be simpler
      message = QueuedEmail.create(
        :subject           => mail.subject,
        :body              => mail.body,
        :recipient_address => mail.to.first,
        :recipient_name    => mail[:to].display_names.first,
        :sender_address    => mail.from.first,
        :sender_name       => mail[:from].display_names.first,
        :reply_to_address  => mail.reply_to ? mail.reply_to.first : nil,
        :reply_to_name     => mail.reply_to ? mail[:reply_to].display_names.first : nil
      )
      
      # once it's written somewhere internal to the app, tell Resque to process the sending in the background
      Resque.enqueue(DeliverEmailJob, :message_id => message.id)
    end
  end
end
class QueueDeliveryMailer < ActionMailer::Base
  # override the delivery method with the outbound delivery method you config'd earlier
  self.delivery_method = Rails.application.config.mail_queue_outbound_delivery_method
  layout nil

  def original_email(email)
    # This is just creating the same email from the DB record that was saved for queueing
    mail(:from => email.formatted_sender,
         :to => email.formatted_recipient,
         :reply_to => email.formatted_reply_to,
         :subject => email.subject) do |format|
      format.text { render :text => email.body }
    end
  end
end
# in config/environments/production.rb (or other environment)

  config.action_mailer.delivery_method = :queued # make the default delivery_method your queue
  config.mail_queue_outbound_delivery_method = :smtp # define new config option that specifies how to really deliver mail (used later)
  config.action_mailer.smtp_settings = { # set the settings for whatever outbound delivery method you're going to use
    :address   => "smtp.sendgrid.net",
    :port      => 25,
    :domain    => "site.com",
    :user_name => "sendgrid@site.com",
    :password  => "smtp-password"
  }
# in config/initializers/mailer.rb, add a custom delivery_method to ActionMailer
ActionMailer::Base.add_delivery_method :queued, ProjectName::QueuedDeliveryMethod
# Resque job to do the true outbound sending
class DeliverEmailJob
  include ProjectName::Job::Logging

  @queue = :mail_queue

  def self.perform(args)
    message = QueuedEmail.get!(args["message_id"])
    
    logger.info("Delivering (%s) to %s" % [message.subject, message.formatted_recipient])

    # This is the important bit, QueueDeliveryMailer rebuilds the email, but overrides the delivery method (see file)
    # Again, using delayed_job, this could be simplified as you could just receive the original Mail::Message
    QueueDeliveryMailer.original_email(message).deliver

    logger.info("Mail delivered to %s" % [message.formatted_recipient])
  end
end