I’ve updated this article once again. While untimely, I hope it will still be as useful as the previous incarnation from 2011.

This tutorial shows and explains how to build a form that emails its submitted fields. Just follow the code examples for the quick version. For the long and explained version, read everything.

Before starting

This tutorial does not explain how to set up a mail server. It’s a tedious task, which is why I prefer to use Google Apps for managing email. For those interested setting one up, plenty of well–written articles are available around the web: here’s one from Linode, my hosting provider.

The list of prerequisites is simple.

  1. A mail server
  2. Ruby on Rails 4.2.1 (any 4.2.x will probably work)

Configuring the mail server with Rails

The application must be able to communicate with the mail server. Open config/application.rb and tell ActionMailer how to connect to the mail server. Modify the code as needed (this example configuration works for Google Apps).

For more details about the possible configuration options, see the official documentation on the ActionMailer.

config.action_mailer.smtp_settings = {
  address: "smtp.gmail.com",
  port: 587,
  domain: "<example.tld>",
  user_name: "<username>",
  password: "<password>",
  authentication: :plain,
  enable_starttls_auto: true
}

config.action_mailer.default_url_options = {
  host: "yourdomain.tld"
}

The host key under the default_url_options configuration of ActionMailer is the domain which any generated URLs show. For example, if the host was left as yourdomain.tld, then link_to(root_url, "Website") would generate <a href="http://yourdomain.tld/">Website</a> in a mailer’s view.

Modelling a message

Models in Rails provide a lot of functionality for free. There’s no point re–creating components like validations when they’re done so well already. ActiveModel is written in such a way that picking individual pieces of functionality and leaving the rest is simple. Create a new model to represent an email in app/models. I chose to call mine, Message.

class Message

  include ActiveModel::Model
  include ActiveModel::Conversion
  include ActiveModel::Validations

  attr_accessor :name, :email, :content

  validates :name,
    presence: true

  validates :email,
    presence: true

  validates :content,
    presence: true

end

The include method calls pick the bits of functionality from ActiveModel that I want. This example only validates that each attribute is present. Adding a format validation on the email address would be a good improvement.

The mailer class

The mailer for sending messages is simple. Run rails g mailer MessageMailer to generate the files. Open up message_mailer.rb in app/mailers and add a new_message method.

class MessageMailer < ActionMailer::Base

  default from: "Your Mailer <noreply@yourdomain.com>"
  default to: "Your Name <your.email@yourdomain.com>"

  def new_message(message)
    @message = message
    
    mail subject: "Message from #{message.name}"
  end

end

Replace the necessary bits of configuration as needed.

Template for the email

Create the file new_message.text.erb in app/views/message_mailer. This is the template used for the email. Mine is bland. Make it better.

<%= @message.name %> <<%= @message.email %>> wrote:

<%= @message.content %>

Controller and routes

I try to stick with convention when it makes sense. Because of that, controller is ‘RESTful’ in the Rails sense of the word. Run rails g controller messages to generate the necessary files. Open message_controller.rb to add new, and create actions.

class MessagesController < ApplicationController

  def new
    @message = Message.new
  end

  def create
    @message = Message.new(message_params)
    
    if @message.valid?
      MessageMailer.new_message(@message).deliver
      redirect_to contact_path, notice: "Your messages has been sent."
    else
      flash[:alert] = "An error occurred while delivering this message."
      render :new
    end
  end

private

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

end

Notice that instead of the typical if @message.save?, it checks for validity instead.

Routes

Here I break away from the convention of routes such as, /messages/new, and, /messages. Instead, I wanted everything under, /contact. I added two lines in my config/routes.rb to accomplish that.

get 'contact', to: 'messages#new', as: 'contact'
post 'contact', to: 'messages#create'

Under /contact, all GET requests call MessagesController#new, and all POST requests call MessagesController#create.

The form template

Create the file new.html.erb in app/views/messages. The code that goes in this file is entirely dependent on the application’s layout. I just copied this code from my own template.

<%= form_for @message, url: contact_path do |message_form| %>
  <fieldset>
    <div class="field">
      <%= message_form.text_field :name, placeholder: "Name" %>
    </div>
    
    <div class="field">
      <%= message_form.email_field :email, placeholder: "Email"%>
    </div>
    
    <div class="field">
      <%= message_form.text_area :content, placeholder: "Message"%>
    </div>
  </fieldset>
  
  <fieldset>
    <div class="field">
      <%= message_form.submit "Send" %>
    </div>
  </fieldset>
<% end %>

Try it

Run rails server and navigate to /contact. Fill out and submit the form!