ActionMailer tips

27 Feb 2007

My latest story card is all about getting our app to send email so I’ve had to delve into ActionMailer. It’s a cool way to send emails without tons of code but there are some things that are a little bit wacky. Let us say that you’ve got a cookie website and you want people to be able to send emails of their favorite recipes to each other. “My that’s a handsome cookie” they might say and “Oh look, an email button that lets me send this recipe to a friend. This site has met my needs exactly.” In order to get such a thing to happen you would create a RecipeMailer model that extended ActionMailer kinda like this:

class RecipeMailer < ActionMailer::Base

def recipe_email(email_params)
@from = email_params[:from]
@recipients = email_params[:recipients]
@subject = email_params[:subject]
@sent_on = email_params[:sent_on]

@body[:recipe] = email_params[:recipe]
end
end

In your controller you’d call:

RecipeMailer.deliver_recipe_email(email_params)

And some good old method_missing magic would match deliver_recipe_email with your recipe_email method which would then render a view.

Er, a view?

That’s right, the email templates are rhtml views. You write them as you would a view, but ActionMailer turns them into emails. So one might look like this:

Howdy neighbor,
Here’s a recipe you might like:
<%= @recipe.text %>

And Rails would of course evaluate the <%= stuff like it would in a view. But, although it’s a view, it doesn’t have access to any helper methods you have defined, even if you put them in ApplicationHelper (which are supposed to be available to all views). You’ll have to declare this in your RecipeMailer model if you want the view to see them.

ApplicationHelper:
helper :application

Another thing to watch out for is any links you include in your email must have the full path to your app. When using link_to you can pass in these options:

:only_path => false, :host => @host

Where host is the host you get off of request.host but you won’t have access to request in your view or your mailer, so you’ll have to pass it in from your controller (again, the email template is only sort of a view). But if you’ve added some fancy custom tags (like a hypothetical recipe_image_tag that takes in a recipe object and does all sorts of neato stuff) you might find that the combination of needing a full path and being rendered by an email client (ie not your rails app) will force you to, sigh, hack up your own links like so:

<%=@cookie_name-%>.

Not the prettiest code I’ve ever written but sometimes time is a factor.