An example from the ActionMailler API docs
# attachments
def signup_notification(recipient)
recipients recipient.email_address_with_name
subject "New account information"
from "system@example.com"
attachment :content_type => “image/jpeg”,
:body => File.read(“an-image.jpg”)
attachment “application/pdf” do |a|
a.body = generate_your_pdf_here()
end
end
You can easily make your own parts and subparts too Check the official ActionMailer API docs first! If they miss stuff look for comments in the ActionMailer source.
The trick for sub-sub-parts is simply to give a block to the part() method.
Here’s an example with an explicit text/plain and text/html message content, followed by an attachment.
class PackageMailer < ActionMailer::Base
def some_mail(binary_contents)
@subject = "mail with subparts"
@recipients = "blah"
part( :content_type => "multipart/alternative" ) do |p|
p.part( :content_type => "text/plain",
:body => render_message("some_mail.text.plain.rhtml", {}) )
p.part( :content_type => "text/html",
:body => render_message("some_mail.text.html.rhtml", {}) )
end
attachment( :content_type => "application/octet-stream",
:body => binary_contents,
:filename => "some_attachment" )
end
end
Note that attachments are just a special kind of parts.
The section below details how to work with TMail to create Mime multipart messages. It it no longer relevant since ActionMailler now provides it’s own interface for creating those messages.
First, see HowToSendEmailsWithActionMailer, as that gives the foundation for creating a mailer object. Pay specific attention to the section on Attachments, because it deals with creation of the raw TMail object. Ok…
In this case, I’ll make a text email and attach a CSV file.
With any multipart email, you need to have a base to add the parts on to. The TMail object treats each part of the whole email as TMail::Mail object. In this way, you can add many different email parts together (attachments, html, text) together fairly simply.
To start, make a container Mail message:
mail = TMail::Mail.new mail.date = Time.now mail.set_content_type 'multipart', 'mixed' mail.mime_version = '1.0' mail.body = nil
It doesn’t need much, because it doesn’t do much :)
WARNING: The code above does not work with TMail and causes an error… The set_content_type has to be set after all parts are added and not before!
The text body of the email gets attached next. Make a standard Mailer class and use the “create_” style call to make the TMail object without sending it.
message = MyMailer.create_my_email
message.set_content_type('text', 'plain')
message.transfer_encoding = '7bit'
Once you’ve got that object, and you’ve got the MIME params set up, just push it onto the container TMail message
mail.parts.push(message)
This is very similar to the first step, as we have to make a mail object that used for the container:
filename = "report.csv"
encoded_file = "1,1,1\n"
attachment = TMail::Mail.new
attachment.body = encoded_file
attachment.transfer_encoding = '7bit'
attachment.set_content_type('application', 'csv', 'name' => filename)
attachment.header["Content-Disposition"] = "attachment; filename=" + filename
Once again, to add the ‘attachment’ to the ‘container’:
mail.parts.push(attachment)
You might notice that for the content-type I used a method, but for the content-disposition I used the hash; This is only because the following line threw an exception for me.
attachment.set_content_disposition('attachment', 'filename' => filename)
Note you could just as easily create a mailer whose template is the file body and you pass in the data, having the mailer render the file, but that did not suit me at the time :)
Seeing as we already have a reference to the \MyMailer object, use it to send. The mail.writeback call ‘flattens’ the TMail objects out into a MIME message ready for sending.
mail.write_back MyMailer::deliver(mail)