From dc7fd821dca0b2088bde7e0a2a06bfe59e1ba5c6 Mon Sep 17 00:00:00 2001 From: Brian Cardarella Date: Sat, 23 Jun 2012 14:36:09 -0400 Subject: 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. --- actionmailer/lib/action_mailer/async.rb | 41 ++++++++++++++++++++++ actionmailer/lib/action_mailer/base.rb | 7 ++++ actionmailer/test/base_test.rb | 14 ++++++++ .../test/fixtures/async_mailer/welcome.erb | 1 + actionmailer/test/mailers/async_mailer.rb | 3 ++ 5 files changed, 66 insertions(+) create mode 100644 actionmailer/lib/action_mailer/async.rb create mode 100644 actionmailer/test/fixtures/async_mailer/welcome.erb create mode 100644 actionmailer/test/mailers/async_mailer.rb diff --git a/actionmailer/lib/action_mailer/async.rb b/actionmailer/lib/action_mailer/async.rb new file mode 100644 index 0000000000..6f768ac286 --- /dev/null +++ b/actionmailer/lib/action_mailer/async.rb @@ -0,0 +1,41 @@ +module ActionMailer::Async + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def method_missing(method_name, *args) + if action_methods.include?(method_name.to_s) + QueuedMessage.new(self, method_name, *args) + else + super + end + end + end + + class QueuedMessage + delegate :to_s, :to => :actual_message + + def initialize(mailer_class, method_name, *args) + @mailer_class = mailer_class + @method_name = method_name + *@args = *args + end + + def run + actual_message.deliver + end + + def deliver + Rails.queue << self + end + + def actual_message + @actual_message ||= @mailer_class.send(:new, @method_name, *@args).message + end + + def method_missing(method_name, *args) + actual_message.send(method_name, *args) + end + end +end diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 739f9a52a9..f40b85549e 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -456,6 +456,13 @@ module ActionMailer #:nodoc: super || action_methods.include?(method.to_s) end + def async=(truth) + if truth + require 'action_mailer/async' + include ActionMailer::Async + end + end + protected def set_payload_for_mail(payload, mail) #:nodoc: diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 1b2e39b3f7..fdc784029b 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -7,6 +7,9 @@ require 'active_support/time' require 'mailers/base_mailer' require 'mailers/proc_mailer' require 'mailers/asset_mailer' +require 'mailers/async_mailer' + +require 'rails/queueing' class BaseTest < ActiveSupport::TestCase def teardown @@ -419,6 +422,16 @@ class BaseTest < ActiveSupport::TestCase assert_equal(1, BaseMailer.deliveries.length) end + test "delivering message asynchronously" do + Rails.stubs(:queue).returns(Rails::Queueing::TestQueue.new) + AsyncMailer.delivery_method = :test + AsyncMailer.deliveries.clear + AsyncMailer.welcome.deliver + assert_equal(0, AsyncMailer.deliveries.length) + Rails.queue.drain + assert_equal(1, AsyncMailer.deliveries.length) + end + test "calling deliver, ActionMailer should yield back to mail to let it call :do_delivery on itself" do mail = Mail::Message.new mail.expects(:do_delivery).once @@ -434,6 +447,7 @@ class BaseTest < ActiveSupport::TestCase end test "should raise if missing template in implicit render" do + BaseMailer.deliveries.clear assert_raises ActionView::MissingTemplate do BaseMailer.implicit_different_template('missing_template').deliver end diff --git a/actionmailer/test/fixtures/async_mailer/welcome.erb b/actionmailer/test/fixtures/async_mailer/welcome.erb new file mode 100644 index 0000000000..01f3f00c63 --- /dev/null +++ b/actionmailer/test/fixtures/async_mailer/welcome.erb @@ -0,0 +1 @@ +Welcome \ No newline at end of file diff --git a/actionmailer/test/mailers/async_mailer.rb b/actionmailer/test/mailers/async_mailer.rb new file mode 100644 index 0000000000..ce601e7343 --- /dev/null +++ b/actionmailer/test/mailers/async_mailer.rb @@ -0,0 +1,3 @@ +class AsyncMailer < BaseMailer + self.async = true +end -- cgit v1.2.3