send_grid_mailer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +7 -0
  6. data/Gemfile +15 -0
  7. data/Gemfile.lock +192 -0
  8. data/Guardfile +15 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +209 -0
  11. data/Rakefile +10 -0
  12. data/lib/send_grid_mailer.rb +27 -0
  13. data/lib/send_grid_mailer/definition.rb +110 -0
  14. data/lib/send_grid_mailer/deliverer.rb +46 -0
  15. data/lib/send_grid_mailer/engine.rb +23 -0
  16. data/lib/send_grid_mailer/errors.rb +2 -0
  17. data/lib/send_grid_mailer/logger.rb +99 -0
  18. data/lib/send_grid_mailer/mail_message_ext.rb +7 -0
  19. data/lib/send_grid_mailer/mailer_base_ext.rb +88 -0
  20. data/lib/send_grid_mailer/version.rb +3 -0
  21. data/lib/tasks/send_grid_mailer_tasks.rake +4 -0
  22. data/send_grid_mailer.gemspec +31 -0
  23. data/spec/dummy/README.rdoc +28 -0
  24. data/spec/dummy/Rakefile +6 -0
  25. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  26. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  27. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  28. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  29. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  30. data/spec/dummy/app/mailers/test_mailer.rb +86 -0
  31. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  32. data/spec/dummy/app/views/layouts/mailer.html.erb +5 -0
  33. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  34. data/spec/dummy/app/views/test_mailer/rails_tpl_email.html.erb +1 -0
  35. data/spec/dummy/bin/bundle +3 -0
  36. data/spec/dummy/bin/rails +4 -0
  37. data/spec/dummy/bin/rake +4 -0
  38. data/spec/dummy/bin/setup +29 -0
  39. data/spec/dummy/config.ru +4 -0
  40. data/spec/dummy/config/application.rb +32 -0
  41. data/spec/dummy/config/boot.rb +5 -0
  42. data/spec/dummy/config/database.yml +25 -0
  43. data/spec/dummy/config/environment.rb +5 -0
  44. data/spec/dummy/config/environments/development.rb +46 -0
  45. data/spec/dummy/config/environments/production.rb +79 -0
  46. data/spec/dummy/config/environments/test.rb +42 -0
  47. data/spec/dummy/config/initializers/assets.rb +11 -0
  48. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  49. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  50. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  51. data/spec/dummy/config/initializers/inflections.rb +16 -0
  52. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  53. data/spec/dummy/config/initializers/session_store.rb +3 -0
  54. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  55. data/spec/dummy/config/locales/en.yml +23 -0
  56. data/spec/dummy/config/routes.rb +56 -0
  57. data/spec/dummy/config/secrets.yml +22 -0
  58. data/spec/dummy/db/development.sqlite3 +0 -0
  59. data/spec/dummy/db/schema.rb +16 -0
  60. data/spec/dummy/db/test.sqlite3 +0 -0
  61. data/spec/dummy/log/development.log +1984 -0
  62. data/spec/dummy/log/test.log +58252 -0
  63. data/spec/dummy/public/404.html +67 -0
  64. data/spec/dummy/public/422.html +67 -0
  65. data/spec/dummy/public/500.html +66 -0
  66. data/spec/dummy/public/favicon.ico +0 -0
  67. data/spec/dummy/spec/assets/image.png +0 -0
  68. data/spec/dummy/spec/assets/video.mp4 +0 -0
  69. data/spec/dummy/spec/lib/send_grid_mailer/definition_spec.rb +102 -0
  70. data/spec/dummy/spec/mailers/test_mailer_spec.rb +423 -0
  71. data/spec/dummy/spec/support/test_helpers.rb +5 -0
  72. data/spec/rails_helper.rb +28 -0
  73. data/spec/spec_helper.rb +9 -0
  74. 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,2 @@
1
+ class SendGridMailer::Exception < ::Exception
2
+ 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,7 @@
1
+ module Mail
2
+ class Message
3
+ def sg_definition
4
+ @sg_definition ||= SendGridMailer::Definition.new
5
+ end
6
+ end
7
+ 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,3 @@
1
+ module SendGridMailer
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :send_grid_mailer do
3
+ # # Task goes here
4
+ # 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