Iteration L1: Receiving Support Emails with Action Mailbox

Configuring Rails to receive emails requires three steps: initially setting up Action Mailbox, setting up Active Storage to hold the raw emails we receive, and implementing a mailbox, which is like a controller that handles incoming emails.

Setting up Action Mailbox

To set up Action Mailbox in our app, we’ll run a Rake task that will create some configuration files, a base mailbox class we’ll inherit from, as well as some database tables that Rails will use to store information about incoming emails. Let’s run the Rake task:

 >​​ ​​bin/rails​​ ​​action_mailbox:install
 Copying application_mailbox.rb to app/mailboxes
  create app/mailboxes/application_mailbox.rb
 Copied migration
  20190316191846_create_active_storage_tables.active_storage.rb
  from active_storage
 Copied migration
  20190316191847_create_action_mailbox_tables.action_mailbox.rb
  from action_mailbox

Note that a) we’ve reformatted our output to fit the pages in the book and b) since there were two migrations created and migration filenames have a date and timestamp in them, your filenames won’t exactly match ours. Next, we’ll add the tables that Rake task created to our development and test databases:

 >​​ ​​bin/rails​​ ​​db:migrate
 == 20190316191846 CreateActiveStorageTables: migrating ======================
 -- create_table(:active_storage_blobs, {})
  ->​​ ​​0.0015s
 -- create_table(:active_storage_attachments, {})
  ->​​ ​​0.0013s
 == 20190316191846 CreateActiveStorageTables: migrated (0.0029s) =============
 
 == 20190316191847 CreateActionMailboxTables: migrating ======================
 -- create_table(:action_mailbox_inbound_emails)
  ->​​ ​​0.0017s
 == 20190316191847 CreateActionMailboxTables: migrated (0.0017s) =============

In the real world, we’d also need to configure Action Mailbox for our particular incoming email service provider. The Rails Guide[85] is the best place to look for how to do that. We won’t set one up here since setting up accounts with services like Amazon SES or Mailgun is somewhat involved (though once you have your account set up, configuring Rails to use it is a snap). For our immediate needs, Rails provides a way to simulate sending emails, which we’ll see in a moment.

The way Action Mailbox works is that all incoming emails get stored in a cloud storage system like Amazon’s S3. Rails includes a library called Active Storage that abstracts away the details of the cloud service you are using. We’ll need to configure Active Storage for Action Mailbox to work properly.

Setting Up Active Storage

As with your real-world email provider, your real-world cloud storage provider will require specific configuration in Rails, and the Guide[86] can give you the details. For our purposes, we’ll set up the disk-based service that works with our local disk. This will allow us to fully use Active Storage locally, which means Action Mailbox can work locally.

To set that up, we’ll need to configure the service in our app’s configuration and then tell Rails where to store the files that Active Storage will manage.

First, edit config/environments/development.rb, adding this line to the configuration at the end of the block:

» config.​active_storage​.​service​ = ​:local
 end

We’ll explain what :local means in a moment. Now, add a similar line to config/environments/test.rb, but using the :test service instead:

» config.​active_storage​.​service​ = ​:test
 end

With those added, we must now define what those symbols mean by creating config/storage.yml to look like so:

»local:
» service: ​Disk
» root: ​<%= Rails.root.join("storage") %>
»
»test:
» service: ​Disk
» root: ​<%= Rails.root.join("tmp/storage") %>

The root key in this file should match the values we used in the files in config/environments. In this case, we’ve configured both :local and :test to use Active Storage’s disk-based service, with our development environment (:local) using the directory storage that is in the root of our project, and the test environment (:test) using tmp/storage.

With this set up, when we receive an email, the entire payload gets written to our storage service and, as we’ll see in a moment, we can access parts of that email to trigger whatever logic we need in our Rails app. The reason Rails does this is that emails can be quite large (especially if they have attachments), and you don’t necessarily want to store very large objects in a relational database. It’s much more common to store such data to disk or with a cloud storage provider and store a reference to that object in the database.

Now that we’ve done the one-time setup, let’s create a mailbox to receive our support request emails from customers.

Creating a Mailbox to Receive Emails

Action Mailbox works by routing incoming emails to a mailbox. A mailbox is a subclass of ApplicationMailbox with a method named process that is called for each email routed to that mailbox. The way emails get routed is similar to how web requests get routed in config/routes.rb. For email, you will tell Rails what sorts of emails you want routed where.

We want emails to [email protected] to get routed to a mailbox so we can handle them. The way to do that is to insert a call to the method routing inside ApplicationMailbox, like so:

 class​ ApplicationMailbox < ActionMailbox::Base
» routing ​"[email protected]"​ => ​:support
 end

This tells Rails that any email to (or cc’d to) [email protected] should be handled by the class SupportMailbox. We can create that class using a Rails generator like so:

 >​​ ​​bin/rails​​ ​​g​​ ​​mailbox​​ ​​support
  create app/mailboxes/support_mailbox.rb
  invoke test_unit
  create test/mailboxes/support_mailbox_test.rb

If you look at app/mailboxes/support_mailbox.rb, you’ll see a few lines of code, notably an empty method called process:

 class​ SupportMailbox < ApplicationMailbox
 def​ ​process
 end
 end

Now, every email we receive at [email protected] will trigger a call to process in SupportMailbox. Inside the process method, we have access to the special variable mail. This is an instance of Mail::Message[87] and allows us to access the various bits of an email you might expect to have, such as who sent it, the subject, and the contents.

Let’s see how this works before getting too far along by adding some puts calls into our mailbox:

 class​ SupportMailbox < ApplicationMailbox
 def​ ​process
» puts ​"START SupportMailbox#process:"
» puts ​"From : ​​#{​mail.​from_address​​}​​"
» puts ​"Subject: ​​#{​mail.​subject​​}​​"
» puts ​"Body : ​​#{​mail.​body​​}​​"
» puts ​"END SupportMailbox#process:"
 end
 end

Since we didn’t configure a real email provider, how do we trigger our mailbox locally? The answer is a special UI included with Rails called a conductor.

Using the Conductor to Send Emails Locally

Action Mailbox includes a special developer-only UI we can use to send emails to ourselves. This allows us to see our mailbox working end-to-end without having to configure a real email provider. To see it, start up your server (or restart it if it’s already running).

Navigate to http://localhost:3000/rails/conductor/action_mailbox/inbound_emails and you should see a bare-bones UI that includes a link labeled “Deliver new inbound email”, like so:

images/depot_ua_conductor1.png

Click that link, and you should see a very basic UI to write an email, like so:

images/xxx_inbound_email_conductor.png

Fill this in, remembering to use [email protected] as the “From” email so that the email gets routed to your mailbox. If you then click “Deliver inbound email”, and flip back to where you ran your server, you should see, among other log output, the puts you inserted:

 START SupportMailbox​#process:
 From : [email protected]
 Subject: I need help!
 Body : I can't find my order. It's ​#12345
 END SupportMailbox​#process:

Now that we see how all the parts fit together, let’s write the real code to store the request for help from the customer (as well as how to test our mailbox with a unit test).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.32.230