lib/action_mailer/base.rb in actionmailer-3.0.0.beta vs lib/action_mailer/base.rb in actionmailer-3.0.0.beta2

- old
+ new

@@ -1,17 +1,19 @@ require 'mail' require 'action_mailer/tmail_compat' require 'action_mailer/collector' +require 'active_support/core_ext/array/wrap' +require 'active_support/core_ext/object/blank' module ActionMailer #:nodoc: # Action Mailer allows you to send email from your application using a mailer model and views. # # = Mailer Models # # To use Action Mailer, you need to create a mailer model. # - # $ script/generate mailer Notifier + # $ rails generate mailer Notifier # # The generated model inherits from ActionMailer::Base. Emails are defined by creating methods # within the model which are then used to set variables to be used in the mail template, to # change options on the mail, or to add attachments. # @@ -179,10 +181,22 @@ # tempalte in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts, # the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside, # and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book # with the filename +free_book.pdf+. # + # = Observing and Intercepting Mails + # + # ActionMailer provides hooks into the Mail observer and interceptor methods. These allow you to + # register objects that are called during the mail delivery life cycle. + # + # An observer object must implement the <tt>:delivered_email(message)</tt> method which will be + # called once for every email sent after the email has been sent. + # + # An interceptor object must implement the <tt>:delivering_email(message)</tt> method which will be + # called before the email is sent, allowing you to make modifications to the email before it hits + # the delivery agents. Your object should make and needed modifications directly to the passed + # in Mail::Message instance. # # = Configuration options # # These options are specified on the class level, like <tt>ActionMailer::Base.template_root = "/my/templates"</tt> # @@ -253,20 +267,21 @@ include DeliveryMethods, Quoting abstract! include AbstractController::Logger include AbstractController::Rendering - include AbstractController::LocalizedCache include AbstractController::Layouts include AbstractController::Helpers include AbstractController::Translation - include AbstractController::Compatibility helper ActionMailer::MailHelper include ActionMailer::OldApi include ActionMailer::DeprecatedApi + + delegate :register_observer, :to => Mail + delegate :register_interceptor, :to => Mail private_class_method :new #:nodoc: class_attribute :default_params self.default_params = { @@ -274,10 +289,12 @@ :charset => "utf-8", :content_type => "text/plain", :parts_order => [ "text/plain", "text/enriched", "text/html" ] }.freeze + ActiveSupport.run_load_hooks(:action_mailer, self) + class << self def mailer_name @mailer_name ||= name.underscore end @@ -450,15 +467,32 @@ # sent to a different address than the one in <tt>:from</tt>. Mail will actually use the # <tt>:return_path</tt> in preference to the <tt>:sender</tt> in preference to the <tt>:from</tt> # 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: # # mail(:to => 'mikel@test.lindsaar.net') do |format| # format.text # format.html @@ -491,11 +525,11 @@ content_type = headers[:content_type] parts_order = headers[:parts_order] # 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 quote_fields!(headers, charset) @@ -512,17 +546,15 @@ m.body.set_sort_order(parts_order) m.body.sort_parts! 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 protected @@ -546,46 +578,51 @@ end # 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: responses, parts_order = [], nil if block_given? - collector = ActionMailer::Collector.new(self) { render(action_name) } + collector = ActionMailer::Collector.new(lookup_context) { render(action_name) } yield(collector) parts_order = collector.responses.map { |r| r[:content_type] } 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| + self.formats = template.formats + responses << { - :body => render_to_body(:_template => template), + :body => render(:template => template), :content_type => template.mime_type.to_s } end end [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.wrap(paths).each do |path| + templates = lookup_context.find_all(name, path) + templates = templates.uniq_by { |t| t.formats } unless templates.empty? templates.each(&block) return end