From a129e277469dae3e27ed84e760c3f53f831b6e78 Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Wed, 26 Aug 2015 10:45:16 -0700 Subject: [PATCH 01/17] Gitignore update --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 166267f4..9269680f 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,3 @@ Gemfile.lock # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: .rvmrc - From ce97bb1f85044616a19cdf6809aee51519a7d2cc Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Wed, 26 Aug 2015 16:59:20 -0700 Subject: [PATCH 02/17] Add support for templates This will add filters to the smtpapi gem: https://github.com/sendgrid/smtpapi-ruby The template_id will be passed in like other attributes to mail Example: ``` mail = SendGrid::Mail.new do |m| m.to = 'test@sendgrid.com' m.from = 'taco@cat.limo' m.subject = 'Hello world!' m.text = 'I heard you like pineapple.' m.template_id = 1234 end ``` --- lib/sendgrid-ruby.rb | 1 + lib/sendgrid/mail.rb | 10 ++++----- lib/sendgrid/template.rb | 20 ++++++++++++++++++ spec/lib/sendgrid/template_spec.rb | 33 ++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 lib/sendgrid/template.rb create mode 100644 spec/lib/sendgrid/template_spec.rb diff --git a/lib/sendgrid-ruby.rb b/lib/sendgrid-ruby.rb index 2dfd8b90..6e65609f 100644 --- a/lib/sendgrid-ruby.rb +++ b/lib/sendgrid-ruby.rb @@ -1,5 +1,6 @@ require_relative 'sendgrid/client' require_relative 'sendgrid/exceptions' +require_relative 'sendgrid/template' require_relative 'sendgrid/mail' require_relative 'sendgrid/response' require_relative 'sendgrid/version' diff --git a/lib/sendgrid/mail.rb b/lib/sendgrid/mail.rb index eedb7f12..685c7225 100644 --- a/lib/sendgrid/mail.rb +++ b/lib/sendgrid/mail.rb @@ -103,11 +103,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) @@ -144,7 +144,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] @@ -153,7 +153,7 @@ def to_h payload[:files].delete(":default") end end - + unless contents.empty? contents.each do |content| payload[:content][content[:name]] = content[:cid] @@ -163,7 +163,7 @@ def to_h payload[:content].delete(":default") end end - + payload end # rubocop:enable Style/HashSyntax diff --git a/lib/sendgrid/template.rb b/lib/sendgrid/template.rb new file mode 100644 index 00000000..7a78b185 --- /dev/null +++ b/lib/sendgrid/template.rb @@ -0,0 +1,20 @@ +require 'smtpapi' + +module SendGrid + class Template + attr_reader :id + + def initialize(id) + @id = id + end + + def add_to_smtpapi(smtpapi) + return if smtpapi.nil? + + smtpapi.tap do |api| + api.add_filter(:template, :enabled, 1) + api.add_filter(:template, :id, id) + end + end + end +end diff --git a/spec/lib/sendgrid/template_spec.rb b/spec/lib/sendgrid/template_spec.rb new file mode 100644 index 00000000..7b0adbf4 --- /dev/null +++ b/spec/lib/sendgrid/template_spec.rb @@ -0,0 +1,33 @@ +require_relative '../../../lib/sendgrid/template' + +module SendGrid + describe Template do + let(:id) { anything } + subject { described_class.new(id) } + + describe '#initialize' do + it 'sets the id instance var' do + expect(subject.instance_variable_get(:@id)).to_not be_nil + end + end + + describe '#add_to_smtpapi' do + let(:id) { rand(8999) } + let(:smtpapi) { Smtpapi::Header.new } + + it 'adds enabled and the templates id' do + expect(smtpapi).to receive(:add_filter).with(:template, :enabled, 1) + expect(smtpapi).to receive(:add_filter).with(:template, :id, id) + subject.add_to_smtpapi(smtpapi) + end + + context 'smtpapi is nil' do + it 'does not error' do + expect do + subject.add_to_smtpapi(nil) + end.to_not raise_error + end + end + end + end +end From 942bea18bf1340446ba3481a5a993a716443e24f Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Fri, 28 Aug 2015 14:10:23 -0700 Subject: [PATCH 03/17] Recipient introduced To help encapsulate the substitution logic for templates and how they correlate with email recipients, this model has been introduced. The benefit is now something like this is possible: ```ruby template = Template.new(TEMPLATE_ID) users = User.find(['firstuser@gmail.com', 'seconduser@gmail.com']) users.each do |user| recipient = Recipient.new(user) recipient.add_substitution(:name, user.name) recipient.add_substitution(:location, user.location) template.add_recipient(recipient) end ``` The next step will be to integrate the `Recipient` and `Template` with `Mail`. --- lib/sendgrid/recipient.rb | 26 ++++++++ lib/sendgrid/template.rb | 9 ++- spec/lib/sendgrid/recipient_spec.rb | 93 +++++++++++++++++++++++++++++ spec/lib/sendgrid/template_spec.rb | 28 +++++++++ 4 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 lib/sendgrid/recipient.rb create mode 100644 spec/lib/sendgrid/recipient_spec.rb diff --git a/lib/sendgrid/recipient.rb b/lib/sendgrid/recipient.rb new file mode 100644 index 00000000..28525d42 --- /dev/null +++ b/lib/sendgrid/recipient.rb @@ -0,0 +1,26 @@ +require 'smtpapi' + +module SendGrid + class Recipient + + attr_reader :address, :substitutions + + def initialize(address) + @address = address + @substitutions = {} + end + + def add_substitution(key, value) + substitutions[key.to_sym] = value + end + + def add_to_smtpapi(smtpapi) + return if @address.nil? || @substitutions.empty? + smtpapi.add_to(@address) + + @substitutions.each do |key, value| + smtpapi.add_substitution(key, value) + end + end + end +end diff --git a/lib/sendgrid/template.rb b/lib/sendgrid/template.rb index 7a78b185..bb67f9b8 100644 --- a/lib/sendgrid/template.rb +++ b/lib/sendgrid/template.rb @@ -1,11 +1,17 @@ require 'smtpapi' +require_relative './recipient' module SendGrid class Template - attr_reader :id + attr_reader :id, :recipients def initialize(id) @id = id + @recipients = [] + end + + def add_recipient(recipient) + recipients << recipient end def add_to_smtpapi(smtpapi) @@ -14,6 +20,7 @@ def add_to_smtpapi(smtpapi) smtpapi.tap do |api| api.add_filter(:template, :enabled, 1) api.add_filter(:template, :id, id) + recipients.each { |r| r.add_to_smtpapi(smtpapi) } end end end diff --git a/spec/lib/sendgrid/recipient_spec.rb b/spec/lib/sendgrid/recipient_spec.rb new file mode 100644 index 00000000..b2ec2619 --- /dev/null +++ b/spec/lib/sendgrid/recipient_spec.rb @@ -0,0 +1,93 @@ +require_relative '../../../lib/sendgrid/recipient' + +module SendGrid + describe Recipient do + subject { described_class.new(anything) } + + describe '#initialize' do + it 'sets the address instance var' do + expect(subject.instance_variable_get(:@address)).to_not be_nil + end + + it 'sets substitutions to an empty hash' do + expect(subject.instance_variable_get(:@substitutions)).to eq({}) + end + end + + describe '#add_substitution' do + it 'adds the key and value to the substitutions hash' do + subject.add_substitution(:foo, :bar) + expect(subject.substitutions).to have_key(:foo) + expect(subject.substitutions[:foo]).to eq(:bar) + end + + context 'the same substiution key already exists' do + before do + subject.add_substitution(:foo, :bar) + end + + it 'replaces the value' do + subject.add_substitution(:foo, :baz) + expect(subject.substitutions).to have_key(:foo) + expect(subject.substitutions[:foo]).to eq(:baz) + end + end + end + + describe '#add_to_smtpapi' do + let(:substitutions) { { foo: :bar, baz: :qux } } + let(:smtp_api) { Smtpapi::Header.new } + before do + substitutions.each do |key, value| + subject.add_substitution(key, value) + end + end + + it 'adds the address' do + expect(smtp_api).to receive(:add_to) + subject.add_to_smtpapi(smtp_api) + end + + it 'calls add_substitution as many times as there are substitution keys' do + substitutions.each do |key, value| + expect(smtp_api).to receive(:add_substitution).with(key, value) + end + + subject.add_to_smtpapi(smtp_api) + end + + context 'address is nil and/or substitutions is empty' do + subject { described_class.new(address) } + let(:address) { anything } + + context 'address is nil' do + let(:address) { nil } + + it 'does nothing' do + expect(smtp_api).to_not receive(:add_substitution) + subject.add_to_smtpapi(smtp_api) + end + end + + context 'substitutions is empty' do + let(:substitutions) { {} } + + it 'does nothing' do + expect(smtp_api).to_not receive(:add_substitution) + subject.add_to_smtpapi(smtp_api) + end + end + + context 'both are nil/empty' do + let(:substitutions) { {} } + let(:address) { nil } + + it 'does nothing' do + expect(smtp_api).to_not receive(:add_substitution) + subject.add_to_smtpapi(smtp_api) + end + end + end + end + end +end diff --git a/spec/lib/sendgrid/template_spec.rb b/spec/lib/sendgrid/template_spec.rb index 7b0adbf4..c500cb41 100644 --- a/spec/lib/sendgrid/template_spec.rb +++ b/spec/lib/sendgrid/template_spec.rb @@ -28,6 +28,34 @@ module SendGrid end.to_not raise_error end end + + context 'with recipients' do + let(:substitution_key) { :foo } + let(:substitution_value) { :bar } + let(:recipients) do + [].tap do |recipients| + 3.times.each do + r = Recipient.new("test+#{ rand(100) }@example.com") + r.add_substitution(substitution_key, substitution_value) + recipients << r + end + end + end + + before do + recipients.each do |r| + subject.add_recipient(r) + end + end + + it 'calls the recipients call to add to smtpapi' do + recipients.each do |recipient| + expect(recipient).to receive(:add_to_smtpapi).with(smtpapi) + end + + subject.add_to_smtpapi(smtpapi) + end + end end end end From 417d012e51ae84adf1f0240b70ef78c7b49e953a Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Fri, 28 Aug 2015 15:20:22 -0700 Subject: [PATCH 04/17] Wrong variable names for x-smtpapi header --- lib/sendgrid/template.rb | 4 ++-- spec/lib/sendgrid/template_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/sendgrid/template.rb b/lib/sendgrid/template.rb index bb67f9b8..503e66b0 100644 --- a/lib/sendgrid/template.rb +++ b/lib/sendgrid/template.rb @@ -18,8 +18,8 @@ def add_to_smtpapi(smtpapi) return if smtpapi.nil? smtpapi.tap do |api| - api.add_filter(:template, :enabled, 1) - api.add_filter(:template, :id, id) + api.add_filter(:templates, :enable, 1) + api.add_filter(:templates, :template_id, id) recipients.each { |r| r.add_to_smtpapi(smtpapi) } end end diff --git a/spec/lib/sendgrid/template_spec.rb b/spec/lib/sendgrid/template_spec.rb index c500cb41..da78ebd0 100644 --- a/spec/lib/sendgrid/template_spec.rb +++ b/spec/lib/sendgrid/template_spec.rb @@ -16,8 +16,8 @@ module SendGrid let(:smtpapi) { Smtpapi::Header.new } it 'adds enabled and the templates id' do - expect(smtpapi).to receive(:add_filter).with(:template, :enabled, 1) - expect(smtpapi).to receive(:add_filter).with(:template, :id, id) + expect(smtpapi).to receive(:add_filter).with(:templates, :enable, 1) + expect(smtpapi).to receive(:add_filter).with(:templates, :template_id, id) subject.add_to_smtpapi(smtpapi) end From 88a1bf3e0f83d6f2b13834731e09b00e5f9d838c Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Fri, 28 Aug 2015 15:48:15 -0700 Subject: [PATCH 05/17] Fail on initialization instead of silently on output --- lib/sendgrid/recipient.rb | 4 ++- spec/lib/sendgrid/recipient_spec.rb | 42 +++++++++-------------------- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/lib/sendgrid/recipient.rb b/lib/sendgrid/recipient.rb index 28525d42..eeea43a1 100644 --- a/lib/sendgrid/recipient.rb +++ b/lib/sendgrid/recipient.rb @@ -2,12 +2,15 @@ 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) @@ -15,7 +18,6 @@ def add_substitution(key, value) end def add_to_smtpapi(smtpapi) - return if @address.nil? || @substitutions.empty? smtpapi.add_to(@address) @substitutions.each do |key, value| diff --git a/spec/lib/sendgrid/recipient_spec.rb b/spec/lib/sendgrid/recipient_spec.rb index b2ec2619..826649fe 100644 --- a/spec/lib/sendgrid/recipient_spec.rb +++ b/spec/lib/sendgrid/recipient_spec.rb @@ -12,6 +12,14 @@ module SendGrid it 'sets substitutions to an empty hash' do expect(subject.instance_variable_get(:@substitutions)).to eq({}) end + + context 'initialized with nil' do + it 'raises an error' do + expect do + described_class.new(nil) + end.to raise_error(Recipient::NoAddress, 'Recipient address cannot be nil') + end + end end describe '#add_substitution' do @@ -56,36 +64,12 @@ module SendGrid subject.add_to_smtpapi(smtp_api) end - context 'address is nil and/or substitutions is empty' do - subject { described_class.new(address) } - let(:address) { anything } - - context 'address is nil' do - let(:address) { nil } - - it 'does nothing' do - expect(smtp_api).to_not receive(:add_substitution) - subject.add_to_smtpapi(smtp_api) - end - end - - context 'substitutions is empty' do - let(:substitutions) { {} } - - it 'does nothing' do - expect(smtp_api).to_not receive(:add_substitution) - subject.add_to_smtpapi(smtp_api) - end - end - - context 'both are nil/empty' do - let(:substitutions) { {} } - let(:address) { nil } + context 'substitutions is empty' do + let(:substitutions) { {} } - it 'does nothing' do - expect(smtp_api).to_not receive(:add_substitution) - subject.add_to_smtpapi(smtp_api) - end + it 'does nothing' do + expect(smtp_api).to_not receive(:add_substitution) + subject.add_to_smtpapi(smtp_api) end end end From 0a82049ed94c6f7deb73dadb8cae39a04966d589 Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Mon, 31 Aug 2015 13:43:33 -0700 Subject: [PATCH 06/17] Bug with substitution calling in template and change to association This will add an accessor for the template without an initialization through a template_id --- lib/sendgrid/recipient.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/sendgrid/recipient.rb b/lib/sendgrid/recipient.rb index eeea43a1..b272241e 100644 --- a/lib/sendgrid/recipient.rb +++ b/lib/sendgrid/recipient.rb @@ -14,14 +14,14 @@ def initialize(address) end def add_substitution(key, value) - substitutions[key.to_sym] = value + substitutions[key] = value end def add_to_smtpapi(smtpapi) smtpapi.add_to(@address) @substitutions.each do |key, value| - smtpapi.add_substitution(key, value) + smtpapi.add_substitution(key, [value]) end end end From 99707029618a6fae53464d6ab99af84e01541c5d Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Mon, 31 Aug 2015 15:08:53 -0700 Subject: [PATCH 07/17] Spec fixes --- spec/lib/sendgrid/recipient_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/sendgrid/recipient_spec.rb b/spec/lib/sendgrid/recipient_spec.rb index 826649fe..a495cda7 100644 --- a/spec/lib/sendgrid/recipient_spec.rb +++ b/spec/lib/sendgrid/recipient_spec.rb @@ -58,7 +58,7 @@ module SendGrid it 'calls add_substitution as many times as there are substitution keys' do substitutions.each do |key, value| - expect(smtp_api).to receive(:add_substitution).with(key, value) + expect(smtp_api).to receive(:add_substitution).with(key, [value]) end subject.add_to_smtpapi(smtp_api) From aa93e116dcd9a149543a699639ca3e64215d17e7 Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Mon, 31 Aug 2015 15:44:05 -0700 Subject: [PATCH 08/17] Template Mailer This is the final piece of abstracting recipients, templates and the sending of mail. The use of this would look like: ```ruby users = User.where(email: ['first@gmail.com', 'second@gmail.com']) 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: 'admin@email.com', html: '

