123456789_123456789_123456789_123456789_123456789_

Class: ActionMailbox::Base

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Instance Chain:
Inherits: Object
Defined in: actionmailbox/lib/action_mailbox/base.rb

Overview

====================

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. ::String and regexp routes match the recipients of the mail: the addresses in its To, Cc, Bcc, X-Original-To, and X-Forwarded-To headers.

class ApplicationMailbox < <code>Base</code>
  # Any of the recipients of the mail are matched against the regexp.
  routing /^replies@/i => :replies

  # Any of the recipients of the mail needs to be an exact match for the string.
  routing "help@example.com" => :help

  # Any callable (proc, lambda, etc) object is passed the inbound_email record and is a match if true.
  routing ->(inbound_email) { inbound_email.mail.to.size > 2 } => :multiple_recipients

  # Any object responding to #match? is called with the inbound_email record as an argument. Match if true.
  routing CustomAddress.new => :custom

  # Any inbound_email that has not been already matched will be sent to the BackstopMailbox.
  routing :all => :backstop
end

Routing is not authentication. The sender of an email can set every header that string and regexp routes match, so a mailbox that acts on behalf of a user or an account must check that the email is allowed to do so.

Application mailboxes need to override 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 to ensure that certain preconditions to processing are fulfilled using before_processing callbacks.

If a precondition fails to be met, you can halt the processing using the #bounced! method, 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.exists?(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, the email will normally have the pending status. Once processing begins, just before callbacks 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 ::ActiveSupport::Rescuable approach:

class ForwardsMailbox < ApplicationMailbox
  rescue_from(ApplicationSpecificVerificationError) { bounced! }
end

Constant Summary

::ActiveSupport::Callbacks - Included

CALLBACK_FILTER_TYPES

Callbacks - Included

TERMINATOR

Routing - Attributes & Methods

::ActiveSupport::Rescuable - Attributes & Methods

Class Method Summary

Instance Attribute Summary

Callbacks - Included

Instance Method Summary

::ActiveSupport::Callbacks - Included

#run_callbacks

Runs the callbacks for the given event.

#halted_callback_hook

A hook invoked every time a before callback is halted.

::ActiveSupport::Rescuable - Included

#rescue_with_handler

Delegates to the class method, but uses the instance as the subject for rescue_from handlers (method calls, instance_exec blocks).

#handler_for_rescue

Internal handler lookup.

Constructor Details

.new(inbound_email) ⇒ Base

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 90

def initialize(inbound_email)
  @inbound_email = inbound_email
end

Class Attribute Details

.rescue_handlers (rw)

[ GitHub ]

  
# File 'activesupport/lib/active_support/rescuable.rb', line 16

class_attribute :rescue_handlers, default: [].freeze

.rescue_handlers? ⇒ Boolean (rw)

[ GitHub ]

  
# File 'activesupport/lib/active_support/rescuable.rb', line 16

class_attribute :rescue_handlers, default: [].freeze

.router (rw)

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/routing.rb', line 9

class_attribute :router, default: ActionMailbox::Router.new

.router? ⇒ Boolean (rw)

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/routing.rb', line 9

class_attribute :router, default: ActionMailbox::Router.new

Class Method Details

.receive(inbound_email)

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 86

def self.receive(inbound_email)
  new(inbound_email).perform_processing
end

Instance Attribute Details

#finished_processing? ⇒ Boolean (readonly)

This method is for internal use only.
[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 111

def finished_processing? # :nodoc:
  inbound_email.delivered? || inbound_email.bounced?
end

#inbound_email (readonly)

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 81

attr_reader :inbound_email

#logger (readonly)

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 84

delegate :logger, to: ActionMailbox

#mail (readonly)

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 82

delegate :mail, :delivered!, :bounced!, to: :inbound_email

#rescue_handlers (rw)

[ GitHub ]

  
# File 'activesupport/lib/active_support/rescuable.rb', line 16

class_attribute :rescue_handlers, default: [].freeze

#rescue_handlers? ⇒ Boolean (rw)

[ GitHub ]

  
# File 'activesupport/lib/active_support/rescuable.rb', line 16

class_attribute :rescue_handlers, default: [].freeze

#router (rw)

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/routing.rb', line 9

class_attribute :router, default: ActionMailbox::Router.new

#router? ⇒ Boolean (rw)

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/routing.rb', line 9

class_attribute :router, default: ActionMailbox::Router.new

Instance Method Details

#bounce_now_with(message)

Immediately sends the given message and changes the inbound email's status to :bounced.

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 122

def bounce_now_with(message)
  inbound_email.bounced!
  message.deliver_now
end

#bounce_with(message)

Enqueues the given message for delivery and changes the inbound email's status to :bounced.

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 116

def bounce_with(message)
  inbound_email.bounced!
  message.deliver_later
end

#bounced!

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 82

delegate :mail, :delivered!, :bounced!, to: :inbound_email

#delivered!

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 82

delegate :mail, :delivered!, :bounced!, to: :inbound_email

#instrumentation_payload (private)

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 128

def instrumentation_payload
  {
    mailbox: self,
    inbound_email: inbound_email.instrumentation_payload
  }
end

#perform_processing

This method is for internal use only.
[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 94

def perform_processing # :nodoc:
  ActiveSupport::Notifications.instrument "process.action_mailbox", instrumentation_payload do
    track_status_of_inbound_email do
      run_callbacks :process do
        process
      end
    end
  rescue => exception
    # TODO: Include a reference to the inbound_email in the exception raised so error handling becomes easier
    rescue_with_handler(exception) || raise
  end
end

#process

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 107

def process
  # Override in subclasses
end

#track_status_of_inbound_email (private)

[ GitHub ]

  
# File 'actionmailbox/lib/action_mailbox/base.rb', line 135

def track_status_of_inbound_email
  inbound_email.processing!
  yield
  inbound_email.delivered! unless inbound_email.bounced?
rescue
  inbound_email.failed!
  raise
end