Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Boulder Ruby: Attraction Mailbox - Why I Love A...

Cody Norman
June 06, 2024
20

Boulder Ruby: Attraction Mailbox - Why I Love Action Mailbox

Early version of my talk Attraction Mailbox that was presented at Boulder Ruby and Philly.rb

Cody Norman

June 06, 2024
Tweet

Transcript

  1. Introduction What we’ll be covering • What is Action Mailbox?

    • How to get started. • Sending inbound emails to your application. • How inbound emails are processed. • Deploying and running on production.
  2. Action Mailbox Background What is it? Action Mailbox routes incoming

    emails to controller-like mailboxes for processing in Rails. The inbound emails are turned into `InboundEmail` records using Active Record and feature life cycle tracking These inbound emails are routed asynchronously using Active Job to one or several dedicated mailboxes, which are capable of interacting directly with the rest of your domain model.
  3. Getting Started • Run generators to install • ApplicationMailbox routes

    the email to speci fi c mailbox • Routing inbound emails • Sending inbound emails to your development environment
  4. Getting Started What’s added bin/rails action_mailbox:install Copying application_mailbox.rb to app/mailboxes

    create app/mailboxes/application_mailbox.rb rails railties:install:migrations FROM=active_storage,action_mailbox Copied migration 20240123213529_create_action_mailbox_tables.action_mailbox.rb from action_mailbox
  5. Getting Started Generated Migrations # This migration comes from action_mailbox

    (originally 20180917164000) class CreateActionMailboxTables < ActiveRecord::Migration[6.0] def change create_table :action_mailbox_inbound_emails do |t| t.integer :status, default: 0, null: false t.string :message_id, null: false t.string :message_checksum, null: false t.timestamps t.index [ :message_id, :message_checksum ], name: "index_action_mailbox_inbound_emails_uniqueness", unique: true end end end
  6. Getting Started Email Routing Tips # app/mailboxes/application_mailbox.rb class ApplicationMailbox <

    ActionMailbox::Base routing all: :support # routing(/support\./i => :support) end
  7. Getting Started Generating your fi rst Mailbox bin/rails generate mailbox

    support class SupportMailbox < ApplicationMailbox def process # email processing logic end end
  8. Rails Conductor Sending Inbound Emails Two options for sending inbound

    email: • By form • Send Email by Raw Source
  9. ActionMailbox::InboundEmail What get’s created class InboundEmail < Record self.table_name =

    "action_mailbox_inbound_emails" include Incineratable, MessageId, Routable has_one_attached :raw_email, service: ActionMailbox.storage_service enum status: %i[ pending processing delivered failed bounced ] def mail @mail ||= Mail.from_source(source) end def source @source ||= raw_email.download end def processed? delivered? || failed? || bounced? end end end
  10. ActionMailbox::InboundEmail What get’s created enum status: %i[ pending processing delivered

    failed bounced ] def mail @mail ||= Mail.from_source(source) end def source @source ||= raw_email.download end def processed? delivered? || failed? || bounced? end
  11. ActionMailbox::InboundEmail What Get’s Created Ruby Mail Object mail = Mail.new

    do from '[email protected]' to '[email protected]' subject 'This is a test email' body File.read('body.txt') end mail.to_s #=> "From: [email protected]\r\nTo: you@…
  12. Mailboxes Processing email messages • Where the magic happens 🪄

    • process method • before_processing method and bounce_with • Ruby Mail object • Parsing Attachments
  13. Mailboxes Processing Emails There are also some lifecycle_hooks around the

    process method before_processing, after_processing, around_processing
  14. Mailboxes Processing Emails # Enqueues the given +message+ for delivery

    and changes the inbound email's status to +:bounced+. def bounce_with(message) inbound_email.bounced! message.deliver_later end # Immediately sends the given +message+ and changes the inbound email's status to +:bounced+. def bounce_now_with(message) inbound_email.bounced! message.deliver_now end
  15. class SupportMailbox < ApplicationMailbox before_processing :ensure_user ... def ensure_user @user

    = User.find_by(email: from_email) unless @user bounce_with Mailer.post_not_found(mail) end end end
  16. Mailboxes Process Method class LegacyDocumentMailbox < ApplicationMailbox def process create_legacy_document

    end def create_legacy_document legacy_document = LegacyDocument.new( name: mail.subject, email: mail.from.first, ) # imported_document is the attachment name on LegacyDocument legacy_document.imported_document.attach( io: StringIO.new(mail.attachments.first.body.decoded), filename: mail.attachments.first.filename ) legacy_document.save! end end
  17. Deploying to Production Potential Pitfalls • Default URL isn’t going

    to be set by default (that happens in the Application Controller). You have to set this the same way you set it for your emails. • You probably want to set up an ActiveStorage service besides ‘disk’. That can cause some issues • Routing all your inbound email through a subdomain can save a lot of headaches.
  18. Default Ingress Options • Exim • Mailgun • Mandrill •

    Post fi x • Postmark (we’ll cover this one) • Qmail • SendGrid
  19. Deploying Con fi gure Ingress # config/environments/production.rb config.action_mailbox.ingress = :postmark

    # generate secure password for webhook URL SecureRandom.alphanumeric => "12312ddfgdfg"