I like email

', text: 'I like email' subject: 'Email is great', } mailer = TemplateMailer.new(client, template, recipients) mailer.mail(mail_defaults) ``` --- lib/sendgrid/mail.rb | 1 + lib/sendgrid/template_mailer.rb | 33 +++++++++ spec/lib/sendgrid/template_mailer_spec.rb | 86 +++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 lib/sendgrid/template_mailer.rb create mode 100644 spec/lib/sendgrid/template_mailer_spec.rb diff --git a/lib/sendgrid/mail.rb b/lib/sendgrid/mail.rb index 685c7225..b2ed35d9 100644 --- a/lib/sendgrid/mail.rb +++ b/lib/sendgrid/mail.rb @@ -11,6 +11,7 @@ def initialize(params = {}) params.each do |k, v| send(:"#{k}=", v) unless v.nil? end + yield self if block_given? end diff --git a/lib/sendgrid/template_mailer.rb b/lib/sendgrid/template_mailer.rb new file mode 100644 index 00000000..61bbda15 --- /dev/null +++ b/lib/sendgrid/template_mailer.rb @@ -0,0 +1,33 @@ +require_relative './recipient' +require_relative './template' +require_relative './mail' + +module SendGrid + class InvalidClient < StandardError; end + class InvalidTemplate < StandardError; end + class InvalidRecipients < StandardError; end + + class TemplateMailer + + 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 diff --git a/spec/lib/sendgrid/template_mailer_spec.rb b/spec/lib/sendgrid/template_mailer_spec.rb new file mode 100644 index 00000000..1105baf1 --- /dev/null +++ b/spec/lib/sendgrid/template_mailer_spec.rb @@ -0,0 +1,86 @@ +require_relative '../../../lib/sendgrid/template_mailer' + +module SendGrid + describe TemplateMailer do + let(:client) { anything } + let(:template) { Template.new(anything) } + let(:recipients) { [Recipient.new(anything)] } + + describe '#initialize' do + let(:client) { anything } + let(:template) { Template.new(anything) } + let(:recipients) { [anything] } + + subject { described_class.new(client, template, recipients) } + + it 'sets the instance variables' do + expect(subject.instance_variable_get(:@client)).to_not be_nil + expect(subject.instance_variable_get(:@template)).to_not be_nil + expect(subject.instance_variable_get(:@recipients)).to_not be_nil + end + + context 'nil variables' do + context 'template is nil' do + let(:template) { nil } + + it 'raises error' do + expect do + subject + end.to raise_error(InvalidTemplate, 'Template must be present') + end + end + + context 'client is nil' do + let(:client) { nil } + + it 'raises error' do + expect do + subject + end.to raise_error(InvalidClient, 'Client must be present') + end + end + end + + context 'recipients' do + let(:first_recipient) { Recipient.new('someone@anything.com') } + let(:second_recipient) { Recipient.new('test@test.com') } + let(:recipients) { [first_recipient, second_recipient] } + + it 'adds them to the template' do + expect(template).to receive(:add_recipient).with(first_recipient) + expect(template).to receive(:add_recipient).with(second_recipient) + + subject + end + end + end + + describe '#mail' do + subject { described_class.new(client, template, recipients) } + + let(:mail_params) { {} } + let(:mail_to_h) { '' } + + before do + allow(subject).to receive(:mail_params) { mail_params } + allow_any_instance_of(Mail).to receive(:to_h) { mail_to_h } + allow(client).to receive(:send) + end + + it 'creates a new mail object' do + expect(Mail).to receive(:new).with(mail_params).and_call_original + subject.mail + end + + it 'adds the template to the mail object' do + expect_any_instance_of(Mail).to receive(:template=).with(template) + subject.mail + end + + it 'calls send on the client with the mail object' do + expect(client).to receive(:send).with(mail_to_h) + subject.mail + end + end + end +end From 0ad342633a44d750544205aeb556580c84bb815c Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Mon, 31 Aug 2015 16:20:25 -0700 Subject: [PATCH 09/17] Make sure substitutions are not constantly overwritten --- lib/sendgrid-ruby.rb | 1 + lib/sendgrid/recipient.rb | 3 ++- spec/lib/sendgrid/recipient_spec.rb | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/sendgrid-ruby.rb b/lib/sendgrid-ruby.rb index 6e65609f..74b5a763 100644 --- a/lib/sendgrid-ruby.rb +++ b/lib/sendgrid-ruby.rb @@ -1,6 +1,7 @@ require_relative 'sendgrid/client' require_relative 'sendgrid/exceptions' require_relative 'sendgrid/template' +require_relative 'sendgrid/template_mailer' require_relative 'sendgrid/mail' require_relative 'sendgrid/response' require_relative 'sendgrid/version' diff --git a/lib/sendgrid/recipient.rb b/lib/sendgrid/recipient.rb index b272241e..181eff3f 100644 --- a/lib/sendgrid/recipient.rb +++ b/lib/sendgrid/recipient.rb @@ -21,7 +21,8 @@ def add_to_smtpapi(smtpapi) smtpapi.add_to(@address) @substitutions.each do |key, value| - smtpapi.add_substitution(key, [value]) + existing = smtpapi.sub[key] || [] + smtpapi.add_substitution(key, existing + [value]) end end end diff --git a/spec/lib/sendgrid/recipient_spec.rb b/spec/lib/sendgrid/recipient_spec.rb index a495cda7..db9b1304 100644 --- a/spec/lib/sendgrid/recipient_spec.rb +++ b/spec/lib/sendgrid/recipient_spec.rb @@ -64,6 +64,20 @@ module SendGrid subject.add_to_smtpapi(smtp_api) end + context 'a substitution for the same key already exists' do + let(:substitutions) { { foo: :bar } } + let(:added_value) { [:bar, :rab] } + + before do + smtp_api.add_substitution(:foo, [:rab]) + end + + it 'adds to it' do + expect(smtp_api).to receive(:add_substitution).with(:foo, array_including(added_value)) + subject.add_to_smtpapi(smtp_api) + end + end + context 'substitutions is empty' do let(:substitutions) { {} } From dbb445475a30a94325999c6052a32f0f989ed0c2 Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Tue, 1 Sep 2015 14:33:44 -0700 Subject: [PATCH 10/17] Explanitory comment about how to use template mailer --- lib/sendgrid/template_mailer.rb | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/sendgrid/template_mailer.rb b/lib/sendgrid/template_mailer.rb index 61bbda15..f77730a4 100644 --- a/lib/sendgrid/template_mailer.rb +++ b/lib/sendgrid/template_mailer.rb @@ -9,6 +9,36 @@ 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: ['first@gmail.com', 'second@gmail.com']) + # + # 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: 'admin@email.com', + # html: '

