Skip to content
This repository
Browse code

Get implicit multipart and attachments working together.

  • Loading branch information...
commit 951397b4a2143eee4b900356dab525aed99430ec 1 parent 1cd5592
authored January 22, 2010
50  actionmailer/lib/action_mailer/base.rb
@@ -390,6 +390,7 @@ def set_payload_for_mail(payload, mail) #:nodoc:
390 390
       end
391 391
     end
392 392
 
  393
+    # TODO Add new delivery method goodness
393 394
     def mail(headers = {})
394 395
       # Guard flag to prevent both the old and the new API from firing
395 396
       # Should be removed when old API is deprecated
@@ -402,9 +403,8 @@ def mail(headers = {})
402 403
 
403 404
       # Give preference to headers and fallbacks to the ones set in mail
404 405
       content_type = headers[:content_type] || m.content_type
405  
-      charset      = headers[:charset]      || m.charset
406  
-      mime_version = headers[:mime_version] || m.mime_version
407  
-      body = nil
  406
+      charset      = headers[:charset]      || m.charset      || self.class.default_charset.dup
  407
+      mime_version = headers[:mime_version] || m.mime_version || self.class.default_mime_version.dup
408 408
 
409 409
       m.subject   ||= quote_if_necessary(headers[:subject], charset)          if headers[:subject]
410 410
       m.to        ||= quote_address_if_necessary(headers[:to], charset)       if headers[:to]
@@ -420,35 +420,41 @@ def mail(headers = {})
420 420
         # TODO Ensure that we don't need to pass I18n.locale as detail
421 421
         templates = self.class.template_root.find_all(action_name, {}, mailer_name)
422 422
 
423  
-        if templates.size == 1
  423
+        if templates.size == 1 && !m.has_attachments?
424 424
           content_type ||= templates[0].mime_type.to_s
425 425
           m.body = render_to_body(:_template => templates[0])
  426
+        elsif templates.size > 1 && m.has_attachments? 
  427
+          container = Mail::Part.new
  428
+          container.content_type = "multipart/alternate"
  429
+          templates.each { |t| insert_part(container, t, charset) }
  430
+          m.add_part(container)
426 431
         else
427  
-          content_type ||= "multipart/alternate"
428  
-
429  
-          templates.each do |template|
430  
-            part = Mail::Part.new
431  
-            part.content_type = template.mime_type.to_s
432  
-            part.charset = charset
433  
-            part.body = render_to_body(:_template => template)
434  
-            m.add_part(part)
435  
-          end
  432
+          templates.each { |t| insert_part(m, t, charset) }
436 433
         end
  434
+
  435
+        content_type ||= (m.has_attachments? ? "multipart/mixed" : "multipart/alternate")
437 436
       end
438  
-                       
  437
+
  438
+      # Check if the content_type was not overwriten along the way and if so,
  439
+      # fallback to default.
439 440
       m.content_type = content_type || self.class.default_content_type.dup
440  
-      m.charset      = charset      || self.class.default_charset.dup
441  
-      m.mime_version = mime_version || self.class.default_mime_version.dup
442  
-                       
443  
-      # TODO Add me and test me
444  
-      # m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order.dup)
445  
-      # m.body.sort_parts!
  441
+      m.charset      = charset
  442
+      m.mime_version = mime_version
  443
+
  444
+      unless m.parts.empty?
  445
+        m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order.dup)
  446
+        m.body.sort_parts!
  447
+      end
446 448
 
447 449
       m
448 450
     end
449 451
 
450  
-    def fill_in_part(part, template, charset)
451  
-      
  452
+    def insert_part(container, template, charset)
  453
+      part = Mail::Part.new
  454
+      part.content_type = template.mime_type.to_s
  455
+      part.charset = charset
  456
+      part.body = render_to_body(:_template => template)
  457
+      container.add_part(part)
452 458
     end
453 459
 
454 460
     # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
2  actionmailer/lib/action_mailer/delivery_methods.rb
@@ -27,6 +27,7 @@ module DeliveryMethods
27 27
     end
28 28
 
29 29
     module ClassMethods
  30
+      # TODO Make me class inheritable
30 31
       def delivery_settings
31 32
         @@delivery_settings ||= Hash.new { |h,k| h[k] = {} }
32 33
       end
@@ -51,6 +52,7 @@ def respond_to?(method_symbol, include_private = false) #:nodoc:
51 52
 
52 53
     protected
53 54
 
  55
+      # TODO Get rid of this method missing magic
54 56
       def method_missing(method_symbol, *parameters) #:nodoc:
55 57
         if match = matches_settings_method?(method_symbol)
56 58
           if match[2]
96  actionmailer/test/base_test.rb
@@ -3,13 +3,13 @@
3 3
 
4 4
 #  class Notifier < ActionMailer::Base
