Skip to content
This repository
Browse code

abstract all of the ActionMailer delivery methods into their own clas…

…ses. thereby the following are equivalent

  ActionMailer::Base.delivery_method = :smtp
  ActionMailer::Base.delivery_method = ActionMailer::DeliveryMethod::Smtp

we could equally set our own custom object
as long as it provides the instance method :perform_delivery(mail)

eg.

  class MySmsDeliveryMethod
    def perform_delivery(mail)
      Sms.send(mail['to'], mail['body'])
    end
  end

  MySmsMailer.delivery_method = MySmsDeliveryMethod.new

Signed-off-by: José Valim <jose.valim@gmail.com>
  • Loading branch information...
commit f4f76772fb5c25357a54baaa9cd20f7e9a1cd653 1 parent 3f56038
Matthew Rudy Jacobs authored October 28, 2009 wycats committed November 01, 2009
1  actionmailer/Rakefile
@@ -44,6 +44,7 @@ Rake::RDocTask.new { |rdoc|
44 44
   rdoc.rdoc_files.include('README', 'CHANGELOG')
45 45
   rdoc.rdoc_files.include('lib/action_mailer.rb')
46 46
   rdoc.rdoc_files.include('lib/action_mailer/*.rb')
  47
+  rdoc.rdoc_files.include('lib/action_mailer/delivery_method/*.rb')
47 48
 }
48 49
 
49 50
 spec = eval(File.read('actionmailer.gemspec'))
1  actionmailer/lib/action_mailer.rb
@@ -33,6 +33,7 @@ def self.load_all!
33 33
 
34 34
   autoload :AdvAttrAccessor, 'action_mailer/adv_attr_accessor'
35 35
   autoload :Base, 'action_mailer/base'
  36
+  autoload :DeliveryMethod, 'action_mailer/delivery_method'
36 37
   autoload :Helpers, 'action_mailer/helpers'
37 38
   autoload :Part, 'action_mailer/part'
38 39
   autoload :PartContainer, 'action_mailer/part_container'
75  actionmailer/lib/action_mailer/base.rb
... ...
@@ -1,5 +1,3 @@
1  
-require 'tmpdir'
2  
-
3 1
 require "active_support/core_ext/class"
4 2
 # Use the old layouts until actionmailer gets refactored
5 3
 require "action_controller/legacy/layout"
@@ -232,7 +230,7 @@ module ActionMailer #:nodoc:
232 230
   # * <tt>raise_delivery_errors</tt> - Whether or not errors should be raised if the email fails to be delivered.
233 231
   #
234 232
   # * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default), <tt>:sendmail</tt>, <tt>:test</tt>,
235  
-  #   and <tt>:file</tt>.
  233
+  #   and <tt>:file</tt>. Or you may provide a custom delivery method object eg. MyOwnDeliveryMethodClass.new
236 234
   #
237 235
   # * <tt>perform_deliveries</tt> - Determines whether <tt>deliver_*</tt> methods are actually carried out. By default they are,
238 236
   #   but this can be turned off to help functional testing.
@@ -270,35 +268,21 @@ class Base
270 268
 
271 269
     cattr_accessor :logger
272 270
 
273  
-    @@smtp_settings = {
274  
-      :address              => "localhost",
275  
-      :port                 => 25,
276  
-      :domain               => 'localhost.localdomain',
277  
-      :user_name            => nil,
278  
-      :password             => nil,
279  
-      :authentication       => nil,
280  
-      :enable_starttls_auto => true,
281  
-    }
282  
-    cattr_accessor :smtp_settings
283  
-
284  
-    @@sendmail_settings = {
285  
-      :location       => '/usr/sbin/sendmail',
286  
-      :arguments      => '-i -t'
287  
-    }
288  
-    cattr_accessor :sendmail_settings
289  
-
290  
-    @@file_settings = {
291  
-      :location       => defined?(Rails) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
292  
-    }
293  
-
294  
-    cattr_accessor :file_settings
  271
+    class << self
  272
+      delegate :settings, :settings=, :to => ActionMailer::DeliveryMethod::File, :prefix => :file
  273
+      delegate :settings, :settings=, :to => ActionMailer::DeliveryMethod::Sendmail, :prefix => :sendmail
  274
+      delegate :settings, :settings=, :to => ActionMailer::DeliveryMethod::Smtp, :prefix => :smtp
  275
+
  276
+      def delivery_method=(method_name)
  277
+        @delivery_method = ActionMailer::DeliveryMethod.lookup_method(method_name)
  278
