2018-12-14 05:06:12 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2018-09-18 19:26:30 -04:00
|
|
|
require "active_support/rescuable"
|
2018-09-19 18:52:16 -04:00
|
|
|
|
2018-09-28 15:19:43 -04:00
|
|
|
require "action_mailbox/callbacks"
|
|
|
|
require "action_mailbox/routing"
|
2018-09-18 19:26:30 -04:00
|
|
|
|
2018-12-12 20:13:16 -05:00
|
|
|
# The base class for all application mailboxes. Not intended to be inherited from directly. Inherit from
|
|
|
|
# `ApplicationMailbox` instead, as that's where the app-specific routing is configured. This routing
|
|
|
|
# is specified in the following ways:
|
|
|
|
#
|
|
|
|
# class ApplicationMailbox < ActionMailbox::Base
|
|
|
|
# # Any of the recipients of the mail (whether to, cc, bcc) are matched against the regexp.
|
2018-12-19 13:01:33 -05:00
|
|
|
# routing /^replies@/i => :replies
|
2018-12-12 20:13:16 -05:00
|
|
|
#
|
|
|
|
# # Any of the recipients of the mail (whether to, cc, bcc) needs to be an exact match for the string.
|
2018-12-19 13:01:33 -05:00
|
|
|
# routing "help@example.com" => :help
|
2018-12-12 20:13:16 -05:00
|
|
|
#
|
|
|
|
# # Any callable (proc, lambda, etc) object is passed the inbound_email record and is a match if true.
|
2018-12-19 13:01:33 -05:00
|
|
|
# routing ->(inbound_email) { inbound_email.mail.to.size > 2 } => :multiple_recipients
|
2018-12-12 20:13:16 -05:00
|
|
|
#
|
|
|
|
# # Any object responding to #match? is called with the inbound_email record as an argument. Match if true.
|
2018-12-19 13:01:33 -05:00
|
|
|
# routing CustomAddress.new => :custom
|
2018-12-12 20:13:16 -05:00
|
|
|
#
|
|
|
|
# # Any inbound_email that has not been already matched will be sent to the BackstopMailbox.
|
2018-12-19 13:01:33 -05:00
|
|
|
# routing :all => :backstop
|
2018-12-12 20:13:16 -05:00
|
|
|
# end
|
|
|
|
#
|
|
|
|
# Application mailboxes need to overwrite the `#process` method, which is invoked by the framework after
|
|
|
|
# callbacks have been run. The callbacks available are: `before_processing`, `after_processing`, and
|
|
|
|
# `around_processing`. The primary use case is ensure certain preconditions to processing are fulfilled
|
|
|
|
# using `before_processing` callbacks.
|
|
|
|
#
|
2018-12-19 13:01:33 -05:00
|
|
|
# If a precondition fails to be met, you can halt the processing using the `#bounced!` method,
|
2018-12-12 20:13:16 -05:00
|
|
|
# which will silently prevent any further processing, but not actually send out any bounce notice. You
|
|
|
|
# can also pair this behavior with the invocation of an Action Mailer class responsible for sending out
|
|
|
|
# an actual bounce email. This is done using the `#bounce_with` method, which takes the mail object returned
|
|
|
|
# by an Action Mailer method, like so:
|
|
|
|
#
|
|
|
|
# class ForwardsMailbox < ApplicationMailbox
|
|
|
|
# before_processing :ensure_sender_is_a_user
|
|
|
|
#
|
|
|
|
# private
|
|
|
|
# def ensure_sender_is_a_user
|
|
|
|
# unless User.exist?(email_address: mail.from)
|
|
|
|
# bounce_with UserRequiredMailer.missing(inbound_email)
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# During the processing of the inbound email, the status will be tracked. Before processing begins,
|
2018-12-19 13:01:33 -05:00
|
|
|
# the email will normally have the `pending` status. Once processing begins, just before callbacks
|
2018-12-12 20:13:16 -05:00
|
|
|
# and the `#process` method is called, the status is changed to `processing`. If processing is allowed to
|
|
|
|
# complete, the status is changed to `delivered`. If a bounce is triggered, then `bounced`. If an unhandled
|
|
|
|
# exception is bubbled up, then `failed`.
|
|
|
|
#
|
|
|
|
# Exceptions can be handled at the class level using the familiar `Rescuable` approach:
|
|
|
|
#
|
|
|
|
# class ForwardsMailbox < ApplicationMailbox
|
2018-12-19 13:01:33 -05:00
|
|
|
# rescue_from(ApplicationSpecificVerificationError) { bounced! }
|
2018-12-12 20:13:16 -05:00
|
|
|
# end
|
2018-09-28 15:19:43 -04:00
|
|
|
class ActionMailbox::Base
|
2018-09-19 18:52:16 -04:00
|
|
|
include ActiveSupport::Rescuable
|
2018-09-28 15:19:43 -04:00
|
|
|
include ActionMailbox::Callbacks, ActionMailbox::Routing
|
2018-09-18 19:26:30 -04:00
|
|
|
|
2018-09-19 18:52:16 -04:00
|
|
|
attr_reader :inbound_email
|
2018-10-06 22:15:36 -04:00
|
|
|
delegate :mail, :delivered!, :bounced!, to: :inbound_email
|
2018-09-17 20:49:47 -04:00
|
|
|
|
2018-10-01 08:16:10 -04:00
|
|
|
delegate :logger, to: ActionMailbox
|
|
|
|
|
2018-09-19 18:52:16 -04:00
|
|
|
def self.receive(inbound_email)
|
|
|
|
new(inbound_email).perform_processing
|
2018-09-17 20:49:47 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def initialize(inbound_email)
|
|
|
|
@inbound_email = inbound_email
|
|
|
|
end
|
|
|
|
|
2018-09-18 19:31:16 -04:00
|
|
|
def perform_processing
|
2018-11-16 13:24:51 -05:00
|
|
|
track_status_of_inbound_email do
|
|
|
|
run_callbacks :process do
|
2018-09-19 19:54:49 -04:00
|
|
|
process
|
|
|
|
end
|
2018-09-18 19:42:38 -04:00
|
|
|
end
|
2018-09-18 19:26:30 -04:00
|
|
|
rescue => exception
|
2018-10-17 13:22:52 -04:00
|
|
|
# TODO: Include a reference to the inbound_email in the exception raised so error handling becomes easier
|
2018-09-18 19:26:30 -04:00
|
|
|
rescue_with_handler(exception) || raise
|
|
|
|
end
|
|
|
|
|
2018-09-17 20:49:47 -04:00
|
|
|
def process
|
2018-09-18 19:26:30 -04:00
|
|
|
# Overwrite in subclasses
|
2018-09-17 20:49:47 -04:00
|
|
|
end
|
2018-10-01 08:16:10 -04:00
|
|
|
|
2018-10-06 22:15:36 -04:00
|
|
|
def finished_processing?
|
|
|
|
inbound_email.delivered? || inbound_email.bounced?
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2018-10-03 15:14:28 -04:00
|
|
|
def bounce_with(message)
|
|
|
|
inbound_email.bounced!
|
|
|
|
message.deliver_later
|
|
|
|
end
|
|
|
|
|
2018-09-19 19:54:49 -04:00
|
|
|
private
|
|
|
|
def track_status_of_inbound_email
|
|
|
|
inbound_email.processing!
|
|
|
|
yield
|
2018-09-21 19:44:48 -04:00
|
|
|
inbound_email.delivered! unless inbound_email.bounced?
|
2018-10-17 12:42:54 -04:00
|
|
|
rescue
|
2018-09-19 19:54:49 -04:00
|
|
|
inbound_email.failed!
|
|
|
|
raise
|
|
|
|
end
|
2018-09-17 20:01:52 -04:00
|
|
|
end
|