5 5
 #    delivers_from 'notifications@example.com'
6  
-#    
  6
+#
7 7
 #    def welcome(user)
8 8
 #      @user = user # available to the view
9 9
 #      mail(:subject => 'Welcome!', :to => user.email_address)
10 10
 #      # auto renders both welcome.text.erb and welcome.html.erb
11 11
 #    end
12  
-#    
  12
+#
13 13
 #    def goodbye(user)
14 14
 #      headers["X-SPAM"] = 'Not-SPAM'
15 15
 #      mail(:subject => 'Goodbye', :to => user.email_address) do |format|
@@ -17,7 +17,7 @@
17 17
 #        format.text # goodbye.text.erb
18 18
 #      end
19 19
 #    end
20  
-#    
  20
+#
21 21
 #    def surprise(user, gift)
22 22
 #      attachments[gift.name] = File.read(gift.path)
23 23
 #      mail(:subject => 'Surprise!', :to => user.email_address) do |format|
@@ -25,17 +25,17 @@
25 25
 #        format.text(:transfer_encoding => "base64") # surprise.text.erb
26 26
 #      end
27 27
 #    end
28  
-#    
  28
+#
29 29
 #    def special_surprise(user, gift)
30 30
 #      attachments[gift.name] = { :content_type => "application/x-gzip", :content => File.read(gift.path) }
31 31
 #      mail(:to => 'special@example.com') # subject not required
32 32
 #      # auto renders both special_surprise.text.erb and special_surprise.html.erb
33 33
 #    end
34 34
 #  end
35  
-#   
  35
+#
36 36
 #  mail = Notifier.welcome(user)         # => returns a Mail object
37 37
 #  mail.deliver
38  
-# 
  38
+#
39 39
 #  Notifier.welcome(user).deliver # => creates and sends the Mail in one step
40 40
 class BaseTest < ActiveSupport::TestCase
41 41
   DEFAULT_HEADERS = {
@@ -44,6 +44,8 @@ class BaseTest < ActiveSupport::TestCase
44 44
     :subject => 'The first email on new API!'
45 45
   }
46 46
 
  47
+  # TODO Think on the simple case where I want to send an e-mail
  48
+  # with attachment and small text (without need to add a template).
47 49
   class BaseMailer < ActionMailer::Base
48 50
     self.mailer_name = "base_mailer"
49 51
 
@@ -63,8 +65,9 @@ def attachment_with_hash
63 65
       mail(DEFAULT_HEADERS)
64 66
     end
65 67
 
66  
-    def implicit_multipart
67  
-      mail(DEFAULT_HEADERS)
  68
+    def implicit_multipart(hash = {})
  69
+      attachments['invoice.pdf'] = 'This is test File content' if hash.delete(:attachments)
  70
+      mail(DEFAULT_HEADERS.merge(hash))
68 71
     end
69 72
   end
70 73
 
@@ -72,6 +75,7 @@ def implicit_multipart
72 75
     assert_nothing_raised { BaseMailer.deliver_welcome }
73 76
   end
74 77
 
  78
+  # Basic mail usage without block
75 79
   test "mail() should set the headers of the mail message" do
76 80
     email = BaseMailer.deliver_welcome
77 81
     assert_equal(email.to,      ['mikel@test.lindsaar.net'])
@@ -102,11 +106,13 @@ def implicit_multipart
102 106
     assert_equal("Welcome", email.body.encoded)
103 107
   end
104 108
 
  109
+  # Custom headers
105 110
   test "custom headers" do
106 111
     email = BaseMailer.deliver_welcome
107 112
     assert_equal("Not SPAM", email['X-SPAM'].decoded)
108 113
   end
109 114
 
  115
+  # Attachments
110 116
   test "attachment with content" do
111 117
     email = BaseMailer.deliver_attachment_with_content
112 118
     assert_equal(1, email.attachments.length)
@@ -126,12 +132,22 @@ def implicit_multipart
126 132
     assert_equal("\312\213\254\232)b", email.attachments['invoice.jpg'].decoded)
127 133
   end
128 134
 
129  
-  # test "mail sets proper content type when attachment is included" do
130  
-  #   email = BaseMailer.deliver_attachment_with_content
131  
-  #   assert_equal(1, email.attachments.length)
132  
-  #   assert_equal("multipart/mixed", email.content_type)
133  
-  # end
  135
+  test "sets mime type to multipart/mixed when attachment is included" do
  136
+    email = BaseMailer.deliver_attachment_with_content
  137
+    assert_equal(1, email.attachments.length)
  138
+    assert_equal("multipart/mixed", email.mime_type)
  139
+  end
134 140
 
  141
+  test "adds the rendered template as part" do
  142
+    email = BaseMailer.deliver_attachment_with_content
  143
