So I have recently become acquainted with Ruby on Rails and have been playing around with some of its functionality. One of the things I spent the most time trying to figure out was how to get my application to send mail correctly. After following the instructions on all the existing sites out there I had it working in my development environment but found that it crapped out when I tried to port it to a production environment. So I decided to throw something together to explain the steps I went through to get everything working.
Disclaimer – I am a Java guy by trade and am new to RoR. If I am doing something “the hard way” please speak up RoR gurus.
First, the background. Ruby on Rails handles most stuff for you – the object relational model (ORM), database transactions, views, and even mail. The mail is composed of a couple parts: configuration, a model, a view, and a client. Lets step through these.
The configuration is simply information that goes into either the config/environment.rb or the config/environments/[production || development || test].rb file. If you use one of the files in the config/environments folder you will be able to specify multiple settings for multiple environments. For example, I set mine up so that config/environments/development.rb pointed to my localhost mail server while my config/environments/production.rb pointed to my hosted mail server. Here is the basic information you’ll need:
ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.raise_delivery_errors = true
ActionMailer::Base.smtp_settings = {
:address => "smtp.myserver.com",
:port => 25,
:user_name => "myusername",
:password => "mypassword",
:authentication => :login
}
Next, you will need a model that contains the actual functionality for sending the mail and interacting with the ActiveMailer. The easiest way to do this is to run “ruby script/generate mailer mymailertest” (for non-windows drop the “ruby”). This will produce the following files:
exists app/models/ create app/views/mytestmailer exists test/unit/ create test/fixtures/mytestmailer create app/models/mytestmailer.rb create test/unit/mytestmailer_test.rb
Before we start describing the parts, lets take a look at how this all works. When the client (usually a controller, but really can be any part of the code) wants to send an email they will invoke the mailer model, calling a specific method. This method will set up the email then forward to a view, which renders the contents of the email. This rendered content is what is actually sent over the wire.
We now need hook the model up so that it will work. The model, mytestmailer.rb, already extends ActionMailer so we just need to add a method. This method will be called later to deliver mail. The method we define will be mapped to a view that will actually be the email template. We will explain that next. First, add a method to your model that will represent an action taken by your application – welcome, confirmation, invalid login attempt, etc. An important note in this step is that if you want your arriving email to display your/a name instead of the email address you will need to use the notation used in the from line below, since the from line equates to the SMTP FROM header. Also, this assumes that you have a User or some other object that you wish to pass to the view in order to render the email.
class Mytestmailer < ActionMailer::Base
def welcome(user)
recipients "somename@somedomain.com"
from "Your Name "
subject "RoR Test Email"
body :user => user
end
end
This will automatically forward to a view with the same name as the method, in this case app/views/mytestmailer/welcome.html.erb (note that if you are using a version of RoR prior to 2.* you will have an extension of *.rhtml – this makes no difference). Put simply, the model is the contents of the email only with scripting functionality built in. In our scenario, the model will pass in a User object which we will use to construct the email. Our view looks like this:
Dear <%= @user.first %> <%= @user.last %>,
Thanks for signing up for the McDonaldLand mailing list!
We are here to help so if you ever need us blah blah blah.
Thanks for your interest in McDonaldLand,
Jason McDonald
So we are almost done. We just need to set a client up to call the mailer. From the client we would simply invoke the deliver_* method on the mailer object, which would then delegate to the appropriate method. In our example we would call deliver_welcome, which would route to the welcome method. The arguments passed here will be routed to the mailer model object as well. So to send our email from whatever part of the code we want all we have to do is this:
mytestmailer.deliver_welcome(@user)
That’s it.