Using ActionMailer for internal messaging 14/11/2009

Rendering internal messages inline can be quite confusing, especially if you want to use some helpers inside (like path helpers). It’d be more convenient to render them in the same way as other messages – using ActionMailer and ERB templates. My solution uses email addresses to figure out the message recipients:

class UserNotifier < ActionMailer::Base
  # sample message
  def some_message(user)
    from %Q{"Internal Messaging System"}
    recipients user.email
    subject "This is internal message"
    body :user => user
  end

  # sample message notification
  def message_notification(message)
    from %Q{"MyAppName.com mailer"}
    recipients message.recipient.email
    subject "You've new message in your inbox"
    body :user => user
  end

  private

  # if the called method has "deliver_internal_"
  # prefix, use notify method to deliver message
  def self.method_missing(method_name, *args)
    if method_name.to_s =~ /^deliver_internal_([_a-z]\w*)/
      new($1, *args).send(:deliver_internal)
    else
      super
    end
  end

  # deliver message to user using internal messaging
  def deliver_internal
    users = User.find_all_by_email(@mail.to)
    from = @mail.from.first[1..-2] # remove quotes
    users.each do |user|
      Message.create!(:recipient => user,
                      :subject => @mail.subject,
                      :body => @mail.body,
                      :sender_name => from)
    end
  end
end

Additionally you can notify the user about a new message, by implementing a simple callback in the Message model.

class Message < ActiveRecord::Base
  after_create :deliver_notification
  belongs_to :recipient, :class_name => "User"

  private

  def deliver_notification
    # deliver message notification email (external)
    # if the user wants it
    if recipient.wants_message_notifications?
      UserNotifier.deliver_message_notification(self)
    end
  end
end

There’s a really good point of this solution – you don’t need to create separate actions for external and internal messaging. The only difference is the method’s prefix:

# send message and notify by email if user wants it
UserNotifier.deliver_internal_some_message(user)
# send email only
UserNotifier.deliver_some_message(user)