Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend Gem with additional Template and Recipient Functionality (round 2) #28

Merged
merged 17 commits into from
Oct 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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