+      end
  279
+    end
  280
+    self.delivery_method = :smtp
  281
+    superclass_delegating_reader :delivery_method
295 282
 
296 283
     @@raise_delivery_errors = true
297 284
     cattr_accessor :raise_delivery_errors
298 285
 
299  
-    superclass_delegating_accessor :delivery_method
300  
-    self.delivery_method = :smtp
301  
-
302 286
     @@perform_deliveries = true
303 287
     cattr_accessor :perform_deliveries
304 288
 
@@ -552,7 +536,7 @@ def deliver!(mail = @mail)
552 536
 
553 537
       ActiveSupport::Notifications.instrument(:deliver_mail, :mail => @mail) do
554 538
         begin
555  
-          __send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries
  539
+          self.delivery_method.perform_delivery(mail) if perform_deliveries
556 540
         rescue Exception => e # Net::SMTP errors or sendmail pipe errors
557 541
           raise e if raise_delivery_errors
558 542
         end
@@ -720,39 +704,6 @@ def create_mail
720 704
         @mail = m
721 705
       end
722 706
 
723  
-      def perform_delivery_smtp(mail)
724  
-        destinations = mail.destinations
725  
-        mail.ready_to_send
726  
-        sender = (mail['return-path'] && mail['return-path'].spec) || mail['from']
727  
-
728  
-        smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port])
729  
-        smtp.enable_starttls_auto if smtp_settings[:enable_starttls_auto] && smtp.respond_to?(:enable_starttls_auto)
730  
-        smtp.start(smtp_settings[:domain], smtp_settings[:user_name], smtp_settings[:password],
731  
-                   smtp_settings[:authentication]) do |smtp|
732  
-          smtp.sendmail(mail.encoded, sender, destinations)
733  
-        end
734  
-      end
735  
-
736  
-      def perform_delivery_sendmail(mail)
737  
-        sendmail_args = sendmail_settings[:arguments]
738  
-        sendmail_args += " -f \"#{mail['return-path']}\"" if mail['return-path']
739  
-        IO.popen("#{sendmail_settings[:location]} #{sendmail_args}","w+") do |sm|
740  
-          sm.print(mail.encoded.gsub(/\r/, ''))
741  
-          sm.flush
742  
-        end
743  
-      end
744  
-
745  
-      def perform_delivery_test(mail)
746  
-        deliveries << mail
747  
-      end
748  
-
749  
-      def perform_delivery_file(mail)
750  
-        FileUtils.mkdir_p file_settings[:location]
751  
-
752  
-        (mail.to + mail.cc + mail.bcc).uniq.each do |to|
753  
-          File.open(File.join(file_settings[:location], to), 'a') { |f| f.write(mail) }
754  
-        end
755  
-      end
756 707
   end
757 708
 
758 709
   Base.class_eval do
58  actionmailer/lib/action_mailer/delivery_method.rb
... ...
@@ -0,0 +1,58 @@
  1
+require "active_support/core_ext/class"
  2
+module ActionMailer
  3
+  module DeliveryMethod
  4
+
  5
+    autoload :File, 'action_mailer/delivery_method/file'
  6
+    autoload :Sendmail, 'action_mailer/delivery_method/sendmail'
  7
+    autoload :Smtp, 'action_mailer/delivery_method/smtp'
  8
+    autoload :Test, 'action_mailer/delivery_method/test'
  9
+
  10
+    # Creates a new DeliveryMethod object according to the given options.
  11
+    #
  12
+    # If no arguments are passed to this method, then a new
  13
+    # ActionMailer::DeliveryMethod::Stmp object will be returned.
  14
+    #
  15
+    # If you pass a Symbol as the first argument, then a corresponding
  16
+    # delivery method class under the ActionMailer::DeliveryMethod namespace
  17
+    # will be created.
  18
+    # For example:
  19
+    #
  20
+    #   ActionMailer::DeliveryMethod.lookup_method(:sendmail)
  21
+    #   # => returns a new ActionMailer::DeliveryMethod::Sendmail object
  22
+    #
  23
+    # If the first argument is not a Symbol, then it will simply be returned:
  24
+    #
  25
+    #   ActionMailer::DeliveryMethod.lookup_method(MyOwnDeliveryMethod.new)
  26
+    #   # => returns MyOwnDeliveryMethod.new
  27
+    def self.lookup_method(delivery_method)
  28
+      case delivery_method
  29
+      when Symbol
  30
+        method_name = delivery_method.to_s.camelize
  31
+        method_class = ActionMailer::DeliveryMethod.const_get(method_name)
  32
