Sending Emails in Rails with Action Mailer and Gmail
Published: 2023-04-19 20:26:07

This post will describe how to set up and send a confirmation email, when a customer books a training session. 

The steps:
1. Set up a mailer with rails generate mailer
2. Create email templates (views)
3. Tell the appropriate controller action to send the email
4. Configure the mail settings for Gmail.

Let's assume we have an app that allows users to book a training session and that we already have a basic Order model and controller setup, which when booked creates and new instance of Order. 

```
class OrdersController < ApplicationController
  def new
    @order = Order.new
  end

  def create
    @order = Order.new(order_params)

    if @order.save
      render json: @order
    else
      render json: { errors: @booking.errors.full_messages }, status: :unprocessable_entity
    end
  end

  private

  def order_params
    params.require(:order).permit(:name, :email, :address, :phone, :message)
  end
end
```

In addition to created the instance of Order, we want to be able to send the user an email confirmation for the order. 

To get started, we first need to create a mailer:

```
$ rails generate mailer OrderMailer
```

This will create the following files and output:

```
create  app/mailers/order_mailer.rb
invoke  erb
create    app/views/order_mailer
invoke  test_unit
create    test/mailers/order_mailer_test.rb
create    test/mailers/previews/order_mailer_preview.rb
```

Then navigate to the "app/mailers" folder and select the application_mailer.rb:

```
class ApplicationMailer < ActionMailer::Base
  default from: 'from@example.com' # Replace this email address with your own
  layout 'mailer'
end
```

The "default from:" is where you add the email address you want to send the emails from. So can either be a business email or personal. 

Now we need to go into the actual order_mailer.rb and add a method to the mailer to send emails:

```
class OrderMailer < ApplicationMailer
  def new_order_email
    @order = params[:order]

    mail(to: @order.email, subject: "You got a new order!")
  end
end
```

The key here is to set the "mail(to: @order.email".  This will be the email(email we are sending to) that is sent from the order controller when a new instance is being created. Any instance variables in new_order_email can be used in the mailer views. The params[:order] will be provided when we tell the OrderController to send the email.

Let's create an email view file now, making sure to name the file the same as the method. Make a new_order_email.html.erb in the app/views/order_mailer/ folder:

```
# app/views/order_mailer/new_order_email.html.erb

<!DOCTYPE html>
<html>
  <head>
    <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
  </head>
  <body>
    <p>You placed a new order!</p>
    <p>
    Order details<br>
    --------------------------
    </p>
    <p>Name: <%= @order.name %></p>
    <p>Email: <%= @order.email %></p>
    <p>Address: <%= @order.address %></p>
    <p>Phone: <%= @order.phone %></p>
    <p>
    Message:<br>
    ----------
    </p>
    <p><%= @order.message %></p>
  </body>
</html>
```

Now that we have the emails set up, next we'll tell the OrdersController to send an email when an order is made, that is, after an order is saved in the create action.

```
class OrdersController < ApplicationController
  def create
    @order = Order.new(order_params)

    if @order.save
OrderMailer.with(order: @order).new_order_email.deliver_later
      render json: @order
    else
      render json: { errors: @booking.errors.full_messages }, status: :unprocessable_entity
    end
  end

....
end
```
Here, we added the following line of code after the order was saved:

```
OrderMailer.with(order: @order).new_order_email.deliver_later
```

Notice the with(order: @order) code. This is what gives the OrderMailer access to the order info as a param. Remember setting the instance variable with @order = params[:order] in the OrderMailer? That's where the param is coming from!

Lastly, we need to configure our Rails app to send emails via Gmail. To do so, we'll add the following settings to our config/environments/production.rb:

```
# config/environments/production.rb

config.action_mailer.delivery_method = :smtp
host = 'example.com' #replace with your own url
config.action_mailer.default_url_options = { host: host }

# SMTP settings for gmail
config.action_mailer.smtp_settings = {
  :address              => "smtp.gmail.com",
  :port                 => 587,
  :user_name            => <gmail_username>,
  :password             => <gmail_password>,
  :authentication       => "plain",
  :enable_starttls_auto => true
}
```
For local use only or development use, use:

```
host = 'localhost:3000'
config.action_mailer.default_url_options = { :host => 'localhost:3000', protocol: 'http' }
```

Replace <gmail_username> and <gmail_password> with your own username and password, which would preferably be hidden away as environment variables. Note on the password: I highly recommend enabling 2-Step Verification and registering an "app password" to use in the app or you're likely to run into problems with Gmail blocking the emails.

If you are using 2-step verification, here's how you set it up:
If your Gmail account uses 2-step verification, you will need to get an app password and use that instead of your regular password. If you don't use 2-step verification, I recommend turning it on to avoid getting the emails blocked by Google.

To create an app password, go to your Google account settings and navigate to the "Security" tab. Under "Signing in to Google", click on the "App passwords" menu item (this will only be available if you have 2-step verification turned on). Next, select Other (Custom name) under the "Select app" dropdown and enter the name of your app or something else useful. Click "Generate" and your new app password will appear on the screen. Make sure you copy it before closing the window, or you won't be able to see the password again.

Now, in your Rails app mailer settings, replace the <gmail_password> with the new app password instead.

**Conclusion**
We know have a fully functional mailer that notifies our customer of their order they placed. Once the Rails mailer and Gmail settings are properly configured, we can easily send other emails from other controller actions by generating and setting up new mailers in much the same way