Ruby, JavaScript, Sass, iOS. Stinky Cheese & Beer Advocate. Working at CustomInk and loving it!

How To Stop Delivery Of An Email Within An ActionMailer Method

OK, so you want to keep your code placement REALLY organized. You have read about my persnicketyness and now you want to practice the best in concern placement and keep those controllers of yours really slim. Like me, you may want to try and keep controller feature additions to very specific one liners of code. Organizing your controller code to do just that with ActiveRecord models or even your own custom classes is a pretty easy task, but how do you keep things simple when dealing with controller actions that have to send email AND you want that single email link of code to be responsible for everything in it's own encapsulated way.

The answer is to push the logic back to the model again. Your controllers should not be concerned with the logic that deals with your own business rules for sending email. The problem with ActionMailer is that most people just use the dynamic MyMailerClass.deliver_ methods that utilize method_missing to instantiate a mailer classs, and send an email in one fell swoop. I like this usage too, but the challenge is how to tell an instance of a the ActionMailer::Base class that it needs to stop delivery within a method definition that is all set to deliver? The answer is Ruby singleton method magic.

module ActionMailer
  class Base

    # A simple way to short circuit the delivery of an email from within
    # deliver_* methods defined in ActionMailer::Base subclases.
    def do_not_deliver!
      def self.deliver! ; false ; end
    end

  end
end

I suggest placing this code into your lib/core_ext/action_mailer.rb file. It will allow you to write mailer methods that can short circuit themselves to stop delivery. For instance:

class MyMailerClass < ActionMailer::Base
  def user_notification(user)
    # ...
    do_not_deliver! if user.email.blank?
  end
end
# In your controller code.
MyMailerClass.deliver_user_notification(@user)

Now you do not have to worry about placing delivery concerns in the controller and even worse duplicate that code when you have to use the same mailer method in multiple places. This works by letting the instance of the ActionMailer::Base class define it's own deliver! method which trumps the delier! method called by that object normally defined in ActionMailer::Base.

Resources