+    assert_equal(2, email.parts.length)
  144
+    assert_equal("text/html", email.parts[0].mime_type)
  145
+    assert_equal("Attachment with content", email.parts[0].body.encoded)
  146
+    assert_equal("application/pdf", email.parts[1].mime_type)
  147
+    assert_equal("VGhpcyBpcyB0ZXN0IEZpbGUgY29udGVudA==\r\n", email.parts[1].body.encoded)
  148
+  end
  149
+
  150
+  # Defaults values
135 151
   test "uses default charset from class" do
136 152
     swap BaseMailer, :default_charset => "US-ASCII" do
137 153
       email = BaseMailer.deliver_welcome
@@ -142,6 +158,16 @@ def implicit_multipart
142 158
     end
143 159
   end
144 160
 
  161
+  test "uses default content type from class" do
  162
+    swap BaseMailer, :default_content_type => "text/html" do
  163
+      email = BaseMailer.deliver_welcome
  164
+      assert_equal("text/html", email.mime_type)
  165
+
  166
+      email = BaseMailer.deliver_welcome(:content_type => "text/plain")
  167
+      assert_equal("text/plain", email.mime_type)
  168
+    end
  169
+  end
  170
+
145 171
   test "uses default mime version from class" do
146 172
     swap BaseMailer, :default_mime_version => "2.0" do
147 173
       email = BaseMailer.deliver_welcome
@@ -161,18 +187,52 @@ def implicit_multipart
161 187
     assert_equal "New Subject!", email.subject
162 188
   end
163 189
 
  190
+  # Implicit multipart
164 191
   test "implicit multipart tests" do
165  
-    require 'ruby-debug'
166  
-    $BREAK = true
167 192
     email = BaseMailer.deliver_implicit_multipart
168  
-
169 193
     assert_equal(2, email.parts.size)
170  
-
171 194
     assert_equal("multipart/alternate", email.mime_type)
172 195
     assert_equal("text/plain", email.parts[0].mime_type)
173  
-    assert_equal("text/html",  email.parts[1].mime_type)    
  196
+    assert_equal("TEXT Implicit Multipart", email.parts[0].body.encoded)
  197
+    assert_equal("text/html", email.parts[1].mime_type)
  198
+    assert_equal("HTML Implicit Multipart", email.parts[1].body.encoded)
  199
+  end
  200
+
  201
+  test "implicit multipart tests with sort order" do
  202
+    order = ["text/html", "text/plain"]
  203
+    swap BaseMailer, :default_implicit_parts_order => order do
  204
+      email = BaseMailer.deliver_implicit_multipart
  205
+      assert_equal("text/html",  email.parts[0].mime_type)
  206
+      assert_equal("text/plain", email.parts[1].mime_type)
  207
+
  208
+      email = BaseMailer.deliver_implicit_multipart(:parts_order => order.reverse)
  209
+      assert_equal("text/plain", email.parts[0].mime_type)
  210
+      assert_equal("text/html",  email.parts[1].mime_type)
  211
+    end
  212
+  end
  213
+
  214
+  test "implicit multipart with attachments creates nested parts" do
  215
+    email = BaseMailer.deliver_implicit_multipart(:attachments => true)
  216
+    assert_equal("application/pdf", email.parts[0].mime_type)
  217
+    assert_equal("multipart/alternate", email.parts[1].mime_type)
  218
+    assert_equal("text/plain", email.parts[1].parts[0].mime_type)
  219
+    assert_equal("TEXT Implicit Multipart", email.parts[1].parts[0].body.encoded)
  220
+    assert_equal("text/html", email.parts[1].parts[1].mime_type)
  221
+    assert_equal("HTML Implicit Multipart", email.parts[1].parts[1].body.encoded)
174 222
   end
175 223
 
  224
+  # TODO This should be fixed in mail. Sort parts should be recursive.
  225
+  # test "implicit multipart with attachments and sort order" do
  226
+  #   order = ["text/html", "text/plain"]
  227
+  #   swap BaseMailer, :default_implicit_parts_order => order do
  228
+  #     email = BaseMailer.deliver_implicit_multipart(:attachments => true)
  229
+  #     assert_equal("application/pdf", email.parts[0].mime_type)
  230
+  #     assert_equal("multipart/alternate", email.parts[1].mime_type)
  231
+  #     assert_equal("text/plain", email.parts[1].parts[1].mime_type)
  232
+  #     assert_equal("text/html", email.parts[1].parts[0].mime_type)
  233
+  #   end
  234
+  # end
  235
+
176 236
   protected
177 237
 
178 238
     # Execute the block setting the given values and restoring old values after
1  actionmailer/test/fixtures/base_mailer/attachment_with_content.erb
... ...
@@ -0,0 +1 @@
  1
+Attachment with content

0 notes on commit 951397b

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