Many applications need to send email. Merb Mailers allow you to do this easily with the same controller and view conventions the rest of your application follows. The Mailer
class is thus a subclass of AbstractController
, and the relevant templates are standardly placed in app/mailers/views
. In this chapter we’ll configure, generate, describe, and test mailers used in Merb applications.
If you don’t configure Merb otherwise, it tries to use SMTP locally when asked to deliver mail. In most cases these exact defaults do not suffice, but alternative configurations are as simple as a few extra lines in either config/init.rb
or one of the environment config files in a before_app_loads
block. Below we describe the various configuration options available, separated by delivery method.
Though SMTP is the default for the Merb Mailer, chances are you’ll need to configure it more explicitly. This may involve specifying the host, port, user, password, authentication method, and HELO domain. Below we do this by setting various hash values for Merb::Mailer.config
.
Note that if you are using the Gmail SMTP servers or some other TLS-required SMTP service, you will need Anatol Pomozov’s gem smtp_tls
in order to enhance some of the net_smtp
methods.
In order to use sendmail, you must specify the mailer delivery method in a configuration file. This overrides the default of :net_smtp
. You may also need to alter the default sendmail path from /usr/sbin/sendmail
. Here we do both:
A third option, perfect for both test and possibly development environments, is the test_send
delivery method. This method does not actually send email but instead pushes the message into the array Merb::Mailer.deliveries
:
Merb::Mailer.delivery_method = :test_send
You can also create custom delivery methods for your applications. For instance, if the SMTP gateway you’re using severely penalizes attempts to send messages above the daily quota, you may want to construct a custom delivery method that first checks to make sure you haven’t surpassed your limit. By adding a couple of methods within Merb::Mailer
, we can do just this:
Note that in the example above, we haven’t included the actual methods for checking and increasing quota usage. This is because implementation will indubitably differ with the circumstances. One warning, though: Make sure the implementation is thread-safe. Otherwise, despite all your efforts, you may go beyond your quota anyway.
Though it’s not advisable, you can use mailers directly to send out messages. In most cases we recommend the use of the Merb Mail Controller, which is covered in the next section. However, because you may come across a scenario where you need to send out mail from inside some other noncontroller class, let’s cover its use directly.
Internally, Merb::Mailer
interfaces to David Powers’s MailFactory. Passing in a hash during initialization, you construct the email just as if you were using MailFactory itself and set the attributes of a message. Below we create and send a new message by first initializing a mailer object and then calling the Mailer#deliver!
method.
Some email clients are capable of also rendering HTML email. The Merb Mailer allows you to send HTML emails by simply setting the :html
key instead of the :text
key. However, it is standard practice to also include a text-only version whenever sending out HTML messages. Such dual-formatted messages are called multipart messages.
Constructing a multipart message with the Merb Mailer is as simple as setting both the :text
and :html
keys at initialization:
Let’s take a look at the code behind Merb::Mailer
to understand how it interfaces with MailFactory:
Mail controllers provide an MVC way of sending out email with Merb. In other words, they are intended to separate your message logic and presentation layers using controllers and views. To accomplish this, MailController
subclasses from Abstract-Controller
, inheriting, among other things, filtering and rendering capabilities.
Mail controllers are standardly located in app/mailers
. By convention their class names are singular words with the word Mailer appended. The actions of the controller represent various types of messages that can be sent. The convention is to name these actions according to the event that sparks them, for example, notify_on_invite
. If, however, there is only one action in the controller, it is often simply named notify
.
Each mail controller instance initializes with a MailFactory object, @mail
. We can consequently construct the message from within the action similarly to the way we did before, using the Merb Mailer directly. Note, however, that if both @mail.html
and @mail.text
are empty by the completion of the action, no message will be sent.
Mail controllers have been designed to be called from inside standard Merb controllers. Thus a subtle characteristic of the mail controller is its reliance on the controller that called it. This dependence allows for the accessing of the original request and session data as well as the use of URL helpers.
The sending of mail from a standard controller is invoked using Controller#send_mail
, a method mixed into Merb::Controller
when the merb-mailer
dependency is included. This method takes three parameters: the mail controller class name, the method name, and a hash of message parameters used during the initialization of @mail
. Below we present the invoking of an invite mailer.
We recommend that you send mail out of sync with the construction of responses since SMTP gateways can hold you up at times. The easiest way to do this is with a run_later
block. Here we use run_later
in a slightly more complex controller action:
Mail controllers by default have access to invoking a controller’s parameters. To access them with the mail controller, we simply use the params
mash as we would in a standard mail controller. We can, however, override the value of params
by using a fourth parameter on the action Controller#send_mail
. Below we pass in a custom hash of parameters to be used from within the mail controller.
Files can be attached to messages using the method MailController#attach
. This method takes either one or two parameters. When you use it to attach a single file, the first parameter should be the file. Optionally, the second parameter may be an alternate filename used when the message is actually sent. This may be useful if you are personalizing documents. Below we attach two documents, one at a time. Both will be included in the final email.
Instead of passing in an array to the attach
method, we can attach multiple documents at once:
attach([first_file, second_file])
Finally, we can also manually specify the MIME type and final filename using arrays of arrays:
Of course the principal attraction of using mail controllers is the rendering of mail message views. These are not stored in the same location as standard views but instead in /app/mailers/views
. Each mail controller should have its own subdirectory within this directory just like a standard controller. This subdirectory should be a snake-cased version of the mail controller’s own name in full, such as invite_mailer/
for the InviteMailer
. Mailer templates are rendered just the same as regular templates, so you should encounter no other complications.
Testing mailers can seem tricky since they’re not part of the standard request-response cycle. However, using a few extra methods alongside the :test_method
delivery method makes testing mailer behavior no problem at all.
In the code above we use the array Merb::Mailer.deliveries
, the array to which test delivery pushes, to test the results of the mailer against our expectations.
Finally, application developers should be aware that they can quickly generate the numerous files that mailers typically entail using merb-gen
. Below we create the invite mailer.
Merb Mailers are an excellent example of the intelligence of the Merb architecture. Subclassing off of the abstract controller base class, the Merb mail controller inherits standard controller functionality, allowing the application developer to deal with the sending of email through the same best practices used for the rest of the app.
3.143.203.146