Sending Mail

Problem

You want to send an email message, either an autogenerated one or one entered in by an end user.

Solution

First you need to turn the parts of the email message into a single string, representing the whole message complete with headers and/or attachments. You can construct the string manually or use a number of libraries, including RubyMail, TMail, and ActionMailer. Since ActionMailer is one of the dependencies of Rails, Ill use it throughout this recipe. ActionMailer uses TMail under the covers, and its provided by the actionmailer gem.

Here, I use ActionMailer to construct a simple, single-part email message:

	require 
ubygems
	require action_mailer

	class SimpleMailer < ActionMailer::Base
	 def simple_message(recipient)
	 from leonardr@example.org
	 recipients recipient
	 subject A single-part message for you
	 body This message has a plain text body.
	 end
	end

ActionMailer then makes two new methods available for generating this kind of email message: SimpleMailer. create_simple_message, which returns the email message as a data structure, and SimpleMailer. deliver_simple_message, which actually sends the message.

	puts SimpleMailer. 
create_simple_message(lucas@example.com)
	# From: leonardr@example.org
	# To: lucas@example.com
	# Subject: A single-part message for you
	# Content-Type: text/plain; charset=utf-8
	#
	# This message has a plain text body.

To deliver the message, call deliver_simple_message instead of create_simple_message. First, though, youll need to tell ActionMailer about your SMTP server. If you e sending mail from example.org and youve got an SMTP server on the local machine, you might send a message this way:

	ActionMailer::Base.server_settings = { :address => localhost,
	 :port => 25, # 25 is the default
	 :domain => example.org }

	SimpleMailer.deliver_simple_message(lucas@example.com)

If you e using your ISPs SMTP server, youll probably need to send authentication information so the server knows you e not a spammer. Your ActionMailer setup will probably look like this:

	ActionMailer::Base.server_settings = { :address => smtp.example.org,
	 :port => 25,
	 :domain => example.org,
	 :user_name => leonardr@example.org,
	 :password => my_password,
	 :authentication => :login }

	SimpleMailer.deliver_simple_message(lucas@example.com)

Discussion

Unless you e writing a general-purpose mail client, you probably won be letting your users compose emails from scratch. More likely, youll define a template for every type of email your application might send, and fill it in with custom data every time you send a message.[1]

[1] You can use ActionMailer even if you are writing a general-purpose mail client (just write a single hook method called custom_messge that takes a whole lot of arguments), but you might prefer to drop down a level and use TMail or RubyMail.

This is what ActionMailer is designed for. The simple_message method defined above is actually a hook method that makes ActionMailer respond to two other methods: create_simple_message and deliver_simple_message. The hook method defines the headers and body of a message template, the create_ method instantiates the template with specific values, and the deliver_ method actually delivers the email. You never call simple_message directly.

Within your hook method, you can set most of the standard email headers by calling a method of the same name (subject, cc, and so on). You can also set custom headers by modifying the @headers instance variable:

	class SimpleMailer
	 def headerful_message
	 @headers[A custom header] = Its value
	 body Body
	 end
	end

	puts SimpleMailer.create_headerful_message
	# Content-Type: text/plain; charset=utf-8
	# A custom header: Its value
	#
	# Body

You can create a multipart message with attachments by passing the MIME type of the attachment into the attachment method.

Heres a method that creates a message containing a dump of the files in a directory (perhaps a bunch of logfiles). It uses the mime-types gem to determine the probable MIME type of a file, based on its filename:

	require mime/types

	class SimpleMailer
	 def directory_dump_message(recipient, directory)
	 from directory-dump@example.org
	 recipients recipient
	 subject "Dump of #{directory}"
	 body %{Here are the files currently in "#{directory}":}

	 Dir.new(directory).each do |f|
	 path = File.join(directory, f)
	 if File.file? path
	 mime_type = MIME::Types.of(f).first
	 content_type = (mime_type ? mime_type.content_type :
	 application/binary)
	 attachment(content_type) do |a|
	 a.body = File.read(path)
	 a.filename = f
	 a.transfer_encoding = quoted-printable if content_type =~ /^text//
	 end
	 end
	 end
	 end
	end

	SimpleMailer.create_directory_dump_message(lucas@example.com,
	 email_test)

Here it is in action:

	Dir.mkdir(email_test)
	open(email_test/image.jpg, wb) { |f| f << "3773303773400020JFIF" }
	open(email_test/text.txt, w) { |f| f << "Heres some text." }

	puts SimpleMailer.create_directory_dump_message(lucas@example.com,
	 email_test)
	# From: directory-dump@example.org
	# To: lucas@example.com
	# Subject: Dump of email_test
	# Mime-Version: 1.0
	# Content-Type: multipart/mixed; boundary=mimepart_443d73ecc651_3ae1..fdbeb1ba4328
	#
	#
	# --mimepart_443d73ecc651_3ae1..fdbeb1ba4328
	# Content-Type: text/plain; charset=utf-8
	# Content-Disposition: inline
	#
	# Here are the files currently in "email_test":
	# --mimepart_443d73ecc651_3ae1..fdbeb1ba4328
	# Content-Type: image/jpeg; name=image.jpg
	# Content-Transfer-Encoding: Base64
	# Content-Disposition: attachment; filename=image.jpg
	#
	# /9j/4AAQSkZJRg==
	#
	# --mimepart_443d73ecc651_3ae1..fdbeb1ba4328
	# Content-Type: text/plain; name=text.txt
	# Content-Transfer-Encoding: Quoted-printable
	# Content-Disposition: attachment; filename=text.txt
	#
	# Heres some text.=
	#
	# --mimepart_443d73ecc651_3ae1..fdbeb1ba4328--

If you e a minimalist, you can use the net/smtp library to send email without installing any gems. Theres nothing in the Ruby standard library to help you with creating the email string, though; youll have to build it manually. Once youve got the string, you can send it as an email message with code like this:

	require 
et/smtp
	Net::SMTP.start(smtp.example.org, 25, example.org,
	 leonardr@example.org, my_password, :login) do |smtp|
	 smtp.send_message(message_string, from_address, to_address)
	end

Whether you use Net::SMTP or ActionMailer to deliver your mail, the possible SMTP authentication schemes are represented with symbols (:login, :plain, and :cram_md5). Any given SMTP server may support any or all of these schemes. Try them one at a time, or ask your system administrator or ISP which one to use.

See Also

  • Recipe 15.19, " Sending Mail with Rails," if you e using Rails
  • The ActionMailer documentation (http://www.lickey.com/rubymail/rubymail/doc/ )
  • The standard for email messages (RFC 2822)
  • More ActionMailer examples (http://am.rubyonrails.com/classes/ActionMailer/Base.html)


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net