+        method_class.new()
  33
+      when nil
  34
+        Smtp.new
  35
+      else
  36
+        delivery_method
  37
+      end
  38
+    end
  39
+
  40
+    # An abstract delivery method class. There are multiple delivery method
  41
+    # classes, documented under
  42
+    # See the classes under the ActionMailer::DeliveryMethod, e.g.
  43
+    # ActionMailer::DeliveryMethod::Smtp.
  44
+    # Smtp is the default delivery method for production
  45
+    # while Test is used in testing.
  46
+    #
  47
+    # each delivery method exposes just one method
  48
+    #
  49
+    #   delivery_method = ActionMailer::DeliveryMethod::Smtp.new
  50
+    #
  51
+    #   delivery_method.perform_delivery(mail) # send the mail via smtp
  52
+    class Method
  53
+      superclass_delegating_accessor :settings
  54
+      self.settings = {}
  55
+    end
  56
+    
  57
+  end
  58
+end
21  actionmailer/lib/action_mailer/delivery_method/file.rb
... ...
@@ -0,0 +1,21 @@
  1
+require 'tmpdir'
  2
+module ActionMailer
  3
+  module DeliveryMethod
  4
+
  5
+    # A delivery method implementation which writes all mails to a file.
  6
+    class File < Method
  7
+
  8
+      self.settings = {
  9
+        :location       => defined?(Rails) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
  10
+      }
  11
+
  12
+      def perform_delivery(mail)
  13
+        FileUtils.mkdir_p settings[:location]
  14
+
  15
+        (mail.to + mail.cc + mail.bcc).uniq.each do |to|
  16
+          ::File.open(::File.join(settings[:location], to), 'a') { |f| f.write(mail) }
  17
+        end
  18
+      end
  19
+    end
  20
+  end
  21
+end
22  actionmailer/lib/action_mailer/delivery_method/sendmail.rb
... ...
@@ -0,0 +1,22 @@
  1
+module ActionMailer
  2
+  module DeliveryMethod
  3
+
  4
+    # A delivery method implementation which sends via sendmail.
  5
+    class Sendmail < Method
  6
+
  7
+      self.settings = {
  8
+        :location       => '/usr/sbin/sendmail',
  9
+        :arguments      => '-i -t'
  10
+      }
  11
+
  12
+      def perform_delivery(mail)
  13
+        sendmail_args = settings[:arguments]
  14
+        sendmail_args += " -f \"#{mail['return-path']}\"" if mail['return-path']
  15
+        IO.popen("#{settings[:location]} #{sendmail_args}","w+") do |sm|
  16
+          sm.print(mail.encoded.gsub(/\r/, ''))
  17
+          sm.flush
  18
+        end
  19
+      end
  20
+    end
  21
+  end
  22
+end
31  actionmailer/lib/action_mailer/delivery_method/smtp.rb
... ...
@@ -0,0 +1,31 @@
  1
+module ActionMailer
  2
+  module DeliveryMethod
  3
+    
  4
+    # A delivery method implementation which sends via smtp.
  5
+    class Smtp < Method
  6
+
  7
+      self.settings = {
  8
+        :address              => "localhost",
  9
+        :port                 => 25,
  10
+        :domain               => 'localhost.localdomain',
  11
+        :user_name            => nil,
  12
+        :password             => nil,
  13
+        :authentication       => nil,
  14
+        :enable_starttls_auto => true,
  15
+      }
  16
+
  17
+      def perform_delivery(mail)
  18
+        destinations = mail.destinations
  19
+        mail.ready_to_send
  20
+        sender = (mail['return-path'] && mail['return-path'].spec) || mail['from']
  21
+
  22
+        smtp = Net::SMTP.new(settings[:address], settings[:port])
  23
+        smtp.enable_starttls_auto if settings[:enable_starttls_auto] && smtp.respond_to?(:enable_starttls_auto)
  24
+        smtp.start(settings[:domain], settings[:user_name], settings[:password],
  25
+                   settings[:authentication]) do |smtp|
  26
+          smtp.sendmail(mail.encoded, sender, destinations)
  27
+        end
  28
+      end
  29
+    end
  30
+  end
  31
+end
12  actionmailer/lib/action_mailer/delivery_method/test.rb
... ...
@@ -0,0 +1,12 @@
  1
+module ActionMailer
  2
+  module DeliveryMethod
  3
+
  4
+    # A delivery method implementation designed for testing, which just appends each record to the :deliveries array
  5
+    class Test < Method
  6
