Monday, April 14, 2014

How I integrated Rails with Mandrill send-template

First off let me say that I am new to Ruby and Rails development so my comments probably won't impress any Sr. developers or win any followers among Ruby Purists. However, my intent is to share actual learning by someone new to Ruby and hopefully help some other newbies in the process.

This post is about integrating my rails application with Mandrill for the purpose of sending transactional e-mail messages. So, let's get started.

My objective is to have three modes of operation in sending my mail messages.
  1. Test – this mode will call my send mail functions, but not invoke the Mandrill APIs. I will simulate various responses by hard-coding them into the results of my mail methods instead of getting them from Mandrill.
  2. Development – in this mode, my send mail functions will call the Mandrill APIs, but using Mandrill's test mode. This will allow me to verify that my JSON calls are in fact getting to Mandrill and able to return their result codes.
  3. Production – this mode will only be used on the production server and will of course, deliver my messages to their intended recipient.

Getting started with Mandrill was easy. I just signed up for a free account. After signing in I navigated to the Configuration Menu (the Gear) and selected SMTP & API credentials. We need a key from Mandrill to include in all of our API calls and this is where we create them. Click the “+ New API Key” button. Which takes us here:


I created two keys, one named TestKey for which I selected the Test Key check box, and one named Production for which I did not select Test Key.

I'm going to use two layers of configuration in my Rails app to set up my Mandrill email access. The first is in my project/config/local_env.yml file. This file holds what I think of as global constants that I don't want stored up on Github. The file can be added to your project/.gitignore file and won't be stored in your repository. I set up two constants to contain my keys:

MANDRILL_PROD_API_KEY: “TheFunkyStringMandrillMade”
MANDRILL_TEST_API_KEY: “AnotherFunkeyStringMandrillMade”

Each key was actually 22 mixed case letters and numbers.

While I was in the local_env.yml file I also threw in a couple of other constants that I might want to change latter that are email related, the name and address of the person I want my emails to be from.

OWNER_EMAIL: “myname@mydomain.com
OWNER_NAME: “My Name”

Now I need to reference these values in my application. This is where the project/config/environments folder comes into play. It contains three files that are loaded into your application at run time based upon the mode you are actually running in:
development.rb
production.rb
test.rb

I created four configuration variables in each of the three files:
config.mandrillsend = false
config.mandrillkey = ENV["MANDRILL_TEST_API_KEY"]
config.returnaddress = ENV["OWNER_EMAIL"]
config.returnname = ENV["OWNER_NAME"]

The first one is how I intend to implement my code that skips the API calls completely. I'll set it to false when I'm in hard core development mode and don't want to even make the calls to Mandrill, and I'll set it to true when I want the calls made. It will generally be true in Development and Production modes, but false in test mode. Also, in the test.rb and developmen.rb files I set

config.mandrillkey = ENV["MANDRILL_TEST_API_KEY"]

and as you can guess, in production.rb I set

config.mandrillkey = ENV["MANDRILL_PROD_API_KEY"]

The next step was to build a function that I could call to actually send a message to Mandrill's APIs. I created:

project/lib/mandrill.rb

Keep in mind that when you put code in the project/lib folder it isn't reloaded on each invocation like your models, views and controllers so when I was debugging I had to stop and start the rails server after each change. Here is the complete file:

module Mandrill
# This object is actually returned from the sendtemplate function
# instead of a real result when I set config.mandrillsend = false.
# I can change it to simulate various results.
class FakeRes
  def code
    "200"
  end
  def body
    "Test mode"
  end
end

