Action Mailer async flag is true by default using a Synchronous impl

This commit is contained in:
Santiago Pastorino 2012-09-10 12:52:19 -07:00
parent b4b5971ff7
commit 34b23e7110
10 changed files with 61 additions and 79 deletions

View File

@ -1,41 +0,0 @@
require 'delegate'
module ActionMailer
module Async
def method_missing(method_name, *args)
if action_methods.include?(method_name.to_s)
QueuedMessage.new(queue, self, method_name, *args)
else
super
end
end
def queue
Rails.queue
end
class QueuedMessage < ::Delegator
attr_reader :queue
def initialize(queue, mailer_class, method_name, *args)
@queue = queue
@mailer_class = mailer_class
@method_name = method_name
@args = args
end
def __getobj__
@actual_message ||= @mailer_class.send(:new, @method_name, *@args).message
end
def run
__getobj__.deliver
end
# Will push the message onto the Queue to be processed
def deliver
@queue << self
end
end
end
end

View File

@ -1,4 +1,5 @@
require 'mail'
require 'action_mailer/queued_message'
require 'action_mailer/collector'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/hash/except'
@ -464,19 +465,6 @@ module ActionMailer #:nodoc:
super || action_methods.include?(method.to_s)
end
# Will force ActionMailer to push new messages to the queue defined
# in the ActionMailer class when set to true.
#
# class WelcomeMailer < ActionMailer::Base
# self.async = true
# end
def async=(truth)
if truth
require 'action_mailer/async'
extend ActionMailer::Async
end
end
protected
def set_payload_for_mail(payload, mail) #:nodoc:
@ -491,10 +479,18 @@ module ActionMailer #:nodoc:
payload[:mail] = mail.encoded
end
def method_missing(method, *args) #:nodoc:
return super unless respond_to?(method)
new(method, *args).message
def method_missing(method_name, *args)
if action_methods.include?(method_name.to_s)
QueuedMessage.new(queue, self, method_name, *args)
else
super
end
end
def queue
Rails.queue
end
end
attr_internal :message

View File

@ -0,0 +1,27 @@
require 'delegate'
module ActionMailer
class QueuedMessage < ::Delegator
attr_reader :queue
def initialize(queue, mailer_class, method_name, *args)
@queue = queue
@mailer_class = mailer_class
@method_name = method_name
@args = args
end
def __getobj__
@actual_message ||= @mailer_class.send(:new, @method_name, *@args).message
end
def run
__getobj__.deliver
end
# Will push the message onto the Queue to be processed
def deliver
@queue << self
end
end
end

View File

@ -11,6 +11,7 @@ end
require 'minitest/autorun'
require 'action_mailer'
require 'action_mailer/test_case'
require 'rails/queueing'
silence_warnings do
# These external dependencies have warnings :/
@ -27,6 +28,14 @@ ActionView::Template.register_template_handler :bak, lambda { |template| "Lame b
FIXTURE_LOAD_PATH = File.expand_path('fixtures', File.dirname(__FILE__))
ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH
class ActionMailer::Base < AbstractController::Base
class << self
def queue
@queue ||= Rails::Queueing::Container.new(Rails::Queueing::SynchronousQueue.new)
end
end
end
class MockSMTP
def self.deliveries
@@deliveries

View File

@ -575,11 +575,11 @@ class BaseTest < ActiveSupport::TestCase
end
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
mail1 = ProcMailer.welcome
mail1 = ProcMailer.welcome['X-Proc-Method']
yesterday = 1.day.ago
Time.stubs(:now).returns(yesterday)
mail2 = ProcMailer.welcome
assert(mail1['X-Proc-Method'].to_s.to_i > mail2['X-Proc-Method'].to_s.to_i)
mail2 = ProcMailer.welcome['X-Proc-Method']
assert(mail1.to_s.to_i > mail2.to_s.to_i)
end
test "we can call other defined methods on the class as needed" do

View File

@ -1,3 +1,2 @@
class AsyncMailer < BaseMailer
self.async = true
end

View File

@ -472,7 +472,6 @@ The following configuration options are best made in one of the environment file
|+delivery_method+|Defines a delivery method. Possible values are <tt>:smtp</tt> (default), <tt>:sendmail</tt>, <tt>:file</tt> and <tt>:test</tt>.|
|+perform_deliveries+|Determines whether deliveries are actually carried out when the +deliver+ method is invoked on the Mail message. By default they are, but this can be turned off to help functional testing.|
|+deliveries+|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful for unit and functional testing.|
|+async+|Setting this flag will turn on asynchronous message sending, message rendering and delivery will be pushed to <tt>Rails.queue</tt> for processing.|
|+default_options+|Allows you to set default values for the <tt>mail</tt> method options (<tt>:from</tt>, <tt>:reply_to</tt>, etc.).|
h4. Example Action Mailer Configuration
@ -535,19 +534,7 @@ In the test we send the email and store the returned object in the +email+ varia
h3. Asynchronous
You can turn on application-wide asynchronous message sending by adding to your <tt>config/application.rb</tt> file:
<ruby>
config.action_mailer.async = true
</ruby>
Alternatively you can turn on async within specific mailers:
<ruby>
class WelcomeMailer < ActionMailer::Base
self.async = true
end
</ruby>
Rails provides a Synchronous Queue by default. If you want to use an Asynchronous one you will need to configure an async Queue provider like Resque. Queue providers are supposed to have a Railtie where they configure it's own async queue.
h4. Custom Queues

View File

@ -41,7 +41,7 @@ module Rails
@exceptions_app = nil
@autoflush_log = true
@log_formatter = ActiveSupport::Logger::SimpleFormatter.new
@queue = Rails::Queueing::Queue
@queue = Rails::Queueing::SynchronousQueue
@queue_consumer = Rails::Queueing::ThreadedConsumer
@eager_load = nil

View File

@ -59,8 +59,5 @@ module <%= app_const_base %>
# Version of your assets, change this if you want to expire all your assets.
config.assets.version = '1.0'
<% end -%>
# Enable app-wide asynchronous ActionMailer.
# config.action_mailer.async = true
end
end

View File

@ -35,6 +35,14 @@ module Rails
class Queue < ::Queue
end
class SynchronousQueue < ::Queue
def push(job)
job.run
end
alias << push
alias enq push
end
# In test mode, the Rails queue is backed by an Array so that assertions
# can be made about its contents. The test queue provides a +jobs+
# method to make assertions about the queue's contents and a +drain+