Let’s build a simple contact form for your Rails driven website that uses Mandrill as the desired email server. Mandrill lets us offload the management of setting up and running an email server ourselves and, therefore, relieving ourselves of a massive headache. Mandrill starts you with 2,000 free trial sends which is more than enough to decide if you’re going to like it.

Setup a Mandrill account

If you do not have a Mandrill account you’ll need to go here and register. Once you’ve done that and logged into the Dashboard you should click Settings. You’ll see some information we’ll end up using later, but it’s important that you generate a new API Key for the project you’re working on.

Mandrill API Key settings

You’ll want to generate a new key for each project you’re working on. This would allow you to disable or revoke a particular API key without disrupting service on any other sites.

Keep that page open so we can copy over some important info into your environment variables.

Set your environment variables

I’m going to assume you know what environment variables are and why they’re important and good to make use of. You might start here if you don’t know what they are.

I do my development on a Mac and have chosen to put my environment variables into .bash_profile. Open whatever file you’ve chosen to use and enter something similar to the following.

## Mandril
export MANDRILL_USER="[email protected]"
export PROJECTNAME_MANDRILL_KEY="..."

Save that file and close out of it. You’re Mandrill user is going to be the same for every project, but you’ll probably end up having multiple API keys. I’ve chosen to use the pattern PROJECTNAME_MANDRILL_KEY to distinguish between the various keys I use.

Update secrets.yml

You’ve added your environment variables so now make sure they’re referenced in your secrets.yml file.

common: &common
  mandrill_user: <%= ENV['MANDRILL_USER'] %>
  mandrill_key:  <%= ENV['PROJECTNAME_MANDRILL_KEY'] %>

development:
  <<: *common
  secret_key_base:blahblah

test:
  <<: *common
  secret_key_base:blahblah

production:
  <<: *common
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

I typically have a common group that can be shared across environments. This does mean we’re using the same Mandrill API Key across all the environments. You may wish to create separate keys.

Configure Action Mailer

Open the file config/environments/development.rb and add the following with your own values.

...
# ActionMailer Config
config.action_mailer.default_url_options = { host: 'localhost:3000' }
config.action_mailer.delivery_method = :smtp
# change to true to allow email to be sent during development
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default :charset => "utf-8"
config.action_mailer.smtp_settings = {
    address:    "smtp.mandrillapp.com",
    port:       587,
    user_name:  Rails.application.secrets.mandrill_user,
    password:   Rails.application.secrets.mandrill_key
}
...

These are just simple configs. Save and close. We’ll do nearly the same for config/environments/development.rb.

...
# ActionMailer Config
config.action_mailer.default_url_options = { host: 'https://example.com' }
config.action_mailer.delivery_method = :smtp
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = false
config.action_mailer.default :charset => "utf-8"
config.action_mailer.smtp_settings = {
    address:    "smtp.mandrillapp.com",
    port:       587,
    user_name:  Rails.application.secrets.mandrill_user,
    password:   Rails.application.secrets.mandrill_key
}
...

The differences between development and production are:

  • default_url_options – Change to your production URL.
  • raise_deliver_errors – Change to false

Create the Message model

We’re going to create a Plain Old Ruby Object, or PORO, and include a few classes so we can benefit from some ActiveModel features and not reinvent the wheel. We’re not going to use the generator to create the model. Instead, create the file app/models/message.rb.

class Message
  include ActiveModel::Model
  include ActiveModel::Conversion
  include ActiveModel::Validations

  # Let's us access as if they were actual database objects
  attr_accessor :name, :email, :subject, :message

  # Yay validation!
  validates :name, :email, :subject, :message, presence: true
end

Create the MessageMailer

Now we move on to generating our MessageMailer. Run the following from your Terminal:

rails generate mailer message_mailer

After that completes, open app/mailers/message_mailer.rb and add the following:

default from: "Project Message <[email protected]>"
default to: "[email protected]"

def contact_team(message)
  @message = message.message
  @subject = message.subject
  @from = "#{message.name} <#{message.email}>"

  mail(subject:@subject, from:@from) do |format|
    format.text
    format.html
  end
end

If you’re going to require a from email on your form, then it’s not necessary to have a default from address. In this case, I’ve included it as a backup. The contact_team is a method we’ll call from a controller in the next step.

It’s important to take note of the formats we’ve defined. For example, format.html and format.text. This allows us to send and receive multi-part emails. When we choose to do this we get a little Rails magic allowing us to create two files and have them automagically used. Create the following files with their content:

<!-- app/views/message_mailer/contact_team.html.erb -->
<p>New application submission</p>
<br>
<p><b>Subject:</b></p>
<%= @subject %>
<p><b>Message:</b></p>
<%= @message %>
<!-- app/views/message_mailer/contact_team.text.erb -->
New application submission

Subject:
<%= @subject %>
Message:
<%= @message %>

If it’s not obvious, these are our mailer views. I’m not going to focus on adding styles to these emails.

Create the messages controller

We need to create a controller for our messages. So, go ahead and run this from your terminal:

rails generate controller messages

After that has run, open app/controllers/messages_controller.rb. We need to create two actions and they look like this:

class MessagesController < ApplicationController

  def new
    @message = Message.new
  end

  def create
    @message = Message.new(message_params)

    respond_to do |format|
      if @message.valid?
        format.html {
          MessageMailer.contact_crew(@message).deliver_now
          redirect_to contact_path, notice: "Your message has been sent."
        }
      else
        format.html {
          flash.now[:error] = "Oops! We weren't able to deliver your message."
          render :new
        }
      end
    end
  end

  private

  def message_params
    params.require(:message).permit(:name, :email, :subject, :message)
  end

end

This will provide us the capabilities for both a new and create view with the validation goodness we’re used to.

Contact page view

We’ve done all the behind the scenes work and can now show it off. Create the file app/views/messages/new.html.erb. It should look something like the following simple form. Uses Bootstrap CSS.

<h1>Contact Form</h1>
<%= form_for @message, url: contact_team_path do |f| %>
  <div class="row">
    <div class="col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2">
      <%= render 'shared/form_errors', object: f.object %>
      <fieldset class="form-group">
        <%= f.label :name %>
        <%= f.text_field :name, class:'form-control' %>
      </fieldset>

      <fieldset class="form-group">
        <%= f.label :email %>
        <%= f.text_field :email, class:'form-control' %>
      </fieldset>

      <fieldset class="form-group">
        <%= f.label :subject %>
        <%= f.text_field :subject, class:'form-control' %>
      </fieldset>

      <fieldset class="form-group">
        <%= f.label :message %>
        <%= f.text_area :message, class:'form-control', rows:7 %>
      </fieldset>

      <fieldset class="form-group text-xs-right">
        <%= f.submit 'Send', class:'btn btn-primary' %>
      </fieldset>
    </div>
  </div>
<% end %>

Routes

The last step is to add the appropriate routes to our config/routes.rb file.

...
# Contact the team
  match '/contact-team', to:'messages#new', via: :get, as:'contact'
  match '/contact-team', to:'messages#create', via: :post
...

That’s it!

That completes the process of setting up a contact form in Rails using Mandrill. It took a while, but we got there 😉

Other considerations

This is a bare-bones example that doesn’t deal with potential abuse from evildoers. It would probably be prudent to, among other things, ensure the form was rate-limited so a user couldn’t spam you with thousands of messages all at once. That’s outside the scope of this log entry, but could use consideration.

Written by Matt Haliski

The First of His Name, Consumer of Tacos, Operator of Computers, Mower of Grass, Father of the Unsleeper, King of Bad Function Names, Feeder of AI Overlords.