class Mandrill

  require "net/http"
  require "uri"
  # Here I create a dummy JSON record that is ready to hold all the parameters to send
  # in an API call. I do this just to help me visualize the record. You could skip this and just
  # build up the record from scratch when you call sendtemplate.
  def initialize
    @jhash = {
      "key" => Rails.configuration.mandrillkey,
      "template_name" => "no-template",
      "template_content" => [{
        "name" => "NotNeeded",
        "content" => "NotSupplied"
      }],
      "message" => {
        "from_email" => Rails.configuration.returnaddress,
        "from_name" => Rails.configuration.returnname,
        "to" => [{
          "email" => Rails.configuration.returnaddress,
          "name" => Rails.configuration.returnname,
          "type" => "to"
        }],
        "headers" => {
          "Reply-To" => Rails.configuration.returnaddress
        },
        "subject" => "Getting started",
        "important" => false,
        "track_opens" => nil,
        "track_clicks" => nil,
        "auto_text" => nil,
        "auto_html" => nil,
        "inline_css" => nil,
        "url_strip_qs" => nil,
        "preserve_recipients" => nil,
        "view_content_link" => nil,
        "bcc_address" => nil,
        "tracking_domain" => nil,
        "signing_domain" => nil,
        "return_path_domain" => nil,
        "merge" => true,
        "global_merge_vars" => [
          {
            "name" => "CURRENT_YEAR",
            "content" => "2014"
          },
          {
            "name" => "LIST_COMPANY",
            "content" => "My Company"
          }
        ]
      }, # message
      "async" => false,
      "ip_pool" => "Main Pool"
    } # jhash
  end # initialize

    def sendtemplate(template, from_email, from_name, to_email, to_name, subject, merge_vars, bcc ="")
      Rails.logger.debug("Now invoking sendtemplate")
      Rails.logger.debug(template, from_email, from_name, to_email, to_name, subject, bcc)
      Rails.logger.debug(merge_vars.to_s)
      if Rails.configuration.mandrillsend
        Rails.logger.debug("Rails.configuration.mandrillsend set to true so trying to send email")    
        # Build an array of to values
        if bcc == ""
          @jhash["message"]["to"] = [{"email" => to_email,"name" => to_name,"type" => "to"}]
        else
          @jhash["message"]["to"] = [{"email" => to_email,"name" => to_name,"type" => "to"},
            {"email" => bcc,"name" => bcc,"type" => "bcc"}]
        end
        @jhash.update("template_name" => template)
        @jhash["message"].update("from_email" => from_email)
        @jhash["message"].update("from_name" => from_name)
        @jhash["message"]["headers"].update("Reply-To" => from_email)
        @jhash["message"].update("subject" => subject)
        @jhash["message"].update("global_merge_vars" => merge_vars)
        
        service_url = "https://mandrillapp.com/api/1.0/messages/send-template.json"
        Rails.logger.debug(service_url)
        
        url = URI.parse(service_url)
        headers = {"host" => url }
        req = Net::HTTP::Post.new(url.path)
        req["Content-Type"] = "application/json"
        req["Accept"] = "application/json"
        
        req.body = JSON.generate(@jhash)
        
        Rails.logger.debug("Start ====req.body==========")
        Rails.logger.debug(req.body)
        Rails.logger.debug("End ======req.body==========")
        
        con = Net::HTTP.new(url.host, url.port)
        
        # ssl for https
        con.use_ssl = true
        con.verify_mode = OpenSSL::SSL::VERIFY_NONE
        
        res = con.start {|http| http.request(req) }    
        
        Rails.logger.debug(res.code)
        Rails.logger.debug(res.body)
        
      else
        Rails.logger.debug("Rails.configuration.mandrillsend set to false so no email sent")    
        res = FakeRes.new
        Rails.logger.debug(res)
      end
        
      Rails.logger.debug("Now done with SendTeamplate")

      return res
    end #sendtemplate
  end #class Mandrill   
end #module Mandrill

Now to send an email message I add

require “mandrill”

in my controller. Then it's just two lines of code:

mandrillsend = Mandrill::Mandrill.new
result = mandrillsend.sendtemplate(template, from_email, from_name, to_email, to_name, subject, merge_vars, bcc_email)


So now you know, for better or for worse, how I implemented Mandrill send-template in my Rails project. In my next post I'll cover implementing the mail template and passing the values into Mandrill to build up the message content.