Skip to content

Commit

Permalink
Merge pull request sendgrid#28 from apartmentlist/master
Browse files Browse the repository at this point in the history
Extend Gem with additional Template and Recipient Functionality (round 2)
  • Loading branch information
thinkingserious committed Oct 28, 2015
2 parents ee0799d + 439f899 commit f28d913
Show file tree
Hide file tree
Showing 13 changed files with 470 additions and 16 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,3 @@ Gemfile.lock

# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc

65 changes: 62 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ params = {
:reply_to,
:date,
:smtpapi,
:attachments
:attachments,
:template
}
```


#### Setting Params

Params can be set in the usual Ruby ways, including a block or a hash.
Expand Down Expand Up @@ -187,9 +187,67 @@ mail = SendGrid::Mail.new
mail.html = '<html><body>Stuff in here, yo!</body></html>'
```

#### :template

## Using SendGrid's X-SMTPAPI Header
The **:template** param allows us to specify a template object for this email to use. The initialized `Template` will automatically be included in the `smtpapi` header and passed to SendGrid.

```ruby
template = SendGrid::Template.new('MY_TEMPLATE_ID')
mail.template = template
```

## Working with Templates

Another easy way to use the [SendGrid Templating](https://sendgrid.com/docs/API_Reference/Web_API_v3/Template_Engine/index.html) system is with the `Recipient`, `Mail`, `Template`, and `TemplateMailer` objects.

Create some `Recipients`

```ruby
users = User.where(email: ['[email protected]', '[email protected]'])

recipients = []

users.each do |user|
recipient = SendGrid::Recipient.new(user.email)
recipient.add_substitution('first_name', user.first_name)
recipient.add_substitution('city', user.city)

recipients << recipient
end
```

Create a `Template`

```ruby
template = SendGrid::Template.new('MY_TEMPLATE_ID')
```

Create a `Client`

```ruby
client = SendGrid::Client.new(api_user: my_user, api_key: my_key)
```

Initialize mail defaults and create the `TemplateMailer`

```ruby
mail_defaults = {
from: '[email protected]',
html: '<h1>I like email</h1>',
text: 'I like email'
subject: 'Email is great',
}

mailer = TemplateMailer.new(client, template, recipients)
```

Mail it!

```ruby
mailer.mail(mail_defaults)
```

## Using SendGrid's X-SMTPAPI Header

<blockquote>
To utilize the X-SMTPAPI header, we have directly integrated the <a href="https://github.com/SendGridJP/smtpapi-ruby">SendGridJP/smtpapi-ruby</a> gem.
Expand Down Expand Up @@ -233,3 +291,4 @@ mail.smtpapi = header
5. Create a new Pull Request

***Hit up [@rbin](http://twitter.com/rbin) or [@sendgrid](http://twitter.com/sendgrid) on Twitter with any issues.***

3 changes: 3 additions & 0 deletions lib/sendgrid-ruby.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
require_relative 'sendgrid/client'
require_relative 'sendgrid/exceptions'
require_relative 'sendgrid/recipient'
require_relative 'sendgrid/template'
require_relative 'sendgrid/template_mailer'
require_relative 'sendgrid/mail'
require_relative 'sendgrid/response'
require_relative 'sendgrid/version'
10 changes: 5 additions & 5 deletions lib/sendgrid/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module SendGrid
class Client
attr_accessor :api_user, :api_key, :protocol, :host, :port, :url, :endpoint,
:user_agent
:user_agent, :template
attr_writer :adapter, :conn, :raise_exceptions

def initialize(params = {})
Expand All @@ -25,7 +25,7 @@ def send(mail)
res = conn.post do |req|
payload = mail.to_h
req.url(endpoint)

# Check if using username + password or API key
if api_user
# Username + password
Expand All @@ -34,12 +34,12 @@ def send(mail)
# API key
req.headers['Authorization'] = "Bearer #{api_key}"
end

req.body = payload
end

fail SendGrid::Exception, res.body if raise_exceptions? && res.status != 200

SendGrid::Response.new(code: res.status, headers: res.headers, body: res.body)
end

Expand Down
23 changes: 16 additions & 7 deletions lib/sendgrid/mail.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
module SendGrid
class Mail
attr_accessor :to, :to_name, :from, :from_name, :subject, :text, :html, :cc,
:bcc, :reply_to, :date, :smtpapi, :attachments, :content
:bcc, :reply_to, :date, :smtpapi, :attachments, :content, :template

def initialize(params = {})
params.each do |k, v|
send(:"#{k}=", v) unless v.nil?
end

yield self if block_given?
end

Expand Down Expand Up @@ -103,11 +104,11 @@ def headers
def attachments
@attachments ||= []
end

def contents
@contents ||= []
end

def add_content(path, cid)
mime_type = MimeMagic.by_path(path)
file = Faraday::UploadIO.new(path, mime_type)
Expand All @@ -119,6 +120,14 @@ def smtpapi
@smtpapi ||= Smtpapi::Header.new
end

def smtpapi_json
if !template.nil? && template.is_a?(Template)
template.add_to_smtpapi(smtpapi)
end

smtpapi.to_json
end

# rubocop:disable Style/HashSyntax
def to_h
payload = {
Expand All @@ -133,7 +142,7 @@ def to_h
:bcc => bcc,
:text => text,
:html => html,
:'x-smtpapi' => smtpapi.to_json,
:'x-smtpapi' => smtpapi_json,
:content => ({":default"=>"0"} unless contents.empty?),
:files => ({":default"=>"0"} unless attachments.empty? and contents.empty?)
# If I don't define a default value, I get a Nil error when
Expand All @@ -144,7 +153,7 @@ def to_h
payload.delete(:'x-smtpapi') if payload[:'x-smtpapi'] == '{}'

payload[:to] = payload[:from] if payload[:to].nil? and not smtpapi.to.empty?

unless attachments.empty?
attachments.each do |file|
payload[:files][file[:name]] = file[:file]
Expand All @@ -153,7 +162,7 @@ def to_h
payload[:files].delete(":default")
end
end

unless contents.empty?
contents.each do |content|
payload[:content][content[:name]] = content[:cid]
Expand All @@ -163,7 +172,7 @@ def to_h
payload[:content].delete(":default")
end
end

payload
end
# rubocop:enable Style/HashSyntax
Expand Down
29 changes: 29 additions & 0 deletions lib/sendgrid/recipient.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'smtpapi'

module SendGrid
class Recipient
class NoAddress < StandardError; end

attr_reader :address, :substitutions

def initialize(address)
@address = address
@substitutions = {}

raise NoAddress, 'Recipient address cannot be nil' if @address.nil?
end

def add_substitution(key, value)
substitutions[key] = value
end

def add_to_smtpapi(smtpapi)
smtpapi.add_to(@address)

@substitutions.each do |key, value|
existing = smtpapi.sub[key] || []
smtpapi.add_substitution(key, existing + [value])
end
end
end
end
26 changes: 26 additions & 0 deletions lib/sendgrid/template.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require 'smtpapi'

module SendGrid
class Template
attr_reader :id, :recipients

def initialize(id)
@id = id
@recipients = []
end

def add_recipient(recipient)
recipients << recipient
end

def add_to_smtpapi(smtpapi)
return if smtpapi.nil?

smtpapi.tap do |api|
api.add_filter(:templates, :enable, 1)
api.add_filter(:templates, :template_id, id)
recipients.each { |r| r.add_to_smtpapi(smtpapi) }
end
end
end
end
59 changes: 59 additions & 0 deletions lib/sendgrid/template_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module SendGrid
class InvalidClient < StandardError; end
class InvalidTemplate < StandardError; end
class InvalidRecipients < StandardError; end

class TemplateMailer

# This class is responsible for coordinating the responsibilities
# of various models in the gem.
# It makes use of the Recipient, Template and Mail models to create
# a single work flow, an example might look like:
#
# users = User.where(email: ['[email protected]', '[email protected]'])
#
# recipients = []
#
# users.each do |user|
# recipient = SendGrid::Recipient.new(user.email)
# recipient.add_substitution('first_name', user.first_name)
# recipient.add_substitution('city', user.city)
#
# recipients << recipient
# end
#
# template = SendGrid::Template.new('MY_TEMPLATE_ID')
#
# client = SendGrid::Client.new(api_user: my_user, api_key: my_key)
#
# mail_defaults = {
# from: '[email protected]',
# html: '<h1>I like email</h1>',
# text: 'I like email'
# subject: 'Email is great',
# }
#
# mailer = TemplateMailer.new(client, template, recipients)
# mailer.mail(mail_defaults)
def initialize(client, template, recipients = [])
@client = client
@template = template
@recipients = recipients

raise InvalidClient, 'Client must be present' if @client.nil?
raise InvalidTemplate, 'Template must be present' if @template.nil?
raise InvalidRecipients, 'Recipients may not be empty' if @recipients.empty?

@recipients.each do |recipient|
@template.add_recipient(recipient)
end
end

def mail(params = {})
mail = Mail.new(params)

mail.template = @template
@client.send(mail.to_h)
end
end
end
4 changes: 4 additions & 0 deletions spec/lib/sendgrid/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
expect(SendGrid::Client.new.endpoint).to eq('/api/mail.send.json')
end

it 'accepts a block' do
expect { |b| SendGrid::Client.new(&b) }.to yield_control
end

describe ':send' do
it 'should make a request to sendgrid' do
stub_request(:any, 'https://api.sendgrid.com/api/mail.send.json')
Expand Down
28 changes: 28 additions & 0 deletions spec/lib/sendgrid/mail_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,32 @@
expect(@mail.reply_to).to eq('[email protected]')
end
end

describe 'smtpapi_json' do
before do
@mail.template = template
end

context 'a template has been set' do
let(:template) { SendGrid::Template.new(anything) }

it 'adds the template to the smtpapi header' do
expect(@mail.template).to receive(:add_to_smtpapi).with(@mail.smtpapi)
expect(@mail.smtpapi).to receive(:to_json)

@mail.to_h
end
end

context 'no template has been set' do
let(:template) { nil }

it 'does not add anything to the smtpapi header' do
expect_any_instance_of(SendGrid::Template).to_not receive(:add_to_smtpapi)
expect(@mail.smtpapi).to receive(:to_json)

@mail.to_h
end
end
end
end
Loading

0 comments on commit f28d913

Please sign in to comment.