From be35a1510d065fc8575524e1b6b9f2bebd3e138c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@gmail.com>
Date: Fri, 19 Feb 2010 10:51:17 +0100
Subject: Allow to choose the template path and template name used in implicit
 rendering with ActionMailer.

---
 actionmailer/lib/action_mailer/base.rb | 70 ++++++++++++++++++++++------------
 actionmailer/test/base_test.rb         | 57 +++++++++++++++++----------
 2 files changed, 81 insertions(+), 46 deletions(-)

diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 4e89c1ea0c..ce13111850 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -452,10 +452,27 @@ module ActionMailer #:nodoc:
     # field for the 'envelope from' value.
     #
     # If you do not pass a block to the +mail+ method, it will find all templates in the 
-    # template path that match the method name that it is being called from, it will then
-    # create parts for each of these templates intelligently, making educated guesses
-    # on correct content type and sequence, and return a fully prepared Mail::Message
-    # ready to call <tt>:deliver</tt> on to send.
+    # view paths using by default the mailer name and the method name that it is being
+    # called from, it will then create parts for each of these templates intelligently,
+    # making educated guesses on correct content type and sequence, and return a fully
+    # prepared Mail::Message ready to call <tt>:deliver</tt> on to send.
+    #
+    # For example:
+    #
+    #   class Notifier < ActionMailer::Base
+    #     default :from => 'no-reply@test.lindsaar.net',
+    #
+    #     def welcome
+    #       mail(:to => 'mikel@test.lindsaar.net')
+    #     end
+    #   end
+    #
+    # Will look for all templates at "app/views/notifier" with name "welcome". However, those
+    # can be customized:
+    #
+    #   mail(:template_path => 'notifications', :template_name => 'another')
+    #
+    # And now it will look for all templates at "app/views/notifications" with name "another".
     #
     # If you do pass a block, you can render specific templates of your choice:
     # 
@@ -493,7 +510,7 @@ module ActionMailer #:nodoc:
 
       # Merge defaults from class
       headers = headers.reverse_merge(self.class.default)
-      charset = headers[:charset]
+      charset = headers.delete(:charset)
 
       # Quote fields
       headers[:subject] ||= default_i18n_subject
@@ -514,13 +531,11 @@ module ActionMailer #:nodoc:
       end
 
       # Set configure delivery behavior
-      wrap_delivery_behavior!(headers[:delivery_method])
+      wrap_delivery_behavior!(headers.delete(:delivery_method))
 
-      # Remove headers already treated and assign all others
-      headers.except!(:subject, :to, :from, :cc, :bcc, :reply_to)
-      headers.except!(:body, :parts_order, :content_type, :charset, :delivery_method)
+      # Remove any missing configuration header and assign all others
+      headers.except!(:parts_order, :content_type)
       headers.each { |k, v| m[k] = v }
-
       m
     end
 
@@ -548,12 +563,12 @@ module ActionMailer #:nodoc:
     # TODO: Move this into Mail
     def quote_fields!(headers, charset) #:nodoc:
       m = @_message
-      m.subject  ||= quote_if_necessary(headers[:subject], charset)          if headers[:subject]
-      m.to       ||= quote_address_if_necessary(headers[:to], charset)       if headers[:to]
-      m.from     ||= quote_address_if_necessary(headers[:from], charset)     if headers[:from]
-      m.cc       ||= quote_address_if_necessary(headers[:cc], charset)       if headers[:cc]
-      m.bcc      ||= quote_address_if_necessary(headers[:bcc], charset)      if headers[:bcc]
-      m.reply_to ||= quote_address_if_necessary(headers[:reply_to], charset) if headers[:reply_to]
+      m.subject  ||= quote_if_necessary(headers.delete(:subject), charset)          if headers[:subject]
+      m.to       ||= quote_address_if_necessary(headers.delete(:to), charset)       if headers[:to]
+      m.from     ||= quote_address_if_necessary(headers.delete(:from), charset)     if headers[:from]
+      m.cc       ||= quote_address_if_necessary(headers.delete(:cc), charset)       if headers[:cc]
+      m.bcc      ||= quote_address_if_necessary(headers.delete(:bcc), charset)      if headers[:bcc]
+      m.reply_to ||= quote_address_if_necessary(headers.delete(:reply_to), charset) if headers[:reply_to]
     end
 
     def collect_responses_and_parts_order(headers) #:nodoc:
@@ -566,11 +581,14 @@ module ActionMailer #:nodoc:
         responses  = collector.responses
       elsif headers[:body]
         responses << {
-          :body => headers[:body],
+          :body => headers.delete(:body),
           :content_type => self.class.default[:content_type] || "text/plain"
         }
       else
