Created
March 27, 2013 23:25
-
-
Save tmm1/5259069 to your computer and use it in GitHub Desktop.
rails3 mailer backport
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if Rails.version < '3.0' | |
require 'mail' | |
require 'active_support/core_ext/module/attr_internal' | |
class ActionMailer3 | |
class Collector | |
attr_reader :responses | |
def initialize(context, &block) | |
@context = context | |
@responses = [] | |
@default_render = block | |
end | |
def any(*args, &block) | |
options = args.extract_options! | |
raise "You have to supply at least one format" if args.empty? | |
args.each { |type| send(type, options.dup, &block) } | |
end | |
alias :all :any | |
def custom(mime, options={}) | |
options.reverse_merge!(:content_type => mime.to_s) | |
@context.template_format = mime.to_sym | |
options[:body] = block_given? ? yield : @default_render.call | |
@responses << options | |
end | |
### | |
# AbstractController::Collector | |
def self.generate_method_for_mime(mime) | |
sym = mime.is_a?(Symbol) ? mime : mime.to_sym | |
const = sym.to_s.upcase | |
class_eval <<-RUBY, __FILE__, __LINE__ + 1 | |
def #{sym}(*args, &block) # def html(*args, &block) | |
custom(Mime::#{const}, *args, &block) # custom(Mime::HTML, *args, &block) | |
end # end | |
RUBY | |
end | |
Mime::SET.each do |mime| | |
generate_method_for_mime(mime) | |
end | |
end | |
class Base | |
private_class_method :new | |
class_attribute :default_params | |
self.default_params = { | |
:mime_version => "1.0", | |
:charset => "UTF-8", | |
:content_type => "text/plain", | |
:parts_order => [ "text/plain", "text/enriched", "text/html" ] | |
}.freeze | |
class << self | |
def default(value = nil) | |
self.default_params = default_params.merge(value).freeze if value | |
default_params | |
end | |
def deliver_mail(mail) #:nodoc: | |
unless (logger = ActionMailer::Base.logger).nil? | |
logger.info "Sent mail to #{Array(mail.destinations).join(', ')}:" | |
logger.debug "\n#{mail.to_s}" | |
end | |
yield # Let Mail do the delivery actions | |
end | |
def respond_to?(method, include_private = false) #:nodoc: | |
super || action_methods.include?(method.to_s) | |
end | |
def method_missing(method, *args) | |
return super unless respond_to?(method) | |
new(method, *args).message | |
end | |
def action_methods | |
public_instance_methods(false).map(&:to_s) | |
end | |
def mailer_name | |
@mailer_name ||= name.blank?? 'anonymous' : name.underscore | |
end | |
attr_writer :mailer_name | |
alias :controller_path :mailer_name | |
end | |
attr_reader :action_name | |
attr_internal :message | |
def initialize(method_name=nil, *args) | |
super() | |
@_message = Mail.new | |
process(method_name, *args) if method_name | |
end | |
def process(method_name, *args) | |
@action_name = method_name | |
__send__(method_name, *args) | |
end | |
def mailer_name | |
self.class.mailer_name | |
end | |
def headers(args=nil) | |
if args | |
@_message.headers(args) | |
else | |
@_message | |
end | |
end | |
def attachments | |
@_message.attachments | |
end | |
def mail(headers={}, &block) | |
m = @_message | |
# At the beginning, do not consider class default for parts order neither content_type | |
content_type = headers[:content_type] | |
parts_order = headers[:parts_order] | |
# Call all the procs (if any) | |
default_values = self.class.default.merge(self.class.default) do |k,v| | |
v.respond_to?(:call) ? v.bind(self).call : v | |
end | |
# Handle defaults | |
headers = headers.reverse_merge(default_values) | |
headers[:subject] ||= action_name.to_s.humanize | |
# Apply charset at the beginning so all fields are properly quoted | |
m.charset = charset = headers[:charset] | |
# Set configure delivery behavior | |
wrap_delivery_behavior!(headers.delete(:delivery_method)) | |
# Assign all headers except parts_order, content_type and body | |
assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path) | |
assignable.each { |k, v| m[k] = v } | |
# Render the templates and blocks | |
responses, explicit_order = collect_responses_and_parts_order(headers, &block) | |
create_parts_from_responses(m, responses) | |
# Setup content type, reapply charset and handle parts order | |
m.content_type = set_content_type(m, content_type, headers[:content_type]) | |
m.charset = charset | |
if m.multipart? | |
parts_order ||= explicit_order || headers[:parts_order] | |
m.body.set_sort_order(parts_order) | |
m.body.sort_parts! | |
end | |
m | |
end | |
protected | |
def set_content_type(m, user_content_type, class_default) | |
params = m.content_type_parameters || {} | |
case | |
when user_content_type.present? | |
user_content_type | |
when m.has_attachments? | |
if m.attachments.detect { |a| a.inline? } | |
["multipart", "related", params] | |
else | |
["multipart", "mixed", params] | |
end | |
when m.multipart? | |
["multipart", "alternative", params] | |
else | |
m.content_type || class_default | |
end | |
end | |
def collect_responses_and_parts_order(headers) #:nodoc: | |
responses, parts_order = [], nil | |
if block_given? | |
collector = ActionMailer3::Collector.new(view_base) { render(action_name) } | |
yield(collector) | |
parts_order = collector.responses.map { |r| r[:content_type] } | |
responses = collector.responses | |
elsif headers[:body] | |
responses << { | |
:body => headers.delete(:body), | |
:content_type => self.class.default[:content_type] || "text/plain" | |
} | |
else | |
templates_path = headers.delete(:template_path) || mailer_name | |
templates_name = headers.delete(:template_name) || action_name | |
each_template(templates_path, templates_name) do |template| | |
responses << { | |
:body => render(:template => template), | |
:content_type => template.mime_type.to_s | |
} | |
end | |
end | |
[responses, parts_order] | |
end | |
def render(opts = {}) | |
case opts | |
when Symbol, String | |
if template = template_root["#{mailer_name}/#{opts}"] | |
return view_base.render(:file => template) | |
else | |
fail "unknown render template (#{opts.inspect})" | |
end | |
when Hash | |
if file = opts[:file] | |
fail "unknown render(:file => #{file.inspect})" | |
end | |
if template = opts[:template] | |
return view_base.render(:file => template) | |
end | |
if text = opts[:text] | |
return text | |
end | |
fail("unknown render() option: #{opts.inspect}") | |
end | |
end | |
def template_root | |
ActionMailer::Base.template_root | |
end | |
def view_base | |
@_view_base ||= ActionView::Base.new(ActionMailer::Base.view_paths, {}, self) | |
end | |
def each_template(paths, name, &block) #:nodoc: | |
Dir.glob("#{template_root}/#{paths}/#{name}.*").each do |path| | |
yield template_root["#{paths}/#{File.basename(path)}"] | |
end | |
end | |
def create_parts_from_responses(m, responses) #:nodoc: | |
if responses.size == 1 && !m.has_attachments? | |
responses[0].each { |k,v| m[k] = v } | |
elsif responses.size > 1 && m.has_attachments? | |
container = Mail::Part.new | |
container.content_type = "multipart/alternative" | |
responses.each { |r| insert_part(container, r, m.charset) } | |
m.add_part(container) | |
else | |
responses.each { |r| insert_part(m, r, m.charset) } | |
end | |
end | |
def insert_part(container, response, charset) #:nodoc: | |
response[:charset] ||= charset | |
part = Mail::Part.new(response) | |
container.add_part(part) | |
end | |
### | |
# ActionMailer::DeliveryMethods | |
class << self | |
# Provides a list of emails that have been delivered by Mail::TestMailer | |
delegate :deliveries, :deliveries=, :to => Mail::TestMailer | |
# Adds a new delivery method through the given class using the given symbol | |
# as alias and the default options supplied: | |
# | |
# Example: | |
# | |
# add_delivery_method :sendmail, Mail::Sendmail, | |
# :location => '/usr/sbin/sendmail', | |
# :arguments => '-i -t' | |
# | |
def add_delivery_method(symbol, klass, default_options={}) | |
class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings") | |
send(:"#{symbol}_settings=", default_options) | |
self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze | |
end | |
def wrap_delivery_behavior(mail, method=nil) #:nodoc: | |
method ||= self.delivery_method | |
mail.delivery_handler = self | |
case method | |
when NilClass | |
raise "Delivery method cannot be nil" | |
when Symbol | |
if klass = delivery_methods[method.to_sym] | |
mail.delivery_method(klass, send(:"#{method}_settings")) | |
else | |
raise "Invalid delivery method #{method.inspect}" | |
end | |
else | |
mail.delivery_method(method) | |
end | |
mail.perform_deliveries = perform_deliveries | |
mail.raise_delivery_errors = raise_delivery_errors | |
end | |
end | |
class_attribute :delivery_methods, :delivery_method | |
# Do not make this inheritable, because we always want it to propagate | |
cattr_accessor :raise_delivery_errors | |
self.raise_delivery_errors = true | |
cattr_accessor :perform_deliveries | |
self.perform_deliveries = true | |
self.delivery_methods = {}.freeze | |
self.delivery_method = :smtp | |
add_delivery_method :smtp, Mail::SMTP, | |
:address => "localhost", | |
:port => 25, | |
:domain => 'localhost.localdomain', | |
:user_name => nil, | |
:password => nil, | |
:authentication => nil, | |
:enable_starttls_auto => true | |
add_delivery_method :file, Mail::FileDelivery, | |
:location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" | |
add_delivery_method :sendmail, Mail::Sendmail, | |
:location => '/usr/sbin/sendmail', | |
:arguments => '-i -t' | |
add_delivery_method :test, Mail::TestMailer | |
def wrap_delivery_behavior!(*args) #:nodoc: | |
self.class.wrap_delivery_behavior(message, *args) | |
end | |
end | |
end | |
%w[ | |
perform_deliveries | |
raise_delivery_errors | |
delivery_method | |
smtp_settings | |
].each do |var| | |
ActionMailer3::Base.send("#{var}=", ActionMailer::Base.send(var)) | |
end | |
if Rails.test? | |
def (ActionMailer::Base).deliveries | |
ActionMailer3::Base.deliveries | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment