14
votes

I can't seem to find a step by step tutorial on how to integrate the Sendgrid web API in to a Ruby on Rails application. I'm pretty new to this so maybe I'm missing something obvious.

I would like to use the Sendgrid web API instead of the smtp delivery method (mailgun talks about the benefits of the web API over the SMTP method here: https://documentation.mailgun.com/quickstart-sending.html, and I was thinking that Sendgrid would either have the same benefits or I would potentially switch to mailgun later).

After installing the sendgrid gem (https://github.com/sendgrid/sendgrid-ruby), the documentation tells me to "Create a new client with your SendGrid API Key", and that I can do it 2 ways:

require 'sendgrid-ruby'

# As a hash
client = SendGrid::Client.new(api_key: 'YOUR_SENDGRID_APIKEY')

# Or as a block
client = SendGrid::Client.new do |c|
  c.api_key = 'YOUR_SENDGRID_APIKEY'
end

Where specifically in my application am I supposed to put this code? Should I put this in my mailer, my application mailer or in the config/environments/production.rb file?

I took a look at this tutorial that walks through how to set up the Mailgun API: https://launchschool.com/blog/handling-emails-in-rails

According to this tutorial it looks like the line client = SendGrid::Client.new(api_key: 'YOUR_SENDGRID_APIKEY') should actually go in to the mailer method itself. See below for the launchschool.com example (presumably replacing the mailgun specific info with the sendgrid info):

class ExampleMailer < ActionMailer::Base

      def sample_email(user)
    @user = user
    mg_client = Mailgun::Client.new ENV['api_key']
    message_params = {:from    => ENV['gmail_username'],
                      :to      => @user.email,
                      :subject => 'Sample Mail using Mailgun API',
                      :text    => 'This mail is sent using Mailgun API via mailgun-ruby'}
    mg_client.send_message ENV['domain'], message_params
  end
end

Additionally, how do I get my mailer method to send a mailer view instead of simple text as outlined in the launchschool example? For example, instead of sending the text 'This mail is sent using...' I would like to send a mailer view (something like account_activation.html.erb).

Finally, I am using Devise in my application, and I would like to have Devise use the web API to send emails (ie password reset, etc). Does this mean I need to create a custom mailer for Devise? If so, how do I do that?

According to Devise (https://github.com/plataformatec/devise/wiki/How-To:-Use-custom-mailer), I should "create a class that extends Devise::Mailer". Does that mean I simply make a file within my mailer folder with the info laid out in the docs? Do I need a separate mailer for Devise or can I have an existing mailer inherit from the Devise mailer? Finally, how do I tell devise to use the sendgrid web api to send emails (instead of the simple smtp method)?

Sorry for the long question, but hopefully others find it useful.

Thanks!

3
Haven't tried this but seems promising: github.com/eddiezane/sendgrid-actionmailerVibhor Mahajan

3 Answers

7
votes

I would create a mailer class to do this

class SendgridWebMailer < ActionMailer::Base
  include Sendgrid

  def initialize
    @client = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY']).client
  end

  def send_some_email(record, token)
    mail = Mail.new
    // do your mail setup here
    mail = Mail.new
    mail.from = Email.new(email: YOUR_EMAIL_HERE)
    mail.subject = YOUR_SUBJECT

    // I personally use sendgrid templates, but if you would like to use html - 
    content = Content.new(
      type: 'text/html',
      value: ApplicationController.render(
        template: PATH_TO_TEMPLATE,
        layout: nil,
        assigns: IF_NEEDED || {}
      )
    mail.contents = content 

    personalization = Personalization.new
    personalization.to = Email.new(email: EMAIL, name: NAME)
    personalization.subject = SUBJECT

    mail.personalizations = personalization
    @client.mail._('send').post(request_body: mail.to_json)
  end
end

Call it using

SendgridWebMailer.send_some_email(record, token).deliver_later

For Devise

 class MyDeviseMailer < Devise::Mailer
   helper :application # gives access to all helpers defined within `application_helper`.
   include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`

   def reset_password_instructions(record, token, opts={})

      SendgridWebMailer.send_some_email(record, token).deliver_later
   end
end

in config/devise.rb

# Configure the class responsible to send e-mails.
  config.mailer = 'MyDeviseMailer'
-1
votes

EDIT:

This method will, unfortunately, only work with remote images.


Okay, so here's how I did it. There may be a better way to do this, but this worked for me.

So with sending an email you would originally send it by doing something like UserMailer.reset_email(user).deliver. So remove the deliver and save it to a variable:

object = UserMailer.reset_email(user)

Deeply nested in this ish is the body of the email. The place where it lies may change with context, so my advice is to return the object to the frontend so that you can dig into it and find it. For me, the body resided here:

object = UserMailer.reset_email(user)
body = object.body.parts.last.body.raw_source

Okay so now you got the raw source. Now to send it, here's a method I created:

def self.sendEmail(from,to,subject,message)
    from = Email.new(email: from)
    to = Email.new(email: to)
    content = Content.new(type: 'text/html', value: message)
    mail = Mail.new(from, subject, to, content)

    sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
    response = sg.client.mail._('send').post(request_body: mail.to_json) 
    puts response.status_code
    puts response.body
    puts response.headers
  end

Call it as such (in my case it's in the user model):

User.sendEmail('[email protected]',user.email,'Reset Password', body)

Make sure to change the content type from plain to html, or you will just get the raw code. Hope this helps.

-3
votes

Here's a step-by-step guide to help you integrate SendGrid into your RoR's app. Hope it helps!

  • Create a SendGrid account first at : http://sendgrid.com/
  • Create a new rails folder (sendgrid_confirmation)

    • rails new sendgrid_confirmation
  • Navigate to ‘sendgrid_confirmation’ folder

    • cd sendgrid_confirmation
  • Open ‘sendgrid_confirmation’ in your text editor (Sublime)

  • Create a user model (User is a test model, you can create any other model as per your project’s use)

    • rails generate scaffold user name email login
    • rake db:migrate
  • Include sendgrid-rails gem in your Gemfile

    • gem 'sendgrid-rails', '~> 2.0'
  • Run bundle install in your terminal

    • bundle install
  • Use secrets.yml to define the SendGrid API credentials: (config/secrets.yml) production: sendgrid_username: your-sendgrid-username sendgrid_password: your-sendgrid-password (Emails are not sent in development and test environments. So you can define it only for production.)

  • Generate a Mailer class. Mailer classes function as our controllers for email views.

    • rails generate mailer UserNotifier
  • Open app/mailers/user_notifier.rb and add the following mailer action that sends users a sign-up mail

class UserNotifier < ActionMailer::Base
  default :from => '[email protected]'
  # send a signup email to the user, pass in the user object that  contains the user's email address
  def send_signup_email(user)
    @user = user
    mail(:to => @user.email,
      :subject => 'Thanks for signing up for our amazing app')
  end
end
  • Create a file app/views/User_notifier/send_signup_email.html.erb as follows: (This will create a view that corresponds to our action and outputs HTML for our email)

<!DOCTYPE html>
    <html>
        <head>
                <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
        </head>
        <body>
                <h1>Thanks for signing up, <%= @user.name %>!</h1>
                <p>Thanks for joining and have a great day! Now sign in and do awesome things!</p>
        </body>
    </html>
  • Go to the Users Controller (app/controllers/users_controller.rb) and add a call to UserNotifier.send_signup_email when a user is saved.

def create
  @user = User.new(user_params)
  respond_to do |format|
    if @user.save
      UserNotifier.send_signup_email(@user).deliver
      format.html { redirect_to @user, notice: 'User was successfully created.' }
      format.json { render :show, status: :created, location: @user }
    else
      format.html { render :new }
      format.json { render json: @user.errors, status: :unprocessable_entity }
    end
  end
end
  • Update your config/environment.rb to point your ActionMailer settings to SendGrid’s servers. Your environment.rb file should look like the following:

# Load the Rails application.
require File.expand_path('../application', __FILE__)

# Initialize the Rails application.
Rails.application.initialize!

ActionMailer::Base.smtp_settings = {
    :user_name => ‘your_sendgrid_username’,
    :password => 'your_sendgrid_password’,
    :domain => ‘your_domain.com',
    :address => 'smtp.sendgrid.net',
    :port => 587,
    :authentication => :plain,
    :enable_starttls_auto => true
}
  • Point your config/routes.rb file to load the index page on load. Add the following in your routes.rb file:

get ‘/’ => ‘users#index’

And that is it! Now when you create a new user, you should receive an email (on the user.email you provided) from [email protected]. You can change this from:e-mail from app/mailers/user_notifier.rb. Change the default :from address and it should do that. You can add email parameters in the send_signup_mail method inside the same file and add details that you’d like to add. You can also change the message body from app/views/user_notifier/send_signup_email.html.erb to display whatever content you want to.