+
  7
+      def perform_delivery(mail)
  8
+        ActionMailer::Base.deliveries << mail
  9
+      end
  10
+    end
  11
+  end
  12
+end
36  actionmailer/test/delivery_method_test.rb
@@ -11,6 +11,21 @@ class FileDeliveryMethodMailer < ActionMailer::Base
11 11
   self.delivery_method = :file
12 12
 end
13 13
 
  14
+class CustomDeliveryMethod
  15
+  attr_accessor :custom_deliveries
  16
+  def initialize()
  17
+    @customer_deliveries = []
  18
+  end
  19
+
  20
+  def self.perform_delivery(mail)
  21
+    self.custom_deliveries << mail
  22
+  end
  23
+end
  24
+
  25
+class CustomerDeliveryMailer < ActionMailer::Base
  26
+  self.delivery_method = CustomDeliveryMethod.new
  27
+end
  28
+
14 29
 class ActionMailerBase_delivery_method_Test < Test::Unit::TestCase
15 30
   def setup
16 31
     set_delivery_method :smtp
@@ -21,7 +36,7 @@ def teardown
21 36
   end
22 37
 
23 38
   def test_should_be_the_default_smtp
24  
-    assert_equal :smtp, ActionMailer::Base.delivery_method
  39
+    assert_instance_of ActionMailer::DeliveryMethod::Smtp, ActionMailer::Base.delivery_method
25 40
   end
26 41
 end
27 42
 
@@ -35,7 +50,7 @@ def teardown
35 50
   end
36 51
   
37 52
   def test_should_be_the_default_smtp
38  
-    assert_equal :smtp, DefaultDeliveryMethodMailer.delivery_method
  53
+    assert_instance_of ActionMailer::DeliveryMethod::Smtp, DefaultDeliveryMethodMailer.delivery_method
39 54
   end
40 55
 end
41 56
 
@@ -49,7 +64,7 @@ def teardown
49 64
   end
50 65
 
51 66
   def test_should_be_the_set_delivery_method
52  
-    assert_equal :sendmail, NonDefaultDeliveryMethodMailer.delivery_method
  67
+    assert_instance_of ActionMailer::DeliveryMethod::Sendmail, NonDefaultDeliveryMethodMailer.delivery_method
53 68
   end
54 69
 end
55 70
 
@@ -63,7 +78,7 @@ def teardown
63 78
   end
64 79
 
65 80
   def test_should_be_the_set_delivery_method
66  
-    assert_equal :file, FileDeliveryMethodMailer.delivery_method
  81
+    assert_instance_of ActionMailer::DeliveryMethod::File, FileDeliveryMethodMailer.delivery_method
67 82
   end
68 83
 
69 84
   def test_should_default_location_to_the_tmpdir
@@ -71,3 +86,16 @@ def test_should_default_location_to_the_tmpdir
71 86
   end
72 87
 end
73 88
 
  89
+class CustomDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase
  90
+  def setup
  91
+    set_delivery_method :smtp
  92
+  end
  93
+
  94
+  def teardown
  95
+    restore_delivery_method
  96
+  end
  97
+
  98
+  def test_should_be_the_set_delivery_method
  99
+    assert_instance_of CustomDeliveryMethod, CustomerDeliveryMailer.delivery_method
  100
+  end
  101
+end
2  actionmailer/test/mail_service_test.rb
@@ -554,7 +554,7 @@ def test_perform_deliveries_flag
554 554
 
555 555
   def test_doesnt_raise_errors_when_raise_delivery_errors_is_false
556 556
     ActionMailer::Base.raise_delivery_errors = false
557  
-    TestMailer.any_instance.expects(:perform_delivery_test).raises(Exception)
  557
+    TestMailer.delivery_method.expects(:perform_delivery).raises(Exception)
558 558
     assert_nothing_raised { TestMailer.deliver_signed_up(@recipient) }
559 559
   end
560 560
 
2  actionmailer/test/test_helper_test.rb
@@ -10,7 +10,7 @@ def test
10 10
 
11 11
 class TestHelperMailerTest < ActionMailer::TestCase
12 12
   def test_setup_sets_right_action_mailer_options
13  
-    assert_equal :test, ActionMailer::Base.delivery_method
  13
+    assert_instance_of ActionMailer::DeliveryMethod::Test, ActionMailer::Base.delivery_method
14 14
     assert ActionMailer::Base.perform_deliveries
15 15
     assert_equal [], ActionMailer::Base.deliveries
16 16
   end

0 notes on commit f4f7677

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