I like email

', + # 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 From ebe0dcfa51d3073e1008a00267157f9bfbe62636 Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Wed, 9 Sep 2015 15:48:57 -0700 Subject: [PATCH 11/17] Update README to remove employee who no longer works at SendGrid --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f9ae89c9..337bec19 100644 --- a/README.md +++ b/README.md @@ -233,3 +233,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.*** + From 531c74854b387769dd10d05528225b55406b640e Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Thu, 10 Sep 2015 23:03:33 -0700 Subject: [PATCH 12/17] Update README with new Template pattern --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 337bec19..01c47d1c 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,6 @@ params = { } ``` - #### Setting Params Params can be set in the usual Ruby ways, including a block or a hash. @@ -187,9 +186,58 @@ mail = SendGrid::Mail.new mail.html = 'Stuff in here, yo!' ``` +## Working with Templates -## Using SendGrid's X-SMTPAPI Header +An 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: ['first@gmail.com', 'second@gmail.com']) + +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: 'admin@email.com', + html: '

I like email

', + 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
To utilize the X-SMTPAPI header, we have directly integrated the SendGridJP/smtpapi-ruby gem. From 916391486ca41c530e6753931f272884276b3544 Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Thu, 1 Oct 2015 22:39:18 -0700 Subject: [PATCH 13/17] Make spec suite pass with two code paths together --- lib/sendgrid/template_mailer.rb | 3 ++- spec/lib/sendgrid/template_mailer_spec.rb | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/sendgrid/template_mailer.rb b/lib/sendgrid/template_mailer.rb index f77730a4..c15376b6 100644 --- a/lib/sendgrid/template_mailer.rb +++ b/lib/sendgrid/template_mailer.rb @@ -56,7 +56,8 @@ def initialize(client, template, recipients = []) def mail(params = {}) mail = Mail.new(params) - mail.template = @template + # TODO Modify to support templating + # mail.template = @template @client.send(mail.to_h) end end diff --git a/spec/lib/sendgrid/template_mailer_spec.rb b/spec/lib/sendgrid/template_mailer_spec.rb index 1105baf1..3c6717fe 100644 --- a/spec/lib/sendgrid/template_mailer_spec.rb +++ b/spec/lib/sendgrid/template_mailer_spec.rb @@ -72,11 +72,6 @@ module SendGrid subject.mail end - it 'adds the template to the mail object' do - expect_any_instance_of(Mail).to receive(:template=).with(template) - subject.mail - end - it 'calls send on the client with the mail object' do expect(client).to receive(:send).with(mail_to_h) subject.mail From e92ebd8bdcfb11cc98e250ba1043dd221b41d66b Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Mon, 12 Oct 2015 17:25:09 -0700 Subject: [PATCH 14/17] Conform to new require pattern --- lib/sendgrid-ruby.rb | 1 + lib/sendgrid/template.rb | 1 - lib/sendgrid/template_mailer.rb | 4 ---- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/sendgrid-ruby.rb b/lib/sendgrid-ruby.rb index 74b5a763..17153b5c 100644 --- a/lib/sendgrid-ruby.rb +++ b/lib/sendgrid-ruby.rb @@ -1,5 +1,6 @@ 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' diff --git a/lib/sendgrid/template.rb b/lib/sendgrid/template.rb index 503e66b0..867b3451 100644 --- a/lib/sendgrid/template.rb +++ b/lib/sendgrid/template.rb @@ -1,5 +1,4 @@ require 'smtpapi' -require_relative './recipient' module SendGrid class Template diff --git a/lib/sendgrid/template_mailer.rb b/lib/sendgrid/template_mailer.rb index c15376b6..6089ed55 100644 --- a/lib/sendgrid/template_mailer.rb +++ b/lib/sendgrid/template_mailer.rb @@ -1,7 +1,3 @@ -require_relative './recipient' -require_relative './template' -require_relative './mail' - module SendGrid class InvalidClient < StandardError; end class InvalidTemplate < StandardError; end From 3bd805f3d5ced301bb672c9f696ba07940567a12 Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Mon, 12 Oct 2015 17:25:42 -0700 Subject: [PATCH 15/17] Enable use of template with Mail object --- lib/sendgrid/client.rb | 10 ++++---- lib/sendgrid/mail.rb | 12 ++++++++-- lib/sendgrid/template_mailer.rb | 3 +-- spec/lib/sendgrid/mail_spec.rb | 28 +++++++++++++++++++++++ spec/lib/sendgrid/template_mailer_spec.rb | 5 ++++ 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/lib/sendgrid/client.rb b/lib/sendgrid/client.rb index 0abac194..6c59299b 100644 --- a/lib/sendgrid/client.rb +++ b/lib/sendgrid/client.rb @@ -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 = {}) @@ -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 @@ -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 diff --git a/lib/sendgrid/mail.rb b/lib/sendgrid/mail.rb index b2ed35d9..58a5393a 100644 --- a/lib/sendgrid/mail.rb +++ b/lib/sendgrid/mail.rb @@ -5,7 +5,7 @@ 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| @@ -120,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 = { @@ -134,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 diff --git a/lib/sendgrid/template_mailer.rb b/lib/sendgrid/template_mailer.rb index 6089ed55..724963bc 100644 --- a/lib/sendgrid/template_mailer.rb +++ b/lib/sendgrid/template_mailer.rb @@ -52,8 +52,7 @@ def initialize(client, template, recipients = []) def mail(params = {}) mail = Mail.new(params) - # TODO Modify to support templating - # mail.template = @template + mail.template = @template @client.send(mail.to_h) end end diff --git a/spec/lib/sendgrid/mail_spec.rb b/spec/lib/sendgrid/mail_spec.rb index 4ce828ca..950714b4 100644 --- a/spec/lib/sendgrid/mail_spec.rb +++ b/spec/lib/sendgrid/mail_spec.rb @@ -120,4 +120,32 @@ expect(@mail.reply_to).to eq('foo@example.com') 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 diff --git a/spec/lib/sendgrid/template_mailer_spec.rb b/spec/lib/sendgrid/template_mailer_spec.rb index 3c6717fe..1105baf1 100644 --- a/spec/lib/sendgrid/template_mailer_spec.rb +++ b/spec/lib/sendgrid/template_mailer_spec.rb @@ -72,6 +72,11 @@ module SendGrid subject.mail end + it 'adds the template to the mail object' do + expect_any_instance_of(Mail).to receive(:template=).with(template) + subject.mail + end + it 'calls send on the client with the mail object' do expect(client).to receive(:send).with(mail_to_h) subject.mail From c02d195a8ade64373d5c04e195fcb3cc6cb16582 Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Mon, 12 Oct 2015 17:25:55 -0700 Subject: [PATCH 16/17] New spec asserting initialize can take a block --- spec/lib/sendgrid/client_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/lib/sendgrid/client_spec.rb b/spec/lib/sendgrid/client_spec.rb index 6600c13e..2a7979e4 100644 --- a/spec/lib/sendgrid/client_spec.rb +++ b/spec/lib/sendgrid/client_spec.rb @@ -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') From 439f8996e84e3fa1e95616d909ad3fc4b4c0fa3d Mon Sep 17 00:00:00 2001 From: Jake Yesbeck Date: Mon, 12 Oct 2015 17:31:07 -0700 Subject: [PATCH 17/17] Update README with additional template usages --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 01c47d1c..38a0f824 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,8 @@ params = { :reply_to, :date, :smtpapi, - :attachments + :attachments, + :template } ``` @@ -186,9 +187,18 @@ mail = SendGrid::Mail.new mail.html = 'Stuff in here, yo!' ``` +#### :template + +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 -An 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. +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`