Skip to content

Instantly share code, notes, and snippets.

@tmm1
Created March 27, 2013 23:25
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tmm1/5259069 to your computer and use it in GitHub Desktop.
Save tmm1/5259069 to your computer and use it in GitHub Desktop.
rails3 mailer backport
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