-        each_template do |template|
+        templates_path = headers.delete(:template_path) || self.class.mailer_name
+        templates_name = headers.delete(:template_name) || action_name
+
+        each_template(templates_path, templates_name) do |template|
           responses << {
             :body => render_to_body(:_template => template),
             :content_type => template.mime_type.to_s
@@ -581,14 +599,16 @@ module ActionMailer #:nodoc:
       [responses, parts_order]
     end
 
-    def each_template(&block) #:nodoc:
-      self.class.view_paths.each do |load_paths|
-        templates = load_paths.find_all(action_name, {}, self.class.mailer_name)
-        templates = templates.uniq_by { |t| t.details[:formats] }
+    def each_template(paths, name, &block) #:nodoc:
+      Array(paths).each do |path|
+        self.class.view_paths.each do |load_paths|
+          templates = load_paths.find_all(name, {}, path)
+          templates = templates.uniq_by { |t| t.details[:formats] }
 
-        unless templates.empty?
-          templates.each(&block)
-          return
+          unless templates.empty?
+            templates.each(&block)
+            return
+          end
         end
       end
     end
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 222db66aaa..5fc229df09 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -14,8 +14,13 @@ class BaseTest < ActiveSupport::TestCase
       mail({:subject => "The first email on new API!"}.merge!(hash))
     end
 
-    def simple(hash = {})
-      mail(hash)
+    def welcome_with_headers(hash = {})
+      headers hash
+      mail
+    end
+
+    def welcome_from_another_path(path)
+      mail(:template_name => "welcome", :template_path => path)
     end
 
     def html_only(hash = {})
@@ -25,11 +30,6 @@ class BaseTest < ActiveSupport::TestCase
     def plain_text_only(hash = {})
       mail(hash)
     end
-    
-    def simple_with_headers(hash = {})
-      headers hash
-      mail
-    end
 
     def attachment_with_content(hash = {})
       attachments['invoice.pdf'] = 'This is test File content'
@@ -78,8 +78,12 @@ class BaseTest < ActiveSupport::TestCase
         format.html{ render "welcome" } if include_html
       end
     end
-    
-    def different_template(template_name='')
+
+    def implicit_different_template(template_name='')
+      mail(:template_name => template_name)
+    end
+
+    def explicit_different_template(template_name='')
       mail do |format|
         format.text { render :template => "#{mailer_name}/#{template_name}" }
         format.html { render :template => "#{mailer_name}/#{template_name}" }
@@ -88,13 +92,10 @@ class BaseTest < ActiveSupport::TestCase
 
     def different_layout(layout_name='')
       mail do |format|
-        format.text { 
-          render :layout => layout_name 
-        }
+        format.text { render :layout => layout_name }
         format.html { render :layout => layout_name }
       end
     end
-
   end
 
   test "method call to mail does not raise error" do
@@ -154,7 +155,7 @@ class BaseTest < ActiveSupport::TestCase
   test "can pass random headers in as a hash to mail" do
     hash = {'X-Special-Domain-Specific-Header' => "SecretValue",
             'In-Reply-To' => '1234@mikel.me.com' }
-    mail = BaseMailer.simple(hash)
+    mail = BaseMailer.welcome(hash)
     assert_equal('SecretValue', mail['X-Special-Domain-Specific-Header'].decoded)
     assert_equal('1234@mikel.me.com', mail['In-Reply-To'].decoded)
   end
@@ -162,7 +163,7 @@ class BaseTest < ActiveSupport::TestCase
   test "can pass random headers in as a hash" do
     hash = {'X-Special-Domain-Specific-Header' => "SecretValue",
             'In-Reply-To' => '1234@mikel.me.com' }
-    mail = BaseMailer.simple_with_headers(hash)
+    mail = BaseMailer.welcome_with_headers(hash)
     assert_equal('SecretValue', mail['X-Special-Domain-Specific-Header'].decoded)
     assert_equal('1234@mikel.me.com', mail['In-Reply-To'].decoded)
   end
@@ -247,9 +248,9 @@ class BaseTest < ActiveSupport::TestCase
   end
 
   test "uses random default headers from class" do
-    with_default BaseMailer, "X-SPAM" => "Not spam" do
-      email = BaseMailer.simple
-      assert_equal("Not spam", email["X-SPAM"].decoded)
+    with_default BaseMailer, "X-Custom" => "Custom" do
+      email = BaseMailer.welcome
+      assert_equal("Custom", email["X-Custom"].decoded)
     end
   end
 
@@ -476,18 +477,32 @@ class BaseTest < ActiveSupport::TestCase
   end
 
   # Rendering
-  test "that you can specify a different template" do
-    mail = BaseMailer.different_template('explicit_multipart_templates')
+  test "you can specify a different template for implicit render" do
+    mail = BaseMailer.implicit_different_template('implicit_multipart')
+    assert_equal("HTML Implicit Multipart", mail.html_part.body.decoded)
+    assert_equal("TEXT Implicit Multipart", mail.text_part.body.decoded)
+  end
+
+  test "you can specify a different template for explicit render" do
+    mail = BaseMailer.explicit_different_template('explicit_multipart_templates')
     assert_equal("HTML Explicit Multipart Templates", mail.html_part.body.decoded)
     assert_equal("TEXT Explicit Multipart Templates", mail.text_part.body.decoded)
   end
 
-  test "that you can specify a different layout" do
+  test "you can specify a different layout" do
     mail = BaseMailer.different_layout('different_layout')
     assert_equal("HTML -- HTML", mail.html_part.body.decoded)
     assert_equal("PLAIN -- PLAIN", mail.text_part.body.decoded)
   end
 
+  test "you can specify the template path for implicit lookup" do
+    mail = BaseMailer.welcome_from_another_path('another.path/base_mailer')
+    assert_equal("Welcome from another path", mail.body.encoded)
+
+    mail = BaseMailer.welcome_from_another_path(['unknown/invalid', 'another.path/base_mailer'])
+    assert_equal("Welcome from another path", mail.body.encoded)
+  end
+
   protected
 
     # Execute the block setting the given values and restoring old values after
-- 
cgit v1.2.3