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.
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