Ruby on Rails
HowToSendEmailsWithActionMailer (Version #291)

Note: This page may be out of date with current API
but the initial setup and example have been modified to reflect the use of methods versus instance variables.

My updated tutorial for sending emails, tested with 2.0
How to send email with Ruby on Rails

ActionMailer is a Rails service-layer package for creating email messages.

Generate a mailer

Create a stub file using the generator:

ruby script/generate mailer Notifier

A file is created in your app/models directory named notifier.rb:

class Notifier < ActionMailer::Base
end

A few points to keep in mind

There are a few confusing things to learn with Action Mailer that took a while to figure out.

  • Action Mailer implementations have views. The above example will reference files in app/views/notifier
  • Each email that you want to send should have one associated method within the class.
  • You don’t call that method. Ever.
  • Instead, you call some dynamic methods, as we’ll see below.

An Example

The scenario

Let’s assume we’re sending an email to a new user to our website to thank them for signing up, and that user is an object coming from an Active Record that has already been created.

Notifier

Add the following method to your Notifier:

def signup_thanks( user )
  # Email header info MUST be added here
  recipients user.email
  from  =="<a href="mailto:accounts@mywebsite.com">accounts@mywebsite.com</a>"==
  subject "Thank you for registering with our website"

  # Email body substitutions go here
  body :user=> user
end

Notice that it is recipient*s* even if you are only sending to one user.

Controller

And the following snippet to the controller in your application that needs to send an email:

def show_page_after_account_creation
...
  Notifier.deliver_signup_thanks(user)
...
end

This threw me for awhile: Make sure you notice the “deliver_” prefix to the method defined in the Notifier model.

View

The body of the email comes from a .rhtml file in app/views/notifier – In this example; app/views/notifier/signup_thanks.rhtml:
(Note: You might have to end the name with en.rhtml, i.e.signupthanks_en.rhtml, if you use localization)

Dear <%= @user.first_name %> <%= @user.last_name %>,

Thanks for signing up with My Website!

Your account will allow you to do blah, blah, blah.

It is possible to use helpers to your email templates simply by adding



helper :application


to your mailer model. (read the article)

Configuration

The mail configuration should be set in your environment. See the API documentation for configuration options. To have your configuration available for all server environments put it in /config/environment.rb, otherwise place it in config/environments/production.rb or config/environments/development.rb.

Make sure ActionMailer is removed from this line in environment.rb:

config.frameworks -= [ :action_web_service, :action_mailer ]

The default method for sending mail is SMTP. ActionMailer can also use sendmail, and for initial testing and development you probably want to switch to that. Stick this in your environment.rb: (After the Rails::Initializer.run do |config| block)

ActionMailer::Base.delivery_method = :sendmail

You may also turn exception raising on and off. The documentation does not state which is the default, so just to be sure, you can turn on exceptions like so:

ActionMailer::Base.raise_delivery_errors = true

A sample configuration setting using the default smtp delivery method would look something like this:

# Include your app's configuration here:
ActionMailer::Base.smtp_settings = {
  :address  => "smtp.postoffice.net",
  :port  => 25, 
  :domain  => "www.mywebsite.com",
  :user_name  => =="<a href="mailto:me@postoffice.net">me@postoffice.net</a>"==,
  :password  => "mypass",
  :authentication  => :login
    } 

:domain is the name sent to the SMTP server during the handshake. It will be used to form the first “Received:” header, which spam filters usually check.

If your SMTP server uses “pop before smtp” authentication, check PopBeforeSMTPForActionMailer

You need to restart WEBrick or Apache to reload changes to to your environment.

Note for TextDrive users: On TextDrive, the default settings work, so no explicit configuration is needed. Leave the server_settings as their defaults (ie. don’t set anything).

Attachments

Instead of calling deliver_XX_mail (whatever your method is called) call create_XX_mail… it returns a TMail object

mail = Mailer.create_my_mail(params)

after that, you can set the mime type and append attachments:

mail.set_content_type('multipart', blah blah)
mail.parts << my_attachment
%{color:red}
(
Excuse me, but what is ‘blah blah’ supposed to be? 
% mail.set_content_type(‘multipart’, ’mixed’) ? %{color:red}
(And is my_attachment just any binary file,
or is there some missing encoding process
involved?)
%

then call

Mailer.deliver(mail)

where Mailer is the name of your class that extends ActionMailer::base.

Gem rails added attachment handling


    # attachments
    def signup_notification(recipient)
      recipients      recipient.email_address_with_name
      subject         "New account information"
      from            =="<a href="mailto:system@example.com">system@example.com</a>"==

attachment :content_type => “image/jpeg”, :filename => “another-image.jpg” :body => File.read(“an-image.jpg”) attachment “application/pdf” do |a| a.body = generate_your_pdf_here() end end

You will probably want to specify the filename of the attachment, and also send it along with a text message:


def new_user(user)
	@recipients = user.email
	@from = <a href="mailto:info@mysite.com">info@mysite.com</a>
	@subject = "Welcome to My Site"
	
	body = {}
	body['first_name'] = user.first_name
	part :content_type => "text/plain",
		 :body => render_message('new_user', body)
		 
	attachment "application/pdf" do |a|
		a.disposition = "attachment"
        a.filename= "myfilenamethatmightbedifferent.pdf"
		a.body = File.read(RAILS_ROOT + "/path/to/myfile.pdf")
	end
end

I don’t know if this is the simplest or latest way to do this, but it works for me. Also note that the ‘body’ variable in the example is not the usual ‘@body’ instance variable; it is a hash passed to render_message() function. I use this variable in the example to demonstrate the relationship to the standard way of sending an email.

Sending emails with TLS and/or authentication-only SMTP servers

If you encounter a smtp server that requires TLS, then you cannot send emails directly using ActionMailer. But you can still send emails by using a lightweight substitute for sendmail such as msmtp to act as mediatory between ActionMailer and the SMTP server. Here is what I did to get ActionMailer up and running:

  1. Download and install msmtp
  2. obtain server info (example for gmail):
    msmtp —serverinfo —host=smtp.gmail.com —tls=on —port=587 —tls-certcheck=off
  3. this may specify the certificate issuer as “Thawte Premium Server CA”
  4. Download the Thawte certificates and put ThawtePremiumServerCA_b64.txt in a convenient directory
  5. Configure it by putting the following in your $HOME/.msmtprc. Make sure that you set the correct permissions. The file must be owned by the user that will invoke msmtp, and must have 600 as its permission.
    
    account provider
    host <smtp.your-host.com>
    auth on
    port <port_num>
    user <username>
    password <password>
    tls on
    *tls_trust_file <directory>ThawtePremiumServerCA_b64.txt*
    tls_starttls on
    auto_from on

account default : provider

  1. Then put the following in your environment.rb towards the end. Be sure to replace /usr/local/bin/msmtp with the correct path where your msmtp is installed.
    
    ActionMailer::Base.delivery_method = :msmtp

module ActionMailer
class Base
def perform_delivery_msmtp(mail)
IO.popen(“/usr/local/bin/msmtp -t -C /path/to/your/.msmtprc -a provider —”, "w") do |sm|
sm.puts(mail.encoded.gsub(/\r/, ’’))
sm.flush
end
if $? != 0
# why >> 8? because this is posix and exit code is in bits 8-16
logger.error(“failed to send mail errno #{$? >> 8}”)
end
end
end
end

  1. You are done. You should be able to send emails using ActionMailer through a TLS and/or authentication only SMTP server without any problems.

Another way to do this is outlined http://stephenchu.blogspot.com/2006/06/how-to-use-gmail-smtp-server-to-send.html

Along the same lines is http://blog.pomozov.info/posts/how-to-send-actionmailer-mails-to-gmailcom.html but it seems a little easier to follow. Be sure to scroll down to the updated version.

Tips

  • It seems to be that if you make a change to your notifier model, you need to restart the server. Can anyone clarify this?
    yes, I encountered this too. When I changed the number of arguments for mail sending function I kept getting “wrong number of arguments” until I restarted the server.
  • While many of the examples have a single “user” or “recipient” parameter in the custom ActionMailer methods, you can actually pass any information you need to your method using any number of parameters. For example:
class AdminMailer < ActionMailer::Base
  def message(recipient, subject, body)
    @from = =="<a href="mailto:admin@expresstrans.com">admin@expresstrans.com</a>"==
    @recipients, @subject, @body = recipient, subject, body
  end
end
  • The @body instance variable can be a string—it does not have to be a hash containing other variables. In such a case, no ERb view need be created, since the entire body will be the @body string.
  • Caution: don’t make your method name ‘send’ or you get some wrong number of arguments errors
    category:Howto
  • For servers not requiring an :authentication => method, remove the :username and :password entries as well, leaving them blank will result in all sorts of unhelpful error messages like 502 Command is locally disabled.

Mass mailing
When you want to send a mail to a large number of recipients, you shouldn’t do it purely with ActiveMailer as it opens an SMTP connection for each mail sent. You can find a script to do just that at Mass mailing with ActiveMailer,

Questions

  • I am getting this error when I try the above program. No rhtml, rxml, or delegate template found for signup_thanks.rhtml. Any suggestions?
    • I had this problem, too. My text editor was creating a backup of the rhtml doc. When I deleted it, the error went away.
    • I’ve fixed this problem through introspection as I didn’t find a solution online. If you have localization installed then you will have to append the language identifier to your rhtml file or it won’t be found – for example sendmsg_en.rhtml instead of send_msg.rhtml. The generator will NOT do this for you correctly.
    • I fixed this error by making this change to my environment.rb file: config.frameworks -= [ :action_web_service, :action_mailer ] – remove :action_mailer
  • Are there ANY complete examples of sending a plain text message with a binary attachment? Where the plain text is an rhtml template (“Dear <%= @name %>, here is the document you asked for.”)
    All examples of sending attachements show only attachements (e.g, plain text as an attached file), not mixed content.
    It seems aas if that would be a common task, but it is not covered in the AWDWR book or in the API docs.
    The Rails Recipes bookrr/index.html by Chad Fowler lists how to send an email with an attachment: recipe 69, page 307_
  • the email bodies are showing up in my rails application logs, causing a large amount of wasted space. Anybody know how to prevent this?

*Can someone give more info for mass mailing? The link is not very helpful as far as a step by step process.. (do you HAVE to send mails from the command line? What if you want to send mails from a form on your site? How does that work?)

Hi All I am getting error code 69 while using msmtp please help me to come out of this my email id is aashutosh.tiwari@in.v2solutions.com

  • You will probably want to set
    config.action_mailer.raise_delivery_errors = true
    in config/environments/development.rb while you are trying to get this to work.

can somebody help me by telling how to get a smtp server

  • I was able to get the email to work when a user gets created and some action happens however is there a way to schedule an email so it runs everynight or something. So it can do some calculations and send it if some condition is met?

Screencast:
How to send emails with pdf attachments in Rails2.0

Note: This page may be out of date with current API
but the initial setup and example have been modified to reflect the use of methods versus instance variables.

My updated tutorial for sending emails, tested with 2.0
How to send email with Ruby on Rails

ActionMailer is a Rails service-layer package for creating email messages.

Generate a mailer

Create a stub file using the generator:

ruby script/generate mailer Notifier

A file is created in your app/models directory named notifier.rb:

class Notifier < ActionMailer::Base
end

A few points to keep in mind

There are a few confusing things to learn with Action Mailer that took a while to figure out.

  • Action Mailer implementations have views. The above example will reference files in app/views/notifier
  • Each email that you want to send should have one associated method within the class.
  • You don’t call that method. Ever.
  • Instead, you call some dynamic methods, as we’ll see below.

An Example

The scenario

Let’s assume we’re sending an email to a new user to our website to thank them for signing up, and that user is an object coming from an Active Record that has already been created.

Notifier

Add the following method to your Notifier:

def signup_thanks( user )
  # Email header info MUST be added here
  recipients user.email
  from  =="<a href="mailto:accounts@mywebsite.com">accounts@mywebsite.com</a>"==
  subject "Thank you for registering with our website"

  # Email body substitutions go here
  body :user=> user
end

Notice that it is recipient*s* even if you are only sending to one user.

Controller

And the following snippet to the controller in your application that needs to send an email:

def show_page_after_account_creation
...
  Notifier.deliver_signup_thanks(user)
...
end

This threw me for awhile: Make sure you notice the “deliver_” prefix to the method defined in the Notifier model.

View

The body of the email comes from a .rhtml file in app/views/notifier – In this example; app/views/notifier/signup_thanks.rhtml:
(Note: You might have to end the name with en.rhtml, i.e.signupthanks_en.rhtml, if you use localization)

Dear <%= @user.first_name %> <%= @user.last_name %>,

Thanks for signing up with My Website!

Your account will allow you to do blah, blah, blah.

It is possible to use helpers to your email templates simply by adding



helper :application


to your mailer model. (read the article)

Configuration

The mail configuration should be set in your environment. See the API documentation for configuration options. To have your configuration available for all server environments put it in /config/environment.rb, otherwise place it in config/environments/production.rb or config/environments/development.rb.

Make sure ActionMailer is removed from this line in environment.rb:

config.frameworks -= [ :action_web_service, :action_mailer ]

The default method for sending mail is SMTP. ActionMailer can also use sendmail, and for initial testing and development you probably want to switch to that. Stick this in your environment.rb: (After the Rails::Initializer.run do |config| block)

ActionMailer::Base.delivery_method = :sendmail

You may also turn exception raising on and off. The documentation does not state which is the default, so just to be sure, you can turn on exceptions like so:

ActionMailer::Base.raise_delivery_errors = true

A sample configuration setting using the default smtp delivery method would look something like this:

# Include your app's configuration here:
ActionMailer::Base.smtp_settings = {
  :address  => "smtp.postoffice.net",
  :port  => 25, 
  :domain  => "www.mywebsite.com",
  :user_name  => =="<a href="mailto:me@postoffice.net">me@postoffice.net</a>"==,
  :password  => "mypass",
  :authentication  => :login
    } 

:domain is the name sent to the SMTP server during the handshake. It will be used to form the first “Received:” header, which spam filters usually check.

If your SMTP server uses “pop before smtp” authentication, check PopBeforeSMTPForActionMailer

You need to restart WEBrick or Apache to reload changes to to your environment.

Note for TextDrive users: On TextDrive, the default settings work, so no explicit configuration is needed. Leave the server_settings as their defaults (ie. don’t set anything).

Attachments

Instead of calling deliver_XX_mail (whatever your method is called) call create_XX_mail… it returns a TMail object

mail = Mailer.create_my_mail(params)

after that, you can set the mime type and append attachments:

mail.set_content_type('multipart', blah blah)
mail.parts << my_attachment
%{color:red}
(
Excuse me, but what is ‘blah blah’ supposed to be? 
% mail.set_content_type(‘multipart’, ’mixed’) ? %{color:red}
(And is my_attachment just any binary file,
or is there some missing encoding process
involved?)
%

then call

Mailer.deliver(mail)

where Mailer is the name of your class that extends ActionMailer::base.

Gem rails added attachment handling


    # attachments
    def signup_notification(recipient)
      recipients      recipient.email_address_with_name
      subject         "New account information"
      from            =="<a href="mailto:system@example.com">system@example.com</a>"==

attachment :content_type => “image/jpeg”, :filename => “another-image.jpg” :body => File.read(“an-image.jpg”) attachment “application/pdf” do |a| a.body = generate_your_pdf_here() end end

You will probably want to specify the filename of the attachment, and also send it along with a text message:


def new_user(user)
	@recipients = user.email
	@from = <a href="mailto:info@mysite.com">info@mysite.com</a>
	@subject = "Welcome to My Site"
	
	body = {}
	body['first_name'] = user.first_name
	part :content_type => "text/plain",
		 :body => render_message('new_user', body)
		 
	attachment "application/pdf" do |a|
		a.disposition = "attachment"
        a.filename= "myfilenamethatmightbedifferent.pdf"
		a.body = File.read(RAILS_ROOT + "/path/to/myfile.pdf")
	end
end

I don’t know if this is the simplest or latest way to do this, but it works for me. Also note that the ‘body’ variable in the example is not the usual ‘@body’ instance variable; it is a hash passed to render_message() function. I use this variable in the example to demonstrate the relationship to the standard way of sending an email.

Sending emails with TLS and/or authentication-only SMTP servers

If you encounter a smtp server that requires TLS, then you cannot send emails directly using ActionMailer. But you can still send emails by using a lightweight substitute for sendmail such as msmtp to act as mediatory between ActionMailer and the SMTP server. Here is what I did to get ActionMailer up and running:

  1. Download and install msmtp
  2. obtain server info (example for gmail):
    msmtp —serverinfo —host=smtp.gmail.com —tls=on —port=587 —tls-certcheck=off
  3. this may specify the certificate issuer as “Thawte Premium Server CA”
  4. Download the Thawte certificates and put ThawtePremiumServerCA_b64.txt in a convenient directory
  5. Configure it by putting the following in your $HOME/.msmtprc. Make sure that you set the correct permissions. The file must be owned by the user that will invoke msmtp, and must have 600 as its permission.
    
    account provider
    host <smtp.your-host.com>
    auth on
    port <port_num>
    user <username>
    password <password>
    tls on
    *tls_trust_file <directory>ThawtePremiumServerCA_b64.txt*
    tls_starttls on
    auto_from on

account default : provider

  1. Then put the following in your environment.rb towards the end. Be sure to replace /usr/local/bin/msmtp with the correct path where your msmtp is installed.
    
    ActionMailer::Base.delivery_method = :msmtp

module ActionMailer
class Base
def perform_delivery_msmtp(mail)
IO.popen(“/usr/local/bin/msmtp -t -C /path/to/your/.msmtprc -a provider —”, "w") do |sm|
sm.puts(mail.encoded.gsub(/\r/, ’’))
sm.flush
end
if $? != 0
# why >> 8? because this is posix and exit code is in bits 8-16
logger.error(“failed to send mail errno #{$? >> 8}”)
end
end
end
end

  1. You are done. You should be able to send emails using ActionMailer through a TLS and/or authentication only SMTP server without any problems.

Another way to do this is outlined http://stephenchu.blogspot.com/2006/06/how-to-use-gmail-smtp-server-to-send.html

Along the same lines is http://blog.pomozov.info/posts/how-to-send-actionmailer-mails-to-gmailcom.html but it seems a little easier to follow. Be sure to scroll down to the updated version.

Tips

  • It seems to be that if you make a change to your notifier model, you need to restart the server. Can anyone clarify this?
    yes, I encountered this too. When I changed the number of arguments for mail sending function I kept getting “wrong number of arguments” until I restarted the server.
  • While many of the examples have a single “user” or “recipient” parameter in the custom ActionMailer methods, you can actually pass any information you need to your method using any number of parameters. For example:
class AdminMailer < ActionMailer::Base
  def message(recipient, subject, body)
    @from = =="<a href="mailto:admin@expresstrans.com">admin@expresstrans.com</a>"==
    @recipients, @subject, @body = recipient, subject, body
  end
end
  • The @body instance variable can be a string—it does not have to be a hash containing other variables. In such a case, no ERb view need be created, since the entire body will be the @body string.
  • Caution: don’t make your method name ‘send’ or you get some wrong number of arguments errors
    category:Howto
  • For servers not requiring an :authentication => method, remove the :username and :password entries as well, leaving them blank will result in all sorts of unhelpful error messages like 502 Command is locally disabled.

Mass mailing
When you want to send a mail to a large number of recipients, you shouldn’t do it purely with ActiveMailer as it opens an SMTP connection for each mail sent. You can find a script to do just that at Mass mailing with ActiveMailer,

Questions

  • I am getting this error when I try the above program. No rhtml, rxml, or delegate template found for signup_thanks.rhtml. Any suggestions?
    • I had this problem, too. My text editor was creating a backup of the rhtml doc. When I deleted it, the error went away.
    • I’ve fixed this problem through introspection as I didn’t find a solution online. If you have localization installed then you will have to append the language identifier to your rhtml file or it won’t be found – for example sendmsg_en.rhtml instead of send_msg.rhtml. The generator will NOT do this for you correctly.
    • I fixed this error by making this change to my environment.rb file: config.frameworks -= [ :action_web_service, :action_mailer ] – remove :action_mailer
  • Are there ANY complete examples of sending a plain text message with a binary attachment? Where the plain text is an rhtml template (“Dear <%= @name %>, here is the document you asked for.”)
    All examples of sending attachements show only attachements (e.g, plain text as an attached file), not mixed content.
    It seems aas if that would be a common task, but it is not covered in the AWDWR book or in the API docs.
    The Rails Recipes bookrr/index.html by Chad Fowler lists how to send an email with an attachment: recipe 69, page 307_
  • the email bodies are showing up in my rails application logs, causing a large amount of wasted space. Anybody know how to prevent this?

*Can someone give more info for mass mailing? The link is not very helpful as far as a step by step process.. (do you HAVE to send mails from the command line? What if you want to send mails from a form on your site? How does that work?)

Hi All I am getting error code 69 while using msmtp please help me to come out of this my email id is aashutosh.tiwari@in.v2solutions.com

  • You will probably want to set
    config.action_mailer.raise_delivery_errors = true
    in config/environments/development.rb while you are trying to get this to work.

can somebody help me by telling how to get a smtp server

  • I was able to get the email to work when a user gets created and some action happens however is there a way to schedule an email so it runs everynight or something. So it can do some calculations and send it if some condition is met?

Screencast:
How to send emails with pdf attachments in Rails2.0