I know that most Rails developers have come across the problem of notifying people by email that they have an internal message on your app’s own messaging system. Usually this is easy enough, and elegantly done in your model by using
class InternalMessage < ActiveRecord::Base after_save :notify_person_of_new_internal_message protected def notify_person_of_new_internal_message MyMailer.deliver_new_posting(self.from_person, self.message) end end
However, let’s consider a situation where you need duplication of data on two different processes. For example, if you would like to generate a notification message when some posts to a common forum. The quick way to do this would be by calling two separate methods in your controller, like so:
subject = "Ted Stevens has posted a new topic" body = "From Ted Stevens: " + "'I hope you get this, the tubes have been acting up recently!'" @person.save_to_inbox(subject, body) MyMailer.deliver_new_posting(@person, subject, body)
This requires you to generate the subject and the body of the message and use the methods to work out the logic.
However, with a little ActionMailer hackery, you can make this scheme much cleaner.
MyMailer.notify(person, :new_posting, from_person, note)
In your model,
class MyMailer < ActionMailer::Base class << self def notify(person, method, *params) new_mailer = new new_mailer.create!(method, *params) mail = new_mailer.mail person.save_to_inbox(mail.subject, mail.body) deliver(mail) if person.wants_email_notification? end end def new_posting(from_person, note) from from_person.email subject "#{from_person.name} has posted a new topic" @body[:from] = from_person @body[:note] = note end end
Next, in views/my_mailer/new_posting.rhtml
From <%= @from_person %>: <%= @note %>
The following three lines generate a new MyMailer object, create the email specified by the method (in this case, :notify_posting) using ActionMailer’s create! method (which in turn uses Ruby’s glorious send method), and pull out the TMail object that ActionMailer sends to the SMTP server. Conveniently, the TMail object lets you extract out the subject and body, which lets you map them to your InternalMessage class.
new_mailer = new new_mailer.create!(method, *params) mail = new_mailer.mail
The above code generates the email, pulls out the generated subject and the body and uses it as the subject and body of the internal message. The idea is that the content of both the email notification and the internal email match up. This method does not require you to setup your subject and body. Instead, this method lets you use ActionMailer as a rendering framework for generating text/html for all of your internal messages. DRY is Good!