Skip to content
This repository
Browse code

Asynchronous ActionMailer

Any ActionMailer class can be set to render and delier messages using
the new Rails Queue.

Some of this work was borrowed (with permission) from Nick Plante's
(zapnap) reqsue_mailer gem.
  • Loading branch information...
commit dc7fd821dca0b2088bde7e0a2a06bfe59e1ba5c6 1 parent 3726080
Brian Cardarella authored June 23, 2012
41  actionmailer/lib/action_mailer/async.rb
... ...
@@ -0,0 +1,41 @@
  1
+module ActionMailer::Async
  2
+  def self.included(base)
  3
+    base.extend(ClassMethods)
  4
+  end
  5
+
  6
+  module ClassMethods
  7
+    def method_missing(method_name, *args)
  8
+      if action_methods.include?(method_name.to_s)
  9
+        QueuedMessage.new(self, method_name, *args)
  10
+      else
  11
+        super
  12
+      end
  13
+    end
  14
+  end
  15
+
  16
+  class QueuedMessage
  17
+    delegate :to_s, :to => :actual_message
  18
+
  19
+    def initialize(mailer_class, method_name, *args)
  20
+      @mailer_class = mailer_class
  21
+      @method_name  = method_name
  22
+      *@args        = *args
  23
+    end
  24
+
  25
+    def run
  26
+      actual_message.deliver
  27
+    end
  28
+
  29
+    def deliver
  30
+      Rails.queue << self
  31
+    end
  32
+
  33
+    def actual_message
  34
+      @actual_message ||= @mailer_class.send(:new, @method_name, *@args).message
  35
+    end
  36
+
  37
+    def method_missing(method_name, *args)
  38
+      actual_message.send(method_name, *args)
  39
+    end
  40
+  end
  41
+end
7  actionmailer/lib/action_mailer/base.rb
@@ -456,6 +456,13 @@ def respond_to?(method, include_private = false) #:nodoc:
456 456
         super || action_methods.include?(method.to_s)
457 457
       end
458 458
 
  459
+      def async=(truth)
  460
+        if truth
  461
+          require 'action_mailer/async'
  462
+          include ActionMailer::Async
  463
+        end
  464
+      end
  465
+
459 466
     protected
460 467
 
461 468
       def set_payload_for_mail(payload, mail) #:nodoc:
14  actionmailer/test/base_test.rb
@@ -7,6 +7,9 @@
7 7
 require 'mailers/base_mailer'
8 8
 require 'mailers/proc_mailer'
9 9
 require 'mailers/asset_mailer'
  10
+require 'mailers/async_mailer'
  11
+
  12
+require 'rails/queueing'
10 13
 
11 14
 class BaseTest < ActiveSupport::TestCase
12 15
   def teardown
@@ -419,6 +422,16 @@ def teardown
419 422
     assert_equal(1, BaseMailer.deliveries.length)
420 423
   end
421 424
 
  425
+  test "delivering message asynchronously" do
  426
+    Rails.stubs(:queue).returns(Rails::Queueing::TestQueue.new)
  427
+    AsyncMailer.delivery_method = :test
  428
+    AsyncMailer.deliveries.clear
  429
+    AsyncMailer.welcome.deliver
  430
+    assert_equal(0, AsyncMailer.deliveries.length)
  431
+    Rails.queue.drain
  432
+    assert_equal(1, AsyncMailer.deliveries.length)
  433
+  end
  434
+
422 435
   test "calling deliver, ActionMailer should yield back to mail to let it call :do_delivery on itself" do
423 436
     mail = Mail::Message.new
424 437
     mail.expects(:do_delivery).once
@@ -434,6 +447,7 @@ def teardown
434 447
   end
435 448
 
436 449
   test "should raise if missing template in implicit render" do
  450
+    BaseMailer.deliveries.clear
437 451
     assert_raises ActionView::MissingTemplate do
438 452
       BaseMailer.implicit_different_template('missing_template').deliver
439 453
     end
1  actionmailer/test/fixtures/async_mailer/welcome.erb
... ...
@@ -0,0 +1 @@
  1
+Welcome
3  actionmailer/test/mailers/async_mailer.rb
... ...
@@ -0,0 +1,3 @@
  1
+class AsyncMailer < BaseMailer
  2
+  self.async = true
  3
+end

0 notes on commit dc7fd82

Please sign in to comment.
Something went wrong with that request. Please try again.