How to send SMTP mail in Ruby using ActionMailer (outside Rails)

Like most bits of Rails, ActionMailer has an elegant and coder friendly interface. With a bit of set up, it’s remarkably quick and easy to get running from vanilla Ruby outside of Rails.

Recently I’ve needed to bulk email a bunch of files to an internal server for testing purposes. I’ve used ActionMailer inside Rails in the past, and wondered how hard it would actually be to get it up and running standalone. Sure, there are a stack of other mail gems and libraries in Ruby to do this, but they expose a lot of the internals of STMP and can be a pain to use. Definitely overkill when all you want to do is quickly fire off some mails from a script. (ActionMailer is actually built on top of a lot of those libraries, and acts like a coder friendly wrapper).

My requirements were pretty simple: Iterate over a bunch of files, and attach each one to a separate email and send to some address.

Here’s what the code looks like for the ActionMailer class:

require action_mailer

ActionMailer::Base.smtp_settings =
{:address => smtp.example.com‘,
 :domain  => example.com‘}

class FileMailer < ActionMailer::Base
  def file(to, sender, file_name, content_type, strip_ext = true)
    # strip any directory fluff 
    subj = file_name.gsub(/.*\//,’‘)
    #remove the file extension if required
    subj = subj.gsub(/\.\w*/,’‘) if strip_ext

    #standard ActionMailer message setup
    recipients  to
    from        sender
    subject     subj
    #setting the body explicitly means we don’t have to provide a separate template file
    body        

    #set up the attachment
    attachment  :content_type => content_type,
                :body         => File.read(file_name),
                :filename     => file_name.gsub(/.*\//,’‘)
  end
end

The only thing different from Rails, is that you need to explicitly configure the SMTP details via ActionMailer::Base#smtp_settings.

It’s worth noting that you don’t actually need to create a .rhtml view file if you specify the body attribute of your message. I ran into a few blogs claiming there was no way to turn off the .rhtml requirement - a quick inspection of the ActionMailer source code proves otherwise. I’m sure it violates good MVC design, but if you’re just throwing together a quick script, who cares. I’ve left the body in the example as an empty string (the server at the other end was only interested in the attachement), but you can specify whatever string you want there.

To use the mailer class, you just call it in the normal Rails ActionMailer manner. With ActionMailer you don’t call the mail action method you implemented, but a generated method prefixed with deliver_. So in our example, even though we implemented a method file, we actually call deliver_file (passing it the same parameters).

FileMailer.deliver_file(’recipient@example.com‘,’julian@example.com‘,’my_file.csv‘,’text/csv‘)

10 Responses to “How to send SMTP mail in Ruby using ActionMailer (outside Rails)”

  1. Anonymous Says:

    Hi Julian,
    Just found your post with google. I am new into programming, and new to ruby as well.

    I am trying to use your example as a standalone tool to send all .jpg files from a directory. I have not succeeded yet, still installing some gems.

    Any more ideas how to make this automatic, when a new.jpg file shows up in a directory, sent it via e-mail directly.

    Need to get the “smtp relay” with auth to work as well via my ISPs servers.

    This will be used for my the surveillance cameras in my summer house…

    Regards
    Gunnar
    Stockholm

  2. Julian Doherty Says:

    Hi Gunnar

    This example just does the mailing part. It’s not too hard to call it from other code that does the work assembling the file list.

    In the case where I’m using this, the code to manipulate the files would look something like

    #start here
    files_to_email = Dir[’/home/juliand/to_email/*.jpg’]
    to = ’some.address@example.com’
    from = ‘julian@example.com’
    mime_type = ‘image/jpeg’
    files_to_email.each do |f|
    FileMailer.deliver_file(to, from, f, mime_type)
    end
    #end here

    (I think the format will get messed up by the comments system, but you get the idea)

    To get it to pick up all files in a directory whenever they arrive, you’d probably want to set up a cron job, or start a permanent ruby process running in the background. You’d need to write some extra code to periodically check for any files, email then, then move them to an archive directory (so they aren’t picked up the next time).

    Hope this helps.

  3. Julian Doherty Says:

    For the SMTP auth to work, http://am.rubyonrails.com/classes/ActionMailer/Base.html has the details on how ActionMailer can be configured.

    Basically, in the smtp_settings call in the original example, you’d want to pass in values for :domain, :user_name, :password, and possibly :authentication

  4. Gunnar Widell Says:

    Thanks for your help Julian,

    I had to do a require ‘rubygems’ as well.

    I now have a working send .jpg script! I also have mail notification via SMS, when the e-mail arrives…

    Remaining:
    crontab job that scans for new .jpg, and send the mail. Or, scan for .jpg, send e-mail, move .jpg to archive.

    This is great!

  5. Julian Doherty Says:

    Great. Glad to help out Gunnar :)

    The other ways to get rubygems working, is to start ruby with the -rubygems argument, or to export RUBYOPT=rubygems as an environment variable.

  6. Outta Says:

    Great article has helped me quite a bit. I have a question though, how could I get this to work so it would send link in the body of the email instead of an attachment?

    I’m looking to do something like link

    I can get http://linkSomewhere in the e-mail but when I add any tags it just becomes regular text.

    Thanks for any help

    Ray

  7. Julian Doherty Says:

    Outta - It’s a little more complicated. Using ActionMailer, you would need to set up an .rhtml template, and set the content type of the message to text/html (not the attachment as I’ve done here).

    Check out http://api.rubyonrails.com/classes/ActionMailer/Base.html for more info.

  8. Outta Says:

    Thank you!

  9. mohamed Says:

    Actually this program is working fine in Windows but not in Linux (CentOS 4.0 or newer),It throws an error like uninitialized constant ActionController::Base

    Can anybody tell me what is the problem with that?

  10. Hasmukh Says:

    Can u tell me how can we schedule email using action mailer? According to this mail is going, no problem. But i want to schedule means i want to set date and time to send mail.
    Thanks.

Leave a Reply