send_grid_mailer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +192 -0
- data/Guardfile +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +209 -0
- data/Rakefile +10 -0
- data/lib/send_grid_mailer.rb +27 -0
- data/lib/send_grid_mailer/definition.rb +110 -0
- data/lib/send_grid_mailer/deliverer.rb +46 -0
- data/lib/send_grid_mailer/engine.rb +23 -0
- data/lib/send_grid_mailer/errors.rb +2 -0
- data/lib/send_grid_mailer/logger.rb +99 -0
- data/lib/send_grid_mailer/mail_message_ext.rb +7 -0
- data/lib/send_grid_mailer/mailer_base_ext.rb +88 -0
- data/lib/send_grid_mailer/version.rb +3 -0
- data/lib/tasks/send_grid_mailer_tasks.rake +4 -0
- data/send_grid_mailer.gemspec +31 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/application_mailer.rb +4 -0
- data/spec/dummy/app/mailers/test_mailer.rb +86 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/app/views/layouts/mailer.html.erb +5 -0
- data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/spec/dummy/app/views/test_mailer/rails_tpl_email.html.erb +1 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +32 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +46 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +56 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/schema.rb +16 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +1984 -0
- data/spec/dummy/log/test.log +58252 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/spec/assets/image.png +0 -0
- data/spec/dummy/spec/assets/video.mp4 +0 -0
- data/spec/dummy/spec/lib/send_grid_mailer/definition_spec.rb +102 -0
- data/spec/dummy/spec/mailers/test_mailer_spec.rb +423 -0
- data/spec/dummy/spec/support/test_helpers.rb +5 -0
- data/spec/rails_helper.rb +28 -0
- data/spec/spec_helper.rb +9 -0
- metadata +313 -0
data/Rakefile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
begin
|
2
|
+
require "bundler/setup"
|
3
|
+
rescue LoadError
|
4
|
+
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
5
|
+
end
|
6
|
+
|
7
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
8
|
+
load "rails/tasks/engine.rake"
|
9
|
+
load "rails/tasks/statistics.rake"
|
10
|
+
Bundler::GemHelper.install_tasks
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "colorize"
|
2
|
+
require "sendgrid-ruby"
|
3
|
+
require "send_grid_mailer/engine"
|
4
|
+
|
5
|
+
module SendGridMailer
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# You can add, in this module, your own configuration options as in the example below...
|
9
|
+
#
|
10
|
+
# attr_writer :my_option
|
11
|
+
#
|
12
|
+
# def my_option
|
13
|
+
# return "Default Value" unless @my_option
|
14
|
+
# @my_option
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# Then, you can customize the default behaviour (typically in a Rails initializer) like this:
|
18
|
+
#
|
19
|
+
# SendGridMailer.setup do |config|
|
20
|
+
# config.root_url = "Another value"
|
21
|
+
# end
|
22
|
+
|
23
|
+
def setup
|
24
|
+
yield self
|
25
|
+
require "send_grid_mailer"
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module SendGridMailer
|
2
|
+
class Definition
|
3
|
+
METHODS = [
|
4
|
+
:substitute,
|
5
|
+
:set_template_id,
|
6
|
+
:set_template_name,
|
7
|
+
:set_sender,
|
8
|
+
:set_recipients,
|
9
|
+
:set_subject,
|
10
|
+
:set_content,
|
11
|
+
:add_attachment,
|
12
|
+
:add_header,
|
13
|
+
:content?,
|
14
|
+
:sender?,
|
15
|
+
:subject?
|
16
|
+
]
|
17
|
+
|
18
|
+
attr_accessor :template_name
|
19
|
+
|
20
|
+
def substitute(key, value, default = "")
|
21
|
+
personalization.substitutions = SendGrid::Substitution.new(
|
22
|
+
key: key, value: value.to_s || default)
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_template_id(value)
|
26
|
+
return unless value
|
27
|
+
mail.template_id = value
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_template_name(value)
|
31
|
+
return unless value
|
32
|
+
self.template_name = value
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_sender(email)
|
36
|
+
return unless email
|
37
|
+
mail.from = SendGrid::Email.new(email: email)
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_recipients(mode, *emails)
|
41
|
+
emails.flatten.each do |email|
|
42
|
+
next unless email
|
43
|
+
personalization.send("#{mode}=", SendGrid::Email.new(email: email))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_subject(value)
|
48
|
+
return unless value
|
49
|
+
personalization.subject = value
|
50
|
+
end
|
51
|
+
|
52
|
+
def set_content(value, type = nil)
|
53
|
+
return unless value
|
54
|
+
type = "text/plain" unless type
|
55
|
+
mail.contents = SendGrid::Content.new(type: type, value: value)
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_attachment(file, name, type, disposition = "inline", content_id = nil)
|
59
|
+
attachment = SendGrid::Attachment.new
|
60
|
+
attachment.content = Base64.strict_encode64(file)
|
61
|
+
attachment.type = type
|
62
|
+
attachment.filename = name
|
63
|
+
attachment.disposition = disposition
|
64
|
+
attachment.content_id = content_id
|
65
|
+
mail.attachments = attachment
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_header(key, value)
|
69
|
+
return if !key || !value
|
70
|
+
personalization.headers = SendGrid::Header.new(key: key, value: value)
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_json
|
74
|
+
mail.personalizations = personalization if personalization?
|
75
|
+
mail.to_json
|
76
|
+
end
|
77
|
+
|
78
|
+
def mail
|
79
|
+
@mail ||= SendGrid::Mail.new
|
80
|
+
end
|
81
|
+
|
82
|
+
def personalization
|
83
|
+
@personalization ||= SendGrid::Personalization.new
|
84
|
+
end
|
85
|
+
|
86
|
+
def personalization?
|
87
|
+
!personalization.to_json.empty?
|
88
|
+
end
|
89
|
+
|
90
|
+
def content?
|
91
|
+
!mail.contents.blank?
|
92
|
+
end
|
93
|
+
|
94
|
+
def sender?
|
95
|
+
!mail.from.blank?
|
96
|
+
end
|
97
|
+
|
98
|
+
def subject?
|
99
|
+
!personalization.subject.blank?
|
100
|
+
end
|
101
|
+
|
102
|
+
def template_id?
|
103
|
+
!mail.template_id.blank?
|
104
|
+
end
|
105
|
+
|
106
|
+
def template_name?
|
107
|
+
!template_name.blank?
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module SendGridMailer
|
2
|
+
class Deliverer
|
3
|
+
attr_accessor :settings
|
4
|
+
|
5
|
+
def initialize(settings)
|
6
|
+
self.settings = settings.merge(return_response: true)
|
7
|
+
end
|
8
|
+
|
9
|
+
def api_key
|
10
|
+
settings[:api_key] || raise(SendGridMailer::Exception.new("Missing sendgrid API key"))
|
11
|
+
end
|
12
|
+
|
13
|
+
def deliver!(msg)
|
14
|
+
set_template_id_from_name(msg.sg_definition)
|
15
|
+
logger = SendGridMailer::Logger.new(msg.sg_definition)
|
16
|
+
logger.log_definition
|
17
|
+
response = sg_api.client.mail._('send').post(request_body: msg.sg_definition.to_json)
|
18
|
+
logger.log_result(response)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def set_template_id_from_name(definition)
|
24
|
+
return unless definition.template_name
|
25
|
+
response = sg_api.client.templates.get
|
26
|
+
|
27
|
+
if response.status_code != "200"
|
28
|
+
raise(SendGridMailer::Exception.new(
|
29
|
+
"Error trying to get templates. Status Code: #{response.status_code}"))
|
30
|
+
end
|
31
|
+
|
32
|
+
JSON.parse(response.body)["templates"].each do |tpl|
|
33
|
+
definition.set_template_id(tpl["id"]) if tpl["name"] == definition.template_name
|
34
|
+
end
|
35
|
+
|
36
|
+
if !definition.template_id?
|
37
|
+
raise(SendGridMailer::Exception.new(
|
38
|
+
"No template with name #{definition.template_name}"))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def sg_api
|
43
|
+
@sg_api ||= SendGrid::API.new(api_key: api_key)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SendGridMailer
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace SendGridMailer
|
4
|
+
|
5
|
+
config.generators do |g|
|
6
|
+
g.test_framework :rspec, fixture: false
|
7
|
+
g.fixture_replacement :factory_girl, dir: "spec/factories"
|
8
|
+
end
|
9
|
+
|
10
|
+
initializer "initialize" do
|
11
|
+
require_relative "./errors"
|
12
|
+
require_relative "./logger"
|
13
|
+
require_relative "./definition"
|
14
|
+
require_relative "./mail_message_ext"
|
15
|
+
require_relative "./mailer_base_ext"
|
16
|
+
end
|
17
|
+
|
18
|
+
initializer "add_sendgrid_deliverer", before: "action_mailer.set_configs" do
|
19
|
+
require_relative "./deliverer"
|
20
|
+
ActionMailer::Base.add_delivery_method(:sendgrid, SendGridMailer::Deliverer)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module SendGridMailer
|
2
|
+
class Logger
|
3
|
+
attr_reader :definition
|
4
|
+
|
5
|
+
def initialize(definition)
|
6
|
+
@definition = definition
|
7
|
+
end
|
8
|
+
|
9
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
10
|
+
def log_definition
|
11
|
+
data = {
|
12
|
+
"Subject" => personalization.subject,
|
13
|
+
"Template ID" => mail.template_id,
|
14
|
+
"From" => log_email(mail.from),
|
15
|
+
"To" => log_emails(:tos),
|
16
|
+
"Cc" => log_emails(:ccs),
|
17
|
+
"Bcc" => log_emails(:bccs),
|
18
|
+
"Substitutions" => log_pairs(personalization.substitutions),
|
19
|
+
"Headers" => log_pairs(personalization.headers),
|
20
|
+
"body" => log_contents,
|
21
|
+
"Attachments" => log_attachments
|
22
|
+
}
|
23
|
+
|
24
|
+
data = data.keys.map do |k|
|
25
|
+
d = data[k].to_s
|
26
|
+
"#{k.light_blue}: #{(d.blank? ? '-' : d).light_yellow}"
|
27
|
+
end.join("\n")
|
28
|
+
|
29
|
+
Rails.logger.info("\n#{data}")
|
30
|
+
end
|
31
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
32
|
+
|
33
|
+
def log_result(response)
|
34
|
+
msg = "The E-mail was successfully sent :)\nStatus Code: #{response.status_code}".green
|
35
|
+
|
36
|
+
if response.status_code != "202"
|
37
|
+
msg = "The E-mail was not sent :(\nStatus Code: #{response.status_code}\nErrors:"
|
38
|
+
msg += log_errors(response.body)
|
39
|
+
msg = msg.red
|
40
|
+
end
|
41
|
+
|
42
|
+
Rails.logger.info("\n#{msg}")
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def log_email(email)
|
49
|
+
email["email"]
|
50
|
+
end
|
51
|
+
|
52
|
+
def log_emails(origin)
|
53
|
+
emails = personalization.send(origin)
|
54
|
+
return if emails.blank?
|
55
|
+
emails.map do |email|
|
56
|
+
log_email(email)
|
57
|
+
end.join(", ")
|
58
|
+
end
|
59
|
+
|
60
|
+
def log_attachments
|
61
|
+
return if mail.attachments.blank?
|
62
|
+
mail.attachments.map do |f|
|
63
|
+
"\n\t#{f['filename']}"
|
64
|
+
end.join("")
|
65
|
+
end
|
66
|
+
|
67
|
+
def log_contents
|
68
|
+
return if mail.contents.blank?
|
69
|
+
mail.contents.map do |content|
|
70
|
+
"\n\ttype: #{content['type']}\n\tvalue: #{content['value']}"
|
71
|
+
end.join("")
|
72
|
+
end
|
73
|
+
|
74
|
+
def log_pairs(hash)
|
75
|
+
return if hash.blank?
|
76
|
+
hash.keys.map do |k|
|
77
|
+
"\n\t#{k} => #{hash[k]}"
|
78
|
+
end.join("")
|
79
|
+
end
|
80
|
+
|
81
|
+
def mail
|
82
|
+
definition.mail
|
83
|
+
end
|
84
|
+
|
85
|
+
def log_errors(body)
|
86
|
+
JSON.parse(body)["errors"].map do |error|
|
87
|
+
msg = []
|
88
|
+
msg << "#{error['field']}: " if error['field']
|
89
|
+
msg << error['message']
|
90
|
+
msg << " - help: #{error['help']}" if error['help']
|
91
|
+
"\n\t* #{msg.join('')}"
|
92
|
+
end.join("")
|
93
|
+
end
|
94
|
+
|
95
|
+
def personalization
|
96
|
+
definition.personalization
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module ActionMailer
|
2
|
+
class Base < AbstractController::Base
|
3
|
+
alias_method :old_mail, :mail
|
4
|
+
|
5
|
+
SendGridMailer::Definition::METHODS.each do |method_name|
|
6
|
+
define_method(method_name) do |*args|
|
7
|
+
sg_definition.send(method_name, *args)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def mail(headers = {}, &_block)
|
12
|
+
return old_mail(headers, &_block) if self.class.delivery_method != :sendgrid
|
13
|
+
m = @_message
|
14
|
+
|
15
|
+
# Call all the procs (if any)
|
16
|
+
default_values = {}
|
17
|
+
self.class.default.each do |k, v|
|
18
|
+
default_values[k] = v.is_a?(Proc) ? instance_eval(&v) : v
|
19
|
+
end
|
20
|
+
|
21
|
+
# Handle defaults
|
22
|
+
headers = headers.reverse_merge(default_values)
|
23
|
+
headers[:subject] ||= default_i18n_subject
|
24
|
+
|
25
|
+
define_sg_mail(headers)
|
26
|
+
|
27
|
+
wrap_delivery_behavior!
|
28
|
+
@_mail_was_called = true
|
29
|
+
m
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def define_sg_mail(params = {})
|
35
|
+
set_sender(params[:from]) unless sender?
|
36
|
+
set_recipients(:to, params[:to])
|
37
|
+
set_recipients(:cc, params[:cc])
|
38
|
+
set_recipients(:bcc, params[:bcc])
|
39
|
+
set_subject(params[:subject]) unless subject?
|
40
|
+
set_body(params)
|
41
|
+
add_attachments
|
42
|
+
add_headers(params.fetch(:headers, {}))
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_attachments
|
46
|
+
attachments.each do |attachment|
|
47
|
+
add_attachment(
|
48
|
+
attachment.read,
|
49
|
+
attachment.filename,
|
50
|
+
attachment.content_type.to_s.split(";").first,
|
51
|
+
((attachment.content_disposition =~ /inline/) ? 'inline' : 'attachment'),
|
52
|
+
attachment.content_id
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_headers(heads = {})
|
58
|
+
heads.keys.each { |key| add_header(key, heads[key]) }
|
59
|
+
@_message.header.fields.each { |field| add_header(field.name, field.value) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_body(params)
|
63
|
+
return if sg_definition.template_name?
|
64
|
+
set_template_id(params[:template_id])
|
65
|
+
return if sg_definition.template_id?
|
66
|
+
set_content(params[:body], params[:content_type])
|
67
|
+
return if sg_definition.content?
|
68
|
+
set_body_from_tpl(params)
|
69
|
+
end
|
70
|
+
|
71
|
+
def set_body_from_tpl(params)
|
72
|
+
templates_path = params.delete(:template_path) || self.class.mailer_name
|
73
|
+
templates_name = params.delete(:template_name) || action_name
|
74
|
+
|
75
|
+
paths = Array(templates_path)
|
76
|
+
template = lookup_context.find_all(templates_name, paths).first
|
77
|
+
|
78
|
+
raise ActionView::MissingTemplate.new(
|
79
|
+
paths, templates_name, paths, false, 'mailer') unless template
|
80
|
+
|
81
|
+
set_content(render(template: template), template.type.to_s)
|
82
|
+
end
|
83
|
+
|
84
|
+
def sg_definition
|
85
|
+
@_message.sg_definition
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
# Maintain your gem"s version:
|
4
|
+
require "send_grid_mailer/version"
|
5
|
+
|
6
|
+
# Describe your gem and declare its dependencies:
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "send_grid_mailer"
|
9
|
+
s.version = SendGridMailer::VERSION
|
10
|
+
s.authors = ["Platanus", "Leandro Segovia"]
|
11
|
+
s.email = ["rubygems@platan.us", "ldlsegovia@gmail.com"]
|
12
|
+
s.homepage = "https://github.com/platanus/send_grid_mailer"
|
13
|
+
s.summary = "Action Mailer adapter for using SendGrid"
|
14
|
+
s.description = "Is an Action Mailer adapter for using SendGrid in a Rails application"
|
15
|
+
s.license = "MIT"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split($/).reject { |fn| fn.start_with? "spec" }
|
18
|
+
s.bindir = "exe"
|
19
|
+
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
s.test_files = Dir["spec/**/*"]
|
21
|
+
|
22
|
+
s.add_dependency "rails", "~> 4.2", ">= 4.2.0"
|
23
|
+
s.add_dependency "sendgrid-ruby", "~> 4.0", ">= 4.0.4"
|
24
|
+
s.add_dependency "colorize", "~> 0.7", ">= 0.7.7"
|
25
|
+
s.add_development_dependency "pry"
|
26
|
+
s.add_development_dependency "pry-rails"
|
27
|
+
s.add_development_dependency "sqlite3"
|
28
|
+
s.add_development_dependency "rspec-rails", "~> 3.4.0"
|
29
|
+
s.add_development_dependency "guard-rspec", "~> 4.7"
|
30
|
+
s.add_development_dependency "factory_girl_rails", "~> 4.6.0"
|
31
|
+
end
|