├── .document ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── VERSION ├── lib ├── sendgrid.rb └── sendgrid │ ├── railtie.rb │ └── version.rb ├── sendgrid.gemspec └── test ├── sendgrid_test.rb └── test_helper.rb /.document: -------------------------------------------------------------------------------- 1 | README.rdoc 2 | lib/**/*.rb 3 | bin/* 4 | features/**/*.feature 5 | LICENSE 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw? 2 | .DS_Store 3 | coverage 4 | rdoc 5 | pkg 6 | .idea -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gem "json" 4 | 5 | # Add dependencies required to use your gem here. 6 | # Example: 7 | # gem "activesupport", ">= 2.3.5" 8 | 9 | # Add dependencies to develop your gem here. 10 | # Include everything needed to run rake, tests, features, etc. 11 | group :development do 12 | gem "shoulda", ">= 0" 13 | gem "bundler", "~> 1.0.0" 14 | gem "jeweler", "~> 1.5.1" 15 | # gem "rcov", ">= 0" 16 | end 17 | 18 | group :test do 19 | gem "actionmailer", ">= 2.3.2" 20 | end -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | actionmailer (3.1.5) 5 | actionpack (= 3.1.5) 6 | mail (~> 2.3.3) 7 | actionpack (3.1.5) 8 | activemodel (= 3.1.5) 9 | activesupport (= 3.1.5) 10 | builder (~> 3.0.0) 11 | erubis (~> 2.7.0) 12 | i18n (~> 0.6) 13 | rack (~> 1.3.6) 14 | rack-cache (~> 1.2) 15 | rack-mount (~> 0.8.2) 16 | rack-test (~> 0.6.1) 17 | sprockets (~> 2.0.4) 18 | activemodel (3.1.5) 19 | activesupport (= 3.1.5) 20 | builder (~> 3.0.0) 21 | i18n (~> 0.6) 22 | activesupport (3.1.5) 23 | multi_json (>= 1.0, < 1.3) 24 | builder (3.0.0) 25 | erubis (2.7.0) 26 | git (1.2.5) 27 | hike (1.2.1) 28 | i18n (0.6.0) 29 | jeweler (1.5.2) 30 | bundler (~> 1.0.0) 31 | git (>= 1.2.5) 32 | rake 33 | json (1.5.1) 34 | mail (2.3.3) 35 | i18n (>= 0.4.0) 36 | mime-types (~> 1.16) 37 | treetop (~> 1.4.8) 38 | mime-types (1.18) 39 | multi_json (1.2.0) 40 | polyglot (0.3.3) 41 | rack (1.3.6) 42 | rack-cache (1.2) 43 | rack (>= 0.4) 44 | rack-mount (0.8.3) 45 | rack (>= 1.0.0) 46 | rack-test (0.6.1) 47 | rack (>= 1.0) 48 | rake (0.8.7) 49 | shoulda (2.10.3) 50 | sprockets (2.0.4) 51 | hike (~> 1.2) 52 | rack (~> 1.0) 53 | tilt (~> 1.1, != 1.3.0) 54 | tilt (1.3.3) 55 | treetop (1.4.10) 56 | polyglot 57 | polyglot (>= 0.3.1) 58 | 59 | PLATFORMS 60 | ruby 61 | 62 | DEPENDENCIES 63 | actionmailer (>= 2.3.2) 64 | bundler (~> 1.0.0) 65 | jeweler (~> 1.5.1) 66 | json 67 | shoulda 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Stephen Blankenship 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sendgrid 2 | ========= 3 | 4 | _Now updated to work with Rails 3._ 5 | 6 | What is SendGrid? 7 | ----------------- 8 | 9 | SendGrid is an awesome service that helps you send large amounts of email (bells and whistles included) without spending large amounts of money. This gem allows for painless integration between ActionMailer and the SendGrid SMTP API. The current scope of this gem is focused around setting configuration options for outgoing email (essentially, setting categories, filters and the settings that can accompany those filters). SendGrid's service allows for some other cool stuff (such as postback notification of unsubscribes, bounces, etc.), but those features are currently outside the scope of this gem. 10 | 11 | Visit [SendGrid](http://sendgrid.com) to learn more. 12 | 13 | Getting Started 14 | --------------- 15 | 16 | First of all, you'll need the gem. It's at http://rubygems.org/gems/sendgrid. If you're using Bundler, just add the following to your Gemfile. 17 | 18 | gem 'sendgrid' 19 | 20 | 21 | Before you can do anything with the sendgrid gem, you'll need to create your very own SendGrid account. Go ahead and do so at [http://sendgrid.com](http://sendgrid.com) (there's even a FREE account option). 22 | 23 | Next, update your application's SMTP settings to use SendGrid's servers (see [SendGrid's getting started guide](http://wiki.sendgrid.com/doku.php?id=get_started) for instructions). 24 | 25 | Example: 26 | 27 | ActionMailer::Base.smtp_settings = { 28 | :address => "smtp.sendgrid.net", 29 | :port => 25, 30 | :domain => "mysite.com", 31 | :authentication => :plain, 32 | :user_name => "sendgrd_username", 33 | :password => "sendgrid_password" 34 | } 35 | 36 | Using the sendgrid Gem 37 | ---------------------- 38 | 39 | If you do not already have an ActionMailer class up and running, then check out [this guide.](http://guides.rubyonrails.org/action_mailer_basics.html#walkthrough-to-generating-a-mailer) 40 | 41 | 1) add the following line within your mailer class: 42 | 43 | include SendGrid 44 | 45 | 46 | 2) customize your sendgrid settings: 47 | 48 | There are 2 main types of settings 49 | 50 | * Category settings 51 | * Enable/disable settings 52 | 53 | You can set both global and per-email settings - the same syntax is used in either case. 54 | Here is an example of what typical usage may look like: 55 | 56 | class MyMailer < ActionMailer::Base 57 | include SendGrid 58 | sendgrid_category :use_subject_lines 59 | sendgrid_enable :ganalytics, :opentrack 60 | sendgrid_unique_args :key1 => "value1", :key2 => "value2" 61 | 62 | def welcome_message(user) 63 | sendgrid_category "Welcome" 64 | sendgrid_unique_args :key2 => "newvalue2", :key3 => "value3" 65 | mail :to => user.email, :subject => "Welcome #{user.name} :-)" 66 | end 67 | 68 | def goodbye_message(user) 69 | sendgrid_disable :ganalytics 70 | mail :to => user.email, :subject => "Fare thee well :-(" 71 | end 72 | end 73 | 74 | Category settings can be any text you like and SendGrid's website will allow you to view email statistics per-category (very nice). There is also a custom global setting that will automatically use the subject line of each email as the sendgrid\_category: 75 | 76 | sendgrid_category :use_subject_lines 77 | 78 | If you have any dynamic subject lines, you'll want to override this setting within the mailer method. Calling sendgrid\_category from within one of your mailer methods will override this global setting. Similarly, calling sendgrid\_enable/sendgrid\_disable from within a mailer method will add or remove from any defaults that may have been set globally. 79 | 80 | Here are a list of supported options for sendgrid\_enable and sendgrid\_disable: 81 | 82 | * :opentrack 83 | * :clicktrack 84 | * :ganalytics 85 | * Call sendgrid\_ganalytics\_options(:utm_source => 'welcome_email', :utm_medium => 'email', :utm_campaign => 'promo', :utm_term => 'intro+text', :utm_content => 'header_link') to set custom Google Analytics variables. 86 | * :gravatar 87 | * :subscriptiontrack 88 | * Call sendgrid\_subscriptiontrack\_text(:html => 'Unsubscribe <% Here %>', :plain => 'Unsubscribe Here: <% %>') to set a custom format for html/plain or both. 89 | * OR Call sendgrid\_subscriptiontrack\_text(:replace => '|unsubscribe\_link|') to replace all occurrences of |unsubscribe\_link| with the url of the unsubscribe link 90 | * :footer 91 | * Call sendgrid\_footer\_text(:html => 'My HTML footer rocks!', :plain => 'My plain text footer is so-so.') to set custom footer text for html, plain or both. 92 | * :spamcheck 93 | * Call sendgrid\_spamcheck\_maxscore(4.5) to set a custom SpamAssassin threshold at which SendGrid drops emails (default value is 5.0). 94 | 95 | For further explanation see [SendGrid's wiki page on filters.](http://wiki.sendgrid.com/doku.php?id=filters) 96 | 97 | Custom parameters can be set using the sendgrid_unique_args methods. Any key/value pairs defined thusly will 98 | be included as parameters in SendGrid post backs. These are especially useful in cases where the recipient's 99 | email address is not unique or when multiple applications/environments are using the same SendGrid account. 100 | 101 | 102 | Delivering to multiple recipients 103 | --------------------------------- 104 | 105 | There is a per-mailer-method setting that can be used to deliver campaigns to multiple (many) recipients in a single delivery/SMTP call. 106 | It is quite easy to build a robust mass-delivery system utilizing this feature, and it is quite difficult to deliver a large email campaign quickly without this feature. 107 | Note: While it may be worth asking yourself, a SendGrid engineer told me it's best to keep the number of recipients to <= 1,000 per delivery. 108 | 109 | 110 | sendgrid_recipients ["email1@blah.com", "email2@blah.com", "email3@blah.com", ...] 111 | 112 | 113 | One issue that arises when delivering multiple emails at once is custom content. Luckily, there is also a per-mailer-method setting that can be used to substitute custom content. 114 | 115 | 116 | sendgrid_substitute "|subme|", ["sub text for 1st recipient", "sub text for 2nd recipient", "sub text for 3rd recipient", ...] 117 | 118 | 119 | In this example, if |subme| is in the body of your email SendGrid will automatically substitute it for the string corresponding the recipient being delivered to. NOTE: You should ensure that the length of the substitution array is equal to the length of the recipients array. 120 | 121 | 122 | TODO 123 | ---- 124 | 125 | * Test coverage (I would appreciate help writing tests). 126 | * Possibly integrate with SendGrid's Event API and some of the other goodies they provide. 127 | 128 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | 4 | begin 5 | require 'jeweler' 6 | Jeweler::Tasks.new do |gem| 7 | gem.name = "sendgrid" 8 | gem.summary = %Q{A gem that allows simple integration of ActionMailer with SendGrid (http://sendgrid.com)} 9 | gem.description = %Q{This gem allows simple integration between ActionMailer and SendGrid. 10 | SendGrid is an email deliverability API that is affordable and has lots of bells and whistles.} 11 | gem.email = "stephenrb@gmail.com" 12 | gem.homepage = "http://github.com/stephenb/sendgrid" 13 | gem.authors = ["Stephen Blankenship", "Marc Tremblay", "Bob Burbach"] 14 | gem.add_dependency "json" 15 | # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings 16 | end 17 | Jeweler::GemcutterTasks.new 18 | rescue LoadError 19 | puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler" 20 | end 21 | 22 | require 'rake/testtask' 23 | Rake::TestTask.new(:test) do |test| 24 | test.libs << 'lib' << 'test' 25 | test.pattern = 'test/**/*_test.rb' 26 | test.verbose = true 27 | end 28 | 29 | begin 30 | require 'rcov/rcovtask' 31 | Rcov::RcovTask.new do |test| 32 | test.libs << 'test' 33 | test.pattern = 'test/**/*_test.rb' 34 | test.verbose = true 35 | end 36 | rescue LoadError 37 | task :rcov do 38 | abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov" 39 | end 40 | end 41 | 42 | task :test 43 | 44 | task :default => :test 45 | 46 | # require 'rake/rdoctask' 47 | # Rake::RDocTask.new do |rdoc| 48 | # if File.exist?('VERSION') 49 | # version = File.read('VERSION') 50 | # else 51 | # version = "" 52 | # end 53 | 54 | # rdoc.rdoc_dir = 'rdoc' 55 | # rdoc.title = "sendgrid #{version}" 56 | # rdoc.rdoc_files.include('README*') 57 | # rdoc.rdoc_files.include('lib/**/*.rb') 58 | # end 59 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.2.4 -------------------------------------------------------------------------------- /lib/sendgrid.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | module SendGrid 4 | 5 | VALID_OPTIONS = [ 6 | :opentrack, 7 | :clicktrack, 8 | :ganalytics, 9 | :gravatar, 10 | :subscriptiontrack, 11 | :footer, 12 | :spamcheck, 13 | :bypass_list_management 14 | ] 15 | 16 | VALID_GANALYTICS_OPTIONS = [ 17 | :utm_source, 18 | :utm_medium, 19 | :utm_campaign, 20 | :utm_term, 21 | :utm_content 22 | ] 23 | 24 | def self.included(base) 25 | base.class_eval do 26 | class << self 27 | attr_accessor :default_sg_category, :default_sg_options, :default_subscriptiontrack_text, 28 | :default_footer_text, :default_spamcheck_score, :default_sg_unique_args 29 | end 30 | attr_accessor :sg_category, :sg_options, :sg_disabled_options, :sg_recipients, :sg_substitutions, 31 | :subscriptiontrack_text, :footer_text, :spamcheck_score, :sg_unique_args, :sg_send_at 32 | end 33 | 34 | # NOTE: This commented-out approach may be a "safer" option for Rails 3, but it 35 | # would cause the headers to get set during delivery, and not when the message is initialized. 36 | # If base supports register_interceptor (i.e., Rails 3 ActionMailer), use it... 37 | # if base.respond_to?(:register_interceptor) 38 | # base.register_interceptor(SendgridInterceptor) 39 | # end 40 | 41 | base.extend(ClassMethods) 42 | end 43 | 44 | module ClassMethods 45 | 46 | # Sets a default category for all emails. 47 | # :use_subject_lines has special behavior that uses the subject-line of 48 | # each outgoing email for the SendGrid category. This special behavior 49 | # can still be overridden by calling sendgrid_category from within a 50 | # mailer method. 51 | def sendgrid_category(category) 52 | self.default_sg_category = category 53 | end 54 | 55 | # Enables a default option for all emails. 56 | # See documentation for details. 57 | # 58 | # Supported options: 59 | # * :opentrack 60 | # * :clicktrack 61 | # * :ganalytics 62 | # * :gravatar 63 | # * :subscriptiontrack 64 | # * :footer 65 | # * :spamcheck 66 | def sendgrid_enable(*options) 67 | self.default_sg_options = Array.new unless self.default_sg_options 68 | options.each { |option| self.default_sg_options << option if VALID_OPTIONS.include?(option) } 69 | end 70 | 71 | # Sets the default text for subscription tracking (must be enabled). 72 | # There are two options: 73 | # 1. Add an unsubscribe link at the bottom of the email 74 | # {:html => "Unsubscribe <% here %>", :plain => "Unsubscribe here: <% %>"} 75 | # 2. Replace given text with the unsubscribe link 76 | # {:replace => "" } 77 | def sendgrid_subscriptiontrack_text(texts) 78 | self.default_subscriptiontrack_text = texts 79 | end 80 | 81 | # Sets the default footer text (must be enabled). 82 | # Should be a hash containing the html/plain text versions: 83 | # {:html => "html version", :plain => "plan text version"} 84 | def sendgrid_footer_text(texts) 85 | self.default_footer_text = texts 86 | end 87 | 88 | # Sets the default spamcheck score text (must be enabled). 89 | def sendgrid_spamcheck_maxscore(score) 90 | self.default_spamcheck_score = score 91 | end 92 | 93 | # Sets unique args at the class level. Should be a hash 94 | # of name, value pairs. 95 | # { :some_unique_arg => "some_value"} 96 | def sendgrid_unique_args(unique_args = {}) 97 | self.default_sg_unique_args = unique_args 98 | end 99 | end 100 | 101 | # Call within mailer method to override the default value. 102 | def sendgrid_category(category) 103 | @sg_category = category 104 | end 105 | 106 | # Call within mailer method to set send time for this mail 107 | def sendgrid_send_at(utc_timestamp) 108 | @sg_send_at = utc_timestamp 109 | end 110 | 111 | # Call within mailer method to set unique args for this email. 112 | # Merged with class-level unique args, if any exist. 113 | def sendgrid_unique_args(unique_args = {}) 114 | @sg_unique_args = unique_args 115 | end 116 | 117 | # Call within mailer method to add an option not in the defaults. 118 | def sendgrid_enable(*options) 119 | @sg_options = Array.new unless @sg_options 120 | options.each { |option| @sg_options << option if VALID_OPTIONS.include?(option) } 121 | end 122 | 123 | # Call within mailer method to remove one of the defaults. 124 | def sendgrid_disable(*options) 125 | @sg_disabled_options = Array.new unless @sg_disabled_options 126 | options.each { |option| @sg_disabled_options << option if VALID_OPTIONS.include?(option) } 127 | end 128 | 129 | # Call within mailer method to add an array of recipients 130 | def sendgrid_recipients(emails) 131 | @sg_recipients = Array.new unless @sg_recipients 132 | @sg_recipients = emails 133 | end 134 | 135 | # Call within mailer method to add an array of substitions 136 | # NOTE: you must ensure that the length of the substitions equals the 137 | # length of the sendgrid_recipients. 138 | def sendgrid_substitute(placeholder, subs) 139 | @sg_substitutions = Hash.new unless @sg_substitutions 140 | @sg_substitutions[placeholder] = subs 141 | end 142 | 143 | # Call within mailer method to override the default value. 144 | def sendgrid_subscriptiontrack_text(texts) 145 | @subscriptiontrack_text = texts 146 | end 147 | 148 | # Call within mailer method to override the default value. 149 | def sendgrid_footer_text(texts) 150 | @footer_text = texts 151 | end 152 | 153 | # Call within mailer method to override the default value. 154 | def sendgrid_spamcheck_maxscore(score) 155 | @spamcheck_score = score 156 | end 157 | 158 | # Call within mailer method to set custom google analytics options 159 | # http://sendgrid.com/documentation/appsGoogleAnalytics 160 | def sendgrid_ganalytics_options(options) 161 | @ganalytics_options = [] 162 | options.each { |option| @ganalytics_options << option if VALID_GANALYTICS_OPTIONS.include?(option[0].to_sym) } 163 | end 164 | 165 | # only override the appropriate methods for the current ActionMailer version 166 | if ActionMailer::Base.respond_to?(:mail) 167 | 168 | protected 169 | 170 | # Sets the custom X-SMTPAPI header after creating the email but before delivery 171 | # NOTE: This override is used for Rails 3 ActionMailer classes. 172 | def mail(headers={}, &block) 173 | m = super 174 | if @sg_substitutions && !@sg_substitutions.empty? 175 | @sg_substitutions.each do |find, replace| 176 | raise ArgumentError.new("Array for #{find} is not the same size as the recipient array") if replace.size != @sg_recipients.size 177 | end 178 | end 179 | puts "SendGrid X-SMTPAPI: #{sendgrid_json_headers(message)}" if Object.const_defined?("SENDGRID_DEBUG_OUTPUT") && SENDGRID_DEBUG_OUTPUT 180 | self.headers['X-SMTPAPI'] = sendgrid_json_headers(message) 181 | m 182 | end 183 | 184 | else 185 | 186 | # Sets the custom X-SMTPAPI header after creating the email but before delivery 187 | # NOTE: This override is used for Rails 2 ActionMailer classes. 188 | def create!(method_name, *parameters) 189 | super 190 | if @sg_substitutions && !@sg_substitutions.empty? 191 | @sg_substitutions.each do |find, replace| 192 | raise ArgumentError.new("Array for #{find} is not the same size as the recipient array") if replace.size != @sg_recipients.size 193 | end 194 | end 195 | puts "SendGrid X-SMTPAPI: #{sendgrid_json_headers(mail)}" if Object.const_defined?("SENDGRID_DEBUG_OUTPUT") && SENDGRID_DEBUG_OUTPUT 196 | @mail['X-SMTPAPI'] = sendgrid_json_headers(mail) 197 | end 198 | 199 | end 200 | 201 | private 202 | 203 | # Take all of the options and turn it into the json format that SendGrid expects 204 | def sendgrid_json_headers(mail) 205 | header_opts = {} 206 | 207 | #if not called within the mailer method, this will be nil so we default to empty hash 208 | @sg_unique_args = @sg_unique_args || {} 209 | 210 | # set the unique arguments 211 | if @sg_unique_args || self.class.default_sg_unique_args 212 | unique_args = self.class.default_sg_unique_args || {} 213 | unique_args = unique_args.merge(@sg_unique_args) 214 | 215 | header_opts[:unique_args] = unique_args unless unique_args.empty? 216 | end 217 | 218 | # Set category 219 | if @sg_category && @sg_category == :use_subject_lines 220 | header_opts[:category] = mail.subject 221 | elsif @sg_category 222 | header_opts[:category] = @sg_category 223 | elsif self.class.default_sg_category && self.class.default_sg_category.to_sym == :use_subject_lines 224 | header_opts[:category] = mail.subject 225 | elsif self.class.default_sg_category 226 | header_opts[:category] = self.class.default_sg_category 227 | end 228 | 229 | #Set send_at if set by the user 230 | header_opts[:send_at] = @sg_send_at unless @sg_send_at.blank? 231 | 232 | # Set multi-recipients 233 | if @sg_recipients && !@sg_recipients.empty? 234 | header_opts[:to] = @sg_recipients 235 | end 236 | 237 | # Set custom substitions 238 | if @sg_substitutions && !@sg_substitutions.empty? 239 | header_opts[:sub] = @sg_substitutions 240 | end 241 | 242 | # Set enables/disables 243 | enabled_opts = [] 244 | if @sg_options && !@sg_options.empty? 245 | # merge the options so that the instance-level "overrides" 246 | merged = self.class.default_sg_options || [] 247 | merged += @sg_options 248 | enabled_opts = merged 249 | elsif self.class.default_sg_options 250 | enabled_opts = self.class.default_sg_options 251 | end 252 | if !enabled_opts.empty? || (@sg_disabled_options && !@sg_disabled_options.empty?) 253 | filters = filters_hash_from_options(enabled_opts, @sg_disabled_options) 254 | header_opts[:filters] = filters if filters && !filters.empty? 255 | end 256 | 257 | header_opts.to_json.gsub(/(["\]}])([,:])(["\[{])/, '\\1\\2 \\3') 258 | end 259 | 260 | def filters_hash_from_options(enabled_opts, disabled_opts) 261 | filters = {} 262 | enabled_opts.each do |opt| 263 | filters[opt] = {'settings' => {'enable' => 1}} 264 | case opt.to_sym 265 | when :subscriptiontrack 266 | if @subscriptiontrack_text 267 | if @subscriptiontrack_text[:replace] 268 | filters[:subscriptiontrack]['settings']['replace'] = @subscriptiontrack_text[:replace] 269 | else 270 | filters[:subscriptiontrack]['settings']['text/html'] = @subscriptiontrack_text[:html] 271 | filters[:subscriptiontrack]['settings']['text/plain'] = @subscriptiontrack_text[:plain] 272 | end 273 | elsif self.class.default_subscriptiontrack_text 274 | if self.class.default_subscriptiontrack_text[:replace] 275 | filters[:subscriptiontrack]['settings']['replace'] = self.class.default_subscriptiontrack_text[:replace] 276 | else 277 | filters[:subscriptiontrack]['settings']['text/html'] = self.class.default_subscriptiontrack_text[:html] 278 | filters[:subscriptiontrack]['settings']['text/plain'] = self.class.default_subscriptiontrack_text[:plain] 279 | end 280 | end 281 | 282 | when :footer 283 | if @footer_text 284 | filters[:footer]['settings']['text/html'] = @footer_text[:html] 285 | filters[:footer]['settings']['text/plain'] = @footer_text[:plain] 286 | elsif self.class.default_footer_text 287 | filters[:footer]['settings']['text/html'] = self.class.default_footer_text[:html] 288 | filters[:footer]['settings']['text/plain'] = self.class.default_footer_text[:plain] 289 | end 290 | 291 | when :spamcheck 292 | if self.class.default_spamcheck_score || @spamcheck_score 293 | filters[:spamcheck]['settings']['maxscore'] = @spamcheck_score || self.class.default_spamcheck_score 294 | end 295 | 296 | when :ganalytics 297 | if @ganalytics_options 298 | @ganalytics_options.each do |key, value| 299 | filters[:ganalytics]['settings'][key.to_s] = value 300 | end 301 | end 302 | end 303 | end 304 | 305 | if disabled_opts 306 | disabled_opts.each do |opt| 307 | filters[opt] = {'settings' => {'enable' => 0}} 308 | end 309 | end 310 | 311 | return filters 312 | end 313 | 314 | end 315 | -------------------------------------------------------------------------------- /lib/sendgrid/railtie.rb: -------------------------------------------------------------------------------- 1 | require 'rails' 2 | 3 | module SendGrid 4 | class Railtie < Rails::Railtie # :nodoc: 5 | 6 | initializer "send_grid.action_mailer" do 7 | ActiveSupport.on_load(:action_mailer) { ActionMailer::Base.send(:include, SendGrid) } 8 | end 9 | 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/sendgrid/version.rb: -------------------------------------------------------------------------------- 1 | module SendGrid #:nodoc: 2 | VERSION = "2.0.0" 3 | end 4 | -------------------------------------------------------------------------------- /sendgrid.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | # stub: sendgrid 1.2.4 ruby lib 6 | 7 | Gem::Specification.new do |s| 8 | s.name = "sendgrid" 9 | s.version = "1.2.4" 10 | 11 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 12 | s.require_paths = ["lib"] 13 | s.authors = ["Stephen Blankenship", "Marc Tremblay", "Bob Burbach"] 14 | s.date = "2016-01-07" 15 | s.description = "This gem allows simple integration between ActionMailer and SendGrid. \n SendGrid is an email deliverability API that is affordable and has lots of bells and whistles." 16 | s.email = "stephenrb@gmail.com" 17 | s.extra_rdoc_files = [ 18 | "LICENSE", 19 | "README.md" 20 | ] 21 | s.files = [ 22 | ".document", 23 | "Gemfile", 24 | "Gemfile.lock", 25 | "LICENSE", 26 | "README.md", 27 | "Rakefile", 28 | "VERSION", 29 | "lib/sendgrid.rb", 30 | "lib/sendgrid/railtie.rb", 31 | "lib/sendgrid/version.rb", 32 | "sendgrid.gemspec", 33 | "test/sendgrid_test.rb", 34 | "test/test_helper.rb" 35 | ] 36 | s.homepage = "http://github.com/stephenb/sendgrid" 37 | s.rubygems_version = "2.5.1" 38 | s.summary = "A gem that allows simple integration of ActionMailer with SendGrid (http://sendgrid.com)" 39 | 40 | if s.respond_to? :specification_version then 41 | s.specification_version = 4 42 | 43 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 44 | s.add_runtime_dependency(%q, [">= 0"]) 45 | s.add_development_dependency(%q, [">= 0"]) 46 | s.add_development_dependency(%q, ["~> 1.0.0"]) 47 | s.add_development_dependency(%q, ["~> 1.5.1"]) 48 | s.add_runtime_dependency(%q, [">= 0"]) 49 | else 50 | s.add_dependency(%q, [">= 0"]) 51 | s.add_dependency(%q, [">= 0"]) 52 | s.add_dependency(%q, ["~> 1.0.0"]) 53 | s.add_dependency(%q, ["~> 1.5.1"]) 54 | s.add_dependency(%q, [">= 0"]) 55 | end 56 | else 57 | s.add_dependency(%q, [">= 0"]) 58 | s.add_dependency(%q, [">= 0"]) 59 | s.add_dependency(%q, ["~> 1.0.0"]) 60 | s.add_dependency(%q, ["~> 1.5.1"]) 61 | s.add_dependency(%q, [">= 0"]) 62 | end 63 | end 64 | 65 | -------------------------------------------------------------------------------- /test/sendgrid_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SendgridTest < Test::Unit::TestCase 4 | def setup 5 | ActionMailer::Base.delivery_method = :test 6 | ActionMailer::Base.perform_deliveries = true 7 | ActionMailer::Base.deliveries = [] 8 | 9 | @options = { 10 | :to => ['test@example.com'], 11 | :from => 'test@example.com', 12 | :reply_to => 'reply-to@example.com', 13 | :subject => 'The Subject', 14 | :body => 'content', 15 | :category => 'MyCategory', 16 | :substitutions => { 17 | 'first_name' => ['Joe'], 18 | 'last_name' => ['Schmoe', 'Cool'] 19 | } 20 | } 21 | end 22 | 23 | should "require the same number of items in a substitution array as is in the recipient array" do 24 | assert_raise ArgumentError do 25 | test_email = SendgridCampaignTestMailer.create_test(@options).deliver 26 | end 27 | end 28 | 29 | should "accept a hash of unique args at the class level" do 30 | assert_equal ({ :test_arg => "test value" }), SendgridUniqueArgsMailer.default_sg_unique_args 31 | end 32 | 33 | should "pass unique args from both the mailer class and the mailer method through custom headers" do 34 | @options.delete(:substitutions) 35 | SendgridUniqueArgsMailer.unique_args_test_email(@options).deliver 36 | mail = ActionMailer::Base.deliveries.last 37 | # assert({ :unique_args => {:mailer_method_unique_arg => "some value", :test_arg => "test value"} }.to_json == mail.header['X-SMTPAPI'].value) 38 | expected = { 'unique_args' => {'mailer_method_unique_arg' => "some value", 'test_arg' => "test value"} } 39 | actual = JSON.parse(mail.header['X-SMTPAPI'].value) 40 | assert_equal(expected, actual) 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'test/unit' 3 | require 'shoulda' 4 | 5 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 6 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 7 | require 'action_mailer' 8 | require 'sendgrid' 9 | 10 | class SendgridCampaignTestMailer < ActionMailer::Base 11 | include SendGrid 12 | 13 | sendgrid_enable :opentrack, :clicktrack, :subscriptiontrack, :bypass_list_management 14 | 15 | REQUIRED_OPTIONS = [:to, :from, :category, :html_content, :subject] 16 | 17 | ## 18 | # options are: 19 | # :from the from address 20 | # :to an array of recipients 21 | # :substitutions a hash of substitutions of the form: 'text to be 22 | # replaced' => [replacements]. The order and size of the replacement 23 | # array must match the :to array 24 | # :category the sendgrid category used for tracking 25 | # :html_content, :text_content, :subject 26 | def create_test(options) 27 | handle_sendgrid_options(options) 28 | mail(options) 29 | end 30 | 31 | protected 32 | def handle_sendgrid_options(options) 33 | REQUIRED_OPTIONS.each do |option| 34 | raise ArgumentError.new("Required sendgrid option ':#{option}' missing") unless options[option] 35 | end 36 | 37 | sendgrid_recipients(options[:to]) 38 | 39 | if options[:substitutions] 40 | options[:substitutions].each do |find, replace| 41 | sendgrid_substitute(find, replace) 42 | end 43 | end 44 | 45 | sendgrid_category(options[:category]) 46 | end 47 | end 48 | 49 | class SendgridUniqueArgsMailer < ActionMailer::Base 50 | include SendGrid 51 | sendgrid_unique_args({ :test_arg => "test value" }) 52 | 53 | def unique_args_test_email(options) 54 | sendgrid_unique_args({ :mailer_method_unique_arg => "some value" }) 55 | mail(options) 56 | end 57 | end --------------------------------------------------------------------------------