Email providers

There is nice table of main providers

Testing SMTP

If you need to test smtp use free service, just use port 9025 instead 25 since ISP is blocking 25. For command line you can use swaks like swaks --to [email protected] --server $SERVER --port $PORT --auth-user $AUTH_USER --auth-password $AUTH_PASSWORD --auth-plaintext --auth-hide-password so in autout you can see all telnet communications:

# generate base64 encoding
# special characters need to have \ in front
perl -MMIME::Base64 -e 'print encode_base64("duleorlovic\");'
perl -MMIME::Base64 -e 'print encode_base64("password");'

telnet 9025
EHLO main
<paste encoded username>
<paste encoded password>

ctrl + ]
ctrl + d

If you want to inspect how rails action_mailer sends and receive tcp messages than put byebug in net smtp class on line 940 get_response recv_response /home/orlovic/.rvm/rubies/ruby-2.3.3/lib/ruby/2.3.0/net/smtp.rb


Gmail smtp is the most easiest way to start

# config/application.rb
    config.action_mailer.smtp_settings = {
      address: '',
      port: 587,
      domain: '',
      authentication: 'plain',
      enable_starttls_auto: true,
      user_name: Rails.application.secrets.smtp_username,
      password: Rails.application.secrets.smtp_password
    config.action_mailer.delivery_method = :smtp

# config/secrets.yml
  smtp_username: <%= ENV["SMTP_USERNAME"] %>
  smtp_password: <%= ENV["SMTP_PASSWORD"] %>

If you receive error SocketError: getaddrinfo: Name or service not known than you probably miss the address field. If there is error with EOFError: end of file reached than you need to change domain field (should not be localhost, but the domain part of the sender email, for example

If you see error in logs:

2018-06-18T09:13:29.371621+00:00 app[web.1]: An error occurred when sending a notification using 'email' notifier. Net::SMTPAuthenticationError: 534-5.7.14 <

Username and Password not accepted. Learn more

You need to Allow less secure apps

Sometimes you can send from your IP but not from Heroku IP address.

For error  Errno::ECONNREFUSED (Connection refused - connect(2) for "localhost" port 25
the problem occurs when you in initializers (for example
config/initializers/devise.rb or config/initializers/exception_notification.rb)
use ApplicationMailer::MAILER_SENDER or some other constain from Rails classes
Note that this occurs only on production. So use only contstants from
=> {:address=>"", :port=>587, :authentication=>"plain", :enable_starttls_auto=>true, :user_name=>...

## Sendgrid

Sendgrid is simple to start on heroku. Just add new add-on free plan with
commands `heroku addons:create sendgrid` and that will set up env keys.
`heroku config` you can find the keys and copy them to `heroku config:set
SMTP_USERNAME=asdasdasd SMTP_PASSWORD=asdasdasd`. It allows sending with `from`
field any domain, but in gmail it shows that message is from: `My Company
[email protected] via`.

cat > config/initializers/smtp.rb « \HERE_DOC ActionMailer::Base.smtp_settings = { :user_name => Rails.application.secrets.smtp_username, :password => Rails.application.secrets.smtp_password, :domain => ‘’, :address => ‘’, :port => 587, :authentication => :plain, :enable_starttls_auto => true } HERE_DOC

Another way is to use API

## Mandrill

Mandrill is better than Sendgrid, since Sendgrid can not automatically convert
html to txt mails. Also mandrill has nice API so you do not need background
job to send a lot of emails quickly. To setup sending using API just run:


gem ‘mandrill_dm’


config.action_mailer.delivery_method = :mandrill


MandrillDm.configure do |config| config.api_key = Rails.application.secrets.mandrill_api_key end


development: mandrill_api_key: <%= ENV[“MANDRILL_API_KEY”] %>

If you are using `mandril_delivery` for ExceptionNotification than emails will
look scrambled, because generated html version will join all lines. Note that it
will trigger any webhooks that you have set up.

## Sparkpost

Sparkpost offer a lot of free usage (mandrill requires subscription) so
currently it is my best option. You need first to validate your domain, so you
can send with `from` field with that domain. You need also to

echo “gem ‘sparkpost_rails’” » Gemfile

sed -i config/environments/production.rb -e ‘/^end$/i
config.action_mailer.delivery_method = :sparkpost’

cat > config/initializers/sparkpostrails.rb « HERE_DOC

SparkPostRails.configure do |c| c.api_key = Rails.application.secrets.sparkpost_api_key end HERE_DOC

sed -i config/secrets.yml -e ‘/^test:/i
# email provider
sparkpost_api_key: <%= ENV[“SPARKPOST_API_KEY”] %>’

vi config/secrets.yml # update mailer_sender to match your domain

You can also use smtp with SPARK_POST but it is two times slower


config.action_mailer.smtp_settings = {
  address: '',
  port: 587,
  enable_starttls_auto: true,
  user_name: 'SMTP_Injection',
  password: Rails.application.secrets.sparkpost_api_key,
config.action_mailer.delivery_method = :smtp # sparkpost

time rails runner ‘UserMailer.signup.deliver_now!’ # ~5sec with smtp time rails runner ‘UserMailer.signup.deliver_now!’ # ~2.5sec with sparkpost

# Letter opener for local preview

sed -i ‘/group :development do/a
# open emails in browser
gem “letter_opener”’ Gemfile sed -i ‘/^end$/i \ config.action_mailer.delivery_method = :letter_opener’ config/environments/development.rb

Note that email letter opener does not work when you run with `rake jobs:work`,
but works when `bin/delayed_job run` (Launchy works in both cases, this
difference is only for mailer).

# Interceptor

When you need to test production emails localy, than you can set up interceptor
so you receive all emails (and not real customer emails).


class DevelopmentMailInterceptor def self.delivering_email(message) message.subject = “#{} #{message.subject}” = Rails.application.secrets.mail_interceptor_email end end if Rails.env.development? ActionMailer::Base.register_interceptor(DevelopmentMailInterceptor) end


mail_interceptop_email: <%= ENV[“MAIL_INTERCEPTOR_EMAIL”] %>

When you need to preview a lot of emails, its faster to use letter_opener gem.
Just put in your Gemfile under development `gem "letter_opener"` and in
*config/environments/development.rb* `config.action_mailer.delivery_method =
:letter_opener`. Works when email is sent (even from ajax response or console).

# Style

[Official gmail styles]( supports
`<style>` in head and media queries but when you forward email than css styles
will be gone. Better is to use gem which will copy and duplicate all styles from
head to inline styles and that will support more clients (not just gmail).

For easier styling, you should use *roadie* gem that will generate all inline
style from your head styles.

Attach css classes to emails

gem ‘roadie’ gem ‘roadie-rails’

You need to include mixing to each mailer (including in ApplicationMailer does
not help)


class MyMailer < ActionMailer::Base include Roadie::Rails::Automatic end

or include in ApplicaitionMailer and use roadie_mail

class ApplicationMailer < ActionMailer::Base include Roadie::Rails::Mailer end

class MyMailer < ActionMailer::Base def welcome(user) roadie_mail to: end end

You can change template with `mail to: '[email protected]', template_name:

To send without template you can
mail to: '[email protected]' do |format|
  format.html { render text: 'a' }

Another solution is `gem 'premailer-rails'`
<> which can also generate text part
so you do not need to maintain it. Just add the gem and you are good to go.

To preview emails use generated preview files in
`test/mailers/previews/my_mailer_preview.rb` or create new file:


class ApplicationMailerPreview < ActionMailer::Preview def new_message_from_client message = Message.first || FactoryBot.create :message ApplicationMailer.new_message_from_client message end end

add a line `config.action_mailer.preview_path =
"#{Rails.root}/app/mailer_previews"` to *config/environments/development.rb* and
go to [rails/mailers](http://localhost:3000/rails/mailers).
If you are using catch all route than add those lines
# config/routes.rb
  get '/rails/mailers' => "rails/mailers#index"
  get '/rails/mailers/*path' => "rails/mailers#preview"
  match '*a', to: 'home#routing_error', via: [:get, :post]

Another gem to preview emails <>

Here is example of style:


class ApplicationMailer < ActionMailer::Base default from: ‘[email protected]’ layout ‘mailer’ add_template_helper MailerHelper end


<!DOCTYPE html>


module MailerHelper def subject_line(message) content_for :subject_line, message end end


<% subject_line %>

<%= t "user_mailer.landing_signup.title", name: %>

# Receiving emails

When you want to receive, use [mandrill-rails](

echo ‘

receiving emails and webhooks

gem “mandrill-rails” ‘ » Gemfile

sed resource :inbox, :controller => ‘inbox’, :only => [:show,:create] config/routes.rb

echo ‘class InboxController < ApplicationController include Mandrill::Rails::WebHookProcessor

def handle_inbound(event_payload) # do something with payload end end ‘ > app/controllers/inbox_controller.rb


* create api key for prod and test
* validate inbound domains for prod and test
* create routes for validated domains (this will create one webhook)
* create webhooks
* create rules that match api and hooktype and send it to webhook


Feedback Loop is in a header and some clients enable them

# Internal Notification

Those are usefull admin or devops notifications


shared: mailer_sender: <%= ENV[“MAILER_SENDER”] || “My Company [email protected]” %> internal_notification_email: <%= ENV[“INTERNAL_NOTIFICATION_EMAIL”] || “[email protected]” %>


class ApplicationMailer < ActionMailer::Base layout ‘mailer’ default from: Rails.application.secrets.mailer_sender

INTERNAL_NOTIFICATION_EMAIL = Rails.application.secrets.internal_notification_email

def internal_notification(subject, item = {}) return unless INTERNAL_NOTIFICATION_EMAIL email_subject = “[MyApp#{‘ staging’ if Rails.application.secrets.is_staging}] #{subject}” email_body = “<h1>#{subject}</h1>Details:” + item.inspect. gsub(‘, ‘, “,
”). gsub(‘{‘, ‘
’). gsub(‘}’, ‘
’) mail to: INTERNAL_NOTIFICATION_EMAIL, subject: email_subject, body: email_body, content_type: “text/html” end end

You can send notification in any class. Note that first param is string, and
ohers are hash.


after_save :send_notification_geocode_failed

def send_notification_geocode_failed if address_changed? && !city.present? ApplicationMailer.internal_notification( “geocode city is not present #{name}”, name: name, url: Rails.application.routes.url_helpers.menu_url(link), address: address, ).deliver_now end end

Note that you should not send email in before blocks since when validation fails
it will rollback and even background job is rollbacked.

# ActionMailer

is what we can do with ActionMailer:

* headers
* attachments
* mail

You can use `before_action` and `after_action` and access to `params`

For example `prevent_delivery_to_guests`

class UserMailer < ApplicationMailer before_action { @business, @user = params[:business], params[:user] }

after_action :prevent_delivery_to_guests

def feedback_message end

def prevent_delivery_to_guests
  if @user && @user.guest?
    mail.perform_deliveries = false
end   end ~~~

Dynamic smtp settings at runtime

class DeviseMailer < Devise::Mailer
  after_action :set_smtp

  def set_smtp
    # determine smtp settings form @receiver or other
    if isp.use_my_smtp_server && @_mail_was_called # spam could ignore mail
      mail.from = "#{receiver.smtp_from_name} <#{receiver.smtp_from_email}>"
      mail.reply_to = "#{receiver.smtp_from_name} <#{receiver.smtp_from_email}>"
        address: receiver.smtp_host,
        port: (receiver.smtp_port.present? ? receiver.smtp_port : 587),
        user_name: receiver.smtp_username,
        password: receiver.smtp_password,


You can include small giff that looks like screencast. Image should be less than 1MB and included inline.

Email gems and gmail

You can use img tags and css background image, but if it is run in background (it does not know on which than you need to set asset host (look in common rails bootstrap snippets

# app/views/layouts/mailer.html.erb
background-image: url('<%= asset_url 'cute-small.jpg' %>');
<%= image_tag 'premesti_se.gif' %>

# config/application.rb
config.action_mailer.asset_host = "http://my_host"

Gmail Go To Action

Using some header json you can set button in gmail subject line “Quick Actions”.

Spam detection

You can disable registering specific email domains using this list Using this gem you can check if actual email account exists on smtp server. Format of emails can be validated using


Testing emails

# test/support/mailer_helpers.rb
module MailerHelpers
  def clear_mails
    ActionMailer::Base.deliveries = []

  # if you deliver_now you can
  # assert_difference 'all_mails.count', 1 do
  # and for background deliver_later you need to assert perform or enqueue
  # inherit from ActiveJob::TestCase
  # or include ActiveJob::TestHelper
  # assert_performed_jobs 1, only: ActionMailer::DeliveryJob do
  def all_mails

  # last_email is renamed to last_mail
  def last_mail
    raise 'you_should_use_give_me_last_mail_and_clear_mails'
    # ActionMailer::Base.deliveries.last

  # some usage is like
  # mail = give_me_last_mail_and_clear_mails
  # assert_equal [email],
  # assert_match t('user_mailer.landing_signup.confirmation_text'), mail.html_part.decoded # mail.body.to_s when it is not multipart (devise) when there is not txt.erb template
  # confirmation_link = mail.html_part.decoded.match(
  #   /(http:.*)">#{t("confirm_email")}/
  # )[1]
  # visit confirmation_link
  def give_me_last_mail_and_clear_mails
    mail = ActionMailer::Base.deliveries.last
class ActiveSupport::TestCase
  include MailerHelpers
  # for assert_performed_jobs
  include ActiveJob::TestHelper
class ActionDispatch::IntegrationTest
  include MailerHelpers
  # for assert_performed_jobs
  include ActiveJob::TestHelper

Click link tracking in emails