Sync with trunk: upgrade to Rails 2.0.2

This commit is contained in:
Jason Blevins 2007-12-22 11:15:52 -05:00
commit fc586e3f6b
1083 changed files with 52815 additions and 41058 deletions

View file

@ -148,16 +148,16 @@ class ApplicationController < ActionController::Base
def set_content_type_header def set_content_type_header
if %w(atom_with_content atom_with_headlines).include?(action_name) if %w(atom_with_content atom_with_headlines).include?(action_name)
response.headers['Content-Type'] = 'application/atom+xml; charset=UTF-8' response.headers['type'] = 'application/atom+xml; charset=UTF-8'
elsif %w(tex).include?(action_name) elsif %w(tex).include?(action_name)
response.headers['Content-Type'] = 'text/plain; charset=UTF-8' response.headers['type'] = 'text/plain; charset=UTF-8'
elsif request.env['HTTP_USER_AGENT'] =~ /Validator/ or request.env.include?('HTTP_ACCEPT') && elsif request.env['HTTP_USER_AGENT'] =~ /Validator/ or request.env.include?('HTTP_ACCEPT') &&
Mime::Type.parse(request.env["HTTP_ACCEPT"]).include?(Mime::XHTML) Mime::Type.parse(request.env["HTTP_ACCEPT"]).include?(Mime::XHTML)
response.headers['Content-Type'] = 'application/xhtml+xml; charset=UTF-8' response.headers['type'] = 'application/xhtml+xml; charset=UTF-8'
elsif request.env['HTTP_USER_AGENT'] =~ /MathPlayer/ elsif request.env['HTTP_USER_AGENT'] =~ /MathPlayer/
response.headers['Content-Type'] = 'application/xhtml+xml' response.headers['type'] = 'application/xhtml+xml'
else else
response.headers['Content-Type'] = 'text/html; charset=UTF-8' response.headers['type'] = 'text/html; charset=UTF-8'
end end
end end

View file

@ -136,8 +136,8 @@ class WikiController < ApplicationController
if rss_with_content_allowed? if rss_with_content_allowed?
render_atom(hide_description = false) render_atom(hide_description = false)
else else
render_text 'Atom feed with content for this web is blocked for security reasons. ' + render :text => 'Atom feed with content for this web is blocked for security reasons. ' +
'The web is password-protected and not published', '403 Forbidden' 'The web is password-protected and not published', :status => 403
end end
end end
@ -268,7 +268,7 @@ class WikiController < ApplicationController
begin begin
@renderer = PageRenderer.new(@page.revisions.last) @renderer = PageRenderer.new(@page.revisions.last)
@show_diff = (params[:mode] == 'diff') @show_diff = (params[:mode] == 'diff')
render_action 'page' render :action => 'page'
# TODO this rescue should differentiate between errors due to rendering and errors in # TODO this rescue should differentiate between errors due to rendering and errors in
# the application itself (for application errors, it's better not to rescue the error at all) # the application itself (for application errors, it's better not to rescue the error at all)
rescue => e rescue => e
@ -284,7 +284,7 @@ class WikiController < ApplicationController
if not @page_name.nil? and not @page_name.empty? if not @page_name.nil? and not @page_name.empty?
redirect_to :web => @web_name, :action => 'new', :id => @page_name redirect_to :web => @web_name, :action => 'new', :id => @page_name
else else
render_text 'Page name is not specified', '404 Not Found' render :text => 'Page name is not specified', :status => 404
end end
end end
end end

View file

@ -1,7 +1,16 @@
# Bootstrap the Rails environment, frameworks, and default configuration # Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot') require File.join(File.dirname(__FILE__), 'boot')
require 'rails_generator/secret_key_generator'
Rails::Initializer.run do |config| Rails::Initializer.run do |config|
# Secret session key
generator = Rails::SecretKeyGenerator.new("Instiki")
config.action_controller.session = {
:session_key => "instiki_session",
:secret => generator.generate_secret
}
# Skip frameworks you're not going to use # Skip frameworks you're not going to use
config.frameworks -= [ :action_web_service, :action_mailer ] config.frameworks -= [ :action_web_service, :action_mailer ]

View file

@ -11,7 +11,7 @@ module Chunk
# Rails's default utf-8 support causes problems here. So, for Chunk::Abstract class, turn off # Rails's default utf-8 support causes problems here. So, for Chunk::Abstract class, turn off
# multibyte character support. # multibyte character support.
$KCODE = 'iso-8859-1' $KCODE = 'n'
# automatically construct the array of derivatives of Chunk::Abstract # automatically construct the array of derivatives of Chunk::Abstract
@derivatives = [] @derivatives = []

View file

@ -24,8 +24,7 @@ module Literal
# A literal chunk that protects HTML tags from wiki rendering. # A literal chunk that protects HTML tags from wiki rendering.
class Tags < AbstractLiteral class Tags < AbstractLiteral
TAGS = "a|img|em|strong|div|span|table|td|th|ul|ol|li|dl|dt|dd" TAGS_PATTERN = Regexp.new('<[a-zA-Z]+[^>]*?>', Regexp::MULTILINE)
TAGS_PATTERN = Regexp.new('<(?:'+TAGS+')[^>]*?>', Regexp::MULTILINE)
def self.pattern() TAGS_PATTERN end def self.pattern() TAGS_PATTERN end
end end
end end

View file

@ -19,7 +19,7 @@ class ApplicationTest < Test::Unit::TestCase
def test_utf8_header def test_utf8_header
get :show, :web => 'wiki1', :id => 'HomePage' get :show, :web => 'wiki1', :id => 'HomePage'
assert_equal 'text/html; charset=UTF-8', @response.headers['Content-Type'] assert_equal 'text/html; charset=UTF-8', @response.headers['type']
end end
def test_connect_to_model_unknown_wiki def test_connect_to_model_unknown_wiki

View file

@ -36,7 +36,7 @@ class FileControllerTest < Test::Unit::TestCase
assert_response(:success, bypass_body_parsing = true) assert_response(:success, bypass_body_parsing = true)
assert_equal "Contents of the file", r.body assert_equal "Contents of the file", r.body
assert_equal 'text/plain', r.headers['Content-Type'] assert_equal 'text/plain', r.headers['type']
end end
def test_file_download_pdf_file def test_file_download_pdf_file
@ -47,7 +47,7 @@ class FileControllerTest < Test::Unit::TestCase
assert_response(:success, bypass_body_parsing = true) assert_response(:success, bypass_body_parsing = true)
assert_equal "aaa\nbbb\n", r.body assert_equal "aaa\nbbb\n", r.body
assert_equal 'application/pdf', r.headers['Content-Type'] assert_equal 'application/pdf', r.headers['type']
end end
def test_pic_download_gif def test_pic_download_gif
@ -57,7 +57,7 @@ class FileControllerTest < Test::Unit::TestCase
r = get :file, :web => 'wiki1', :id => 'rails.gif' r = get :file, :web => 'wiki1', :id => 'rails.gif'
assert_response(:success, bypass_body_parsing = true) assert_response(:success, bypass_body_parsing = true)
assert_equal 'image/gif', r.headers['Content-Type'] assert_equal 'image/gif', r.headers['type']
assert_equal pic.size, r.body.size assert_equal pic.size, r.body.size
assert_equal pic, r.body assert_equal pic, r.body
end end

View file

@ -112,7 +112,7 @@ class WikiControllerTest < Test::Unit::TestCase
r = process 'export_html', 'web' => 'wiki1' r = process 'export_html', 'web' => 'wiki1'
assert_response(:success, bypass_body_parsing = true) assert_response(:success, bypass_body_parsing = true)
assert_equal 'application/zip', r.headers['Content-Type'] assert_equal 'application/zip', r.headers['type']
assert_match /attachment; filename="wiki1-html-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/, assert_match /attachment; filename="wiki1-html-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/,
r.headers['Content-Disposition'] r.headers['Content-Disposition']
assert_equal 'PK', r.body[0..1], 'Content is not a zip file' assert_equal 'PK', r.body[0..1], 'Content is not a zip file'
@ -140,7 +140,7 @@ class WikiControllerTest < Test::Unit::TestCase
r = process 'export_html', 'web' => 'wiki1', 'layout' => 'no' r = process 'export_html', 'web' => 'wiki1', 'layout' => 'no'
assert_response(:success, bypass_body_parsing = true) assert_response(:success, bypass_body_parsing = true)
assert_equal 'application/zip', r.headers['Content-Type'] assert_equal 'application/zip', r.headers['type']
assert_match /attachment; filename="wiki1-html-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/, assert_match /attachment; filename="wiki1-html-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/,
r.headers['Content-Disposition'] r.headers['Content-Disposition']
assert_equal 'PK', r.body[0..1], 'Content is not a zip file' assert_equal 'PK', r.body[0..1], 'Content is not a zip file'
@ -150,7 +150,7 @@ class WikiControllerTest < Test::Unit::TestCase
r = process 'export_markup', 'web' => 'wiki1' r = process 'export_markup', 'web' => 'wiki1'
assert_response(:success, bypass_body_parsing = true) assert_response(:success, bypass_body_parsing = true)
assert_equal 'application/zip', r.headers['Content-Type'] assert_equal 'application/zip', r.headers['type']
assert_match /attachment; filename="wiki1-markdownMML-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/, assert_match /attachment; filename="wiki1-markdownMML-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/,
r.headers['Content-Disposition'] r.headers['Content-Disposition']
assert_equal 'PK', r.body[0..1], 'Content is not a zip file' assert_equal 'PK', r.body[0..1], 'Content is not a zip file'
@ -162,7 +162,7 @@ class WikiControllerTest < Test::Unit::TestCase
# def test_export_pdf # def test_export_pdf
# r = process 'export_pdf', 'web' => 'wiki1' # r = process 'export_pdf', 'web' => 'wiki1'
# assert_response(:success, bypass_body_parsing = true) # assert_response(:success, bypass_body_parsing = true)
# assert_equal 'application/pdf', r.headers['Content-Type'] # assert_equal 'application/pdf', r.headers['type']
# assert_match /attachment; filename="wiki1-tex-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.pdf"/, # assert_match /attachment; filename="wiki1-tex-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.pdf"/,
# r.headers['Content-Disposition'] # r.headers['Content-Disposition']
# assert_equal '%PDF', r.body[0..3] # assert_equal '%PDF', r.body[0..3]
@ -179,7 +179,7 @@ class WikiControllerTest < Test::Unit::TestCase
# r = process 'export_tex', 'web' => 'wiki1' # r = process 'export_tex', 'web' => 'wiki1'
# #
# assert_response(:success, bypass_body_parsing = true) # assert_response(:success, bypass_body_parsing = true)
# assert_equal 'application/octet-stream', r.headers['Content-Type'] # assert_equal 'application/octet-stream', r.headers['type']
# assert_match /attachment; filename="wiki1-tex-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.tex"/, # assert_match /attachment; filename="wiki1-tex-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.tex"/,
# r.headers['Content-Disposition'] # r.headers['Content-Disposition']
# assert_equal '\documentclass', r.body[0..13], 'Content is not a TeX file' # assert_equal '\documentclass', r.body[0..13], 'Content is not a TeX file'
@ -259,7 +259,7 @@ class WikiControllerTest < Test::Unit::TestCase
# assert_equal '%PDF', r.body[0..3] # assert_equal '%PDF', r.body[0..3]
# assert_equal "EOF\n", r.body[-4..-1] # assert_equal "EOF\n", r.body[-4..-1]
# #
# assert_equal 'application/pdf', r.headers['Content-Type'] # assert_equal 'application/pdf', r.headers['type']
# assert_match /attachment; filename="HomePage-wiki1-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.pdf"/, # assert_match /attachment; filename="HomePage-wiki1-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.pdf"/,
# r.headers['Content-Disposition'] # r.headers['Content-Disposition']
# end # end
@ -633,8 +633,8 @@ class WikiControllerTest < Test::Unit::TestCase
end end
def test_show_page def test_show_page
r = process('show', 'id' => 'Oak', 'web' => 'wiki1') r = process 'show', 'id' => 'Oak', 'web' => 'wiki1'
assert_response(:success) assert_response :success
assert_tag :content => /All about oak/ assert_tag :content => /All about oak/
end end
@ -644,7 +644,7 @@ class WikiControllerTest < Test::Unit::TestCase
r = process('show', 'id' => 'HomePage', 'web' => 'wiki1') r = process('show', 'id' => 'HomePage', 'web' => 'wiki1')
assert_response(:success) assert_response :success
assert_match /Second revision of the <a.*HomePage.*<\/a> end/, r.body assert_match /Second revision of the <a.*HomePage.*<\/a> end/, r.body
end end

View file

@ -75,6 +75,11 @@ class PageRendererTest < Test::Unit::TestCase
%{Smart Engine GUI<a href="../show/SmartEngineGUI">?</a></span></p>}, %{Smart Engine GUI<a href="../show/SmartEngineGUI">?</a></span></p>},
"#My Headline#\n\nthat SmartEngineGUI") "#My Headline#\n\nthat SmartEngineGUI")
assert_markup_parsed_as(
%{<p>SVG <animateColor title="MathML"><span class="newWikiWord">} +
%{Math ML<a href="../show/MathML">?</a></span></animateColor></p>},
"SVG <animateColor title='MathML'>MathML</animateColor>")
assert_markup_parsed_as( assert_markup_parsed_as(
%{<div class="maruku-equation"><math class="maruku-mathml" display="block" } + %{<div class="maruku-equation"><math class="maruku-mathml" display="block" } +
%{xmlns="http://www.w3.org/1998/Math/MathML"><mi>sin</mi><mo stretchy="false">} + %{xmlns="http://www.w3.org/1998/Math/MathML"><mi>sin</mi><mo stretchy="false">} +
@ -110,6 +115,12 @@ class PageRendererTest < Test::Unit::TestCase
%{<div class="maruku-eq-tex"><code style="display: none;">\\sin(x)</code></div></div>}, %{<div class="maruku-eq-tex"><code style="display: none;">\\sin(x)</code></div></div>},
"ecuasi\303\263n\n$$\\sin(x)$$") "ecuasi\303\263n\n$$\\sin(x)$$")
assert_markup_parsed_as(
%{<p>ecuasi\303\263n</p>\n\n<p><span class="maruku-inline"><math class="maruku-mathml" } +
%{display="inline" xmlns="http://www.w3.org/1998/Math/MathML">} +
%{<mi>sin</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></math></span></p>},
"ecuasi\303\263n \n\n$\\sin(x)$")
#FIXME #FIXME
assert_markup_parsed_as( assert_markup_parsed_as(
%{<p>ecuasi\303\263n <span class="maruku-inline"><math class="maruku-mathml" } + %{<p>ecuasi\303\263n <span class="maruku-inline"><math class="maruku-mathml" } +

View file

@ -150,7 +150,7 @@ module ActionController
def send_not_modified(controller) def send_not_modified(controller)
controller.logger.info "Send Not Modified" controller.logger.info "Send Not Modified"
controller.response.headers['ETag'] = %{"#{MD5.new(fragment_body(controller)).to_s}"} controller.response.headers['ETag'] = %{"#{MD5.new(fragment_body(controller)).to_s}"}
controller.render(:text => "", :status => 304) controller.send(:render, {:text => "", :status => 304})
end end
def client_has_latest?(cache_entry, controller) def client_has_latest?(cache_entry, controller)

View file

@ -105,7 +105,12 @@ module MaRuKu
xml = "" xml = ""
if (content_only) if (content_only)
body.write(xml,indent,transitive=true,ie_hack); if $rexml_new_version
formatter = REXML::Formatters::Default.new(ie_hack)
formatter.write(body, xml)
else
body.write(xml,indent,transitive=true,ie_hack);
end
else else
doc2 = Document.new("<div>"+S5_external+"</div>",{:respect_whitespace =>:all}) doc2 = Document.new("<div>"+S5_external+"</div>",{:respect_whitespace =>:all})
doc2.root.children.each{ |child| head << child } doc2.root.children.each{ |child| head << child }

View file

@ -1,11 +1,49 @@
*1.3.5* (October 12th, 2007) *2.0.2* (December 16th, 2007)
* Depend on Action Pack 1.13.5 * Included in Rails 2.0.2
*1.3.4* (October 4th, 2007) *2.0.1* (December 7th, 2007)
* Depend on Action Pack 1.13.4 * Update ActionMailer so it treats ActionView the same way that ActionController does. Closes #10244 [rick]
* Pass the template_root as an array as ActionView's view_path
* Request templates with the "#{mailer_name}/#{action}" as opposed to just "#{action}"
* Fixed that partials would be broken when using text.plain.erb as the extension #10130 [java]
* Update README to use new smtp settings configuration API. Closes #10060 [psq]
* Allow ActionMailer subclasses to individually set their delivery method (so two subclasses can have different delivery methods) #10033 [zdennis]
* Update TMail to v1.1.0. Use an updated version of TMail if available. [mikel]
* Introduce a new base test class for testing Mailers. ActionMailer::TestCase [Koz]
* Fix silent failure of rxml templates. #9879 [jstewart]
* Fix attachment decoding when using the TMail C extension. #7861 [orangechicken]
* Increase mail delivery test coverage. #8692 [Kamal Fariz Mahyuddin]
* Register alternative template engines using ActionMailer::Base.register_template_extension('haml'). #7534 [cwd, Josh Peek]
* Only load ActionController::UrlWriter if ActionController is present [Rick Olson]
* Make sure parsed emails recognized attachments nested inside multipart parts. #6714 [Jamis Buck]
* Allow mailer actions named send by using __send__ internally. #6467 [iGEL]
* Add assert_emails and assert_no_emails to test the number of emails delivered. #6479 [Jonathan Viney]
# Assert total number of emails delivered:
assert_emails 0
ContactMailer.deliver_contact
assert_emails 1
# Assert number of emails delivered within a block:
assert_emails 1 do
post :signup, :name => 'Jonathan'
end
*1.3.3* (March 12th, 2007) *1.3.3* (March 12th, 2007)
@ -39,6 +77,8 @@
* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar] * Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
* Mailer template root applies to a class and its subclasses rather than acting globally. #5555 [somekool@gmail.com]
* Resolve action naming collision. #5520 [ssinghi@kreeti.com] * Resolve action naming collision. #5520 [ssinghi@kreeti.com]
* ActionMailer::Base documentation rewrite. Closes #4991 [Kevin Clark, Marcel Molina Jr.] * ActionMailer::Base documentation rewrite. Closes #4991 [Kevin Clark, Marcel Molina Jr.]
@ -47,30 +87,8 @@
* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.] * Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
*1.2.5* (August 10th, 2006)
* Depend on Action Pack 1.12.5
*1.2.4* (August 8th, 2006)
* Backport of documentation enhancements. [Kevin Clark, Marcel Molina Jr]
* Correct spurious documentation example code which results in a SyntaxError. [Marcel Molina Jr.] * Correct spurious documentation example code which results in a SyntaxError. [Marcel Molina Jr.]
* Mailer template root applies to a class and its subclasses rather than acting globally. #5555 [somekool@gmail.com]
*1.2.3* (June 29th, 2006)
* Depend on Action Pack 1.12.3
*1.2.2* (June 27th, 2006)
* Depend on Action Pack 1.12.2
*1.2.1* (April 6th, 2006) *1.2.1* (April 6th, 2006)

View file

@ -1,4 +1,4 @@
Copyright (c) 2004-2006 David Heinemeier Hansson Copyright (c) 2004-2007 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the

View file

@ -86,7 +86,7 @@ This Mailman can be the target for Postfix. In Rails, you would use the runner l
The Base class has the full list of configuration options. Here's an example: The Base class has the full list of configuration options. Here's an example:
ActionMailer::Base.server_settings = { ActionMailer::Base.smtp_settings = {
:address=>'smtp.yourserver.com', # default: localhost :address=>'smtp.yourserver.com', # default: localhost
:port=>'25', # default: 25 :port=>'25', # default: 25
:user_name=>'user', :user_name=>'user',

View file

@ -29,11 +29,12 @@ Rake::TestTask.new { |t|
} }
# Genereate the RDoc documentation # Generate the RDoc documentation
Rake::RDocTask.new { |rdoc| Rake::RDocTask.new { |rdoc|
rdoc.rdoc_dir = 'doc' rdoc.rdoc_dir = 'doc'
rdoc.title = "Action Mailer -- Easy email delivery and testing" rdoc.title = "Action Mailer -- Easy email delivery and testing"
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object' rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
rdoc.options << '--charset' << 'utf-8'
rdoc.template = "#{ENV['template']}.rb" if ENV['template'] rdoc.template = "#{ENV['template']}.rb" if ENV['template']
rdoc.rdoc_files.include('README', 'CHANGELOG') rdoc.rdoc_files.include('README', 'CHANGELOG')
rdoc.rdoc_files.include('lib/action_mailer.rb') rdoc.rdoc_files.include('lib/action_mailer.rb')
@ -54,7 +55,7 @@ spec = Gem::Specification.new do |s|
s.rubyforge_project = "actionmailer" s.rubyforge_project = "actionmailer"
s.homepage = "http://www.rubyonrails.org" s.homepage = "http://www.rubyonrails.org"
s.add_dependency('actionpack', '= 1.13.5' + PKG_BUILD) s.add_dependency('actionpack', '= 2.0.2' + PKG_BUILD)
s.has_rdoc = true s.has_rdoc = true
s.requirements << 'none' s.requirements << 'none'

View file

@ -18,7 +18,7 @@ unless $sitedir
end end
end end
# the acual gruntwork # the actual gruntwork
Dir.chdir("lib") Dir.chdir("lib")
Find.find("action_mailer", "action_mailer.rb") { |f| Find.find("action_mailer", "action_mailer.rb") { |f|

View file

@ -1,5 +1,5 @@
#-- #--
# Copyright (c) 2004-2006 David Heinemeier Hansson # Copyright (c) 2004-2007 David Heinemeier Hansson
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -31,13 +31,15 @@ unless defined?(ActionController)
end end
end end
$:.unshift(File.dirname(__FILE__) + "/action_mailer/vendor/") require 'action_mailer/vendor'
require 'tmail'
require 'action_mailer/base' require 'action_mailer/base'
require 'action_mailer/helpers' require 'action_mailer/helpers'
require 'action_mailer/mail_helper' require 'action_mailer/mail_helper'
require 'action_mailer/quoting' require 'action_mailer/quoting'
require 'tmail' require 'action_mailer/test_helper'
require 'net/smtp' require 'net/smtp'
ActionMailer::Base.class_eval do ActionMailer::Base.class_eval do

View file

@ -52,9 +52,9 @@ module ActionMailer #:nodoc:
# #
# Like ActionController, each mailer class has a corresponding view directory # Like ActionController, each mailer class has a corresponding view directory
# in which each method of the class looks for a template with its name. # in which each method of the class looks for a template with its name.
# To define a template to be used with a mailing, create an <tt>.rhtml</tt> file with the same name as the method # To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same name as the method
# in your mailer model. For example, in the mailer defined above, the template at # in your mailer model. For example, in the mailer defined above, the template at
# <tt>app/views/notifier/signup_notification.rhtml</tt> would be used to generate the email. # <tt>app/views/notifier/signup_notification.erb</tt> would be used to generate the email.
# #
# Variables defined in the model are accessible as instance variables in the view. # Variables defined in the model are accessible as instance variables in the view.
# #
@ -103,7 +103,7 @@ module ActionMailer #:nodoc:
# #
# = HTML email # = HTML email
# #
# To send mail as HTML, make sure your view (the <tt>.rhtml</tt> file) generates HTML and # To send mail as HTML, make sure your view (the <tt>.erb</tt> file) generates HTML and
# set the content type to html. # set the content type to html.
# #
# class MyMailer < ActionMailer::Base # class MyMailer < ActionMailer::Base
@ -142,10 +142,10 @@ module ActionMailer #:nodoc:
# by the content type. Each such detected template will be added as separate part to the message. # by the content type. Each such detected template will be added as separate part to the message.
# #
# For example, if the following templates existed: # For example, if the following templates existed:
# * signup_notification.text.plain.rhtml # * signup_notification.text.plain.erb
# * signup_notification.text.html.rhtml # * signup_notification.text.html.erb
# * signup_notification.text.xml.rxml # * signup_notification.text.xml.builder
# * signup_notification.text.x-yaml.rhtml # * signup_notification.text.x-yaml.erb
# #
# Each would be rendered and added as a separate part to the message, # Each would be rendered and added as a separate part to the message,
# with the corresponding content type. The same body hash is passed to # with the corresponding content type. The same body hash is passed to
@ -219,17 +219,16 @@ module ActionMailer #:nodoc:
# <tt>@implicit_parts_order</tt>. # <tt>@implicit_parts_order</tt>.
class Base class Base
include AdvAttrAccessor, PartContainer include AdvAttrAccessor, PartContainer
include ActionController::UrlWriter include ActionController::UrlWriter if Object.const_defined?(:ActionController)
# Action Mailer subclasses should be reloaded by the dispatcher in Rails
# when Dependencies.mechanism = :load.
include Reloadable::Deprecated
private_class_method :new #:nodoc: private_class_method :new #:nodoc:
class_inheritable_accessor :template_root class_inheritable_accessor :template_root
cattr_accessor :logger cattr_accessor :logger
cattr_accessor :template_extensions
@@template_extensions = ['erb', 'builder', 'rhtml', 'rxml']
@@smtp_settings = { @@smtp_settings = {
:address => "localhost", :address => "localhost",
:port => 25, :port => 25,
@ -249,8 +248,8 @@ module ActionMailer #:nodoc:
@@raise_delivery_errors = true @@raise_delivery_errors = true
cattr_accessor :raise_delivery_errors cattr_accessor :raise_delivery_errors
@@delivery_method = :smtp superclass_delegating_accessor :delivery_method
cattr_accessor :delivery_method self.delivery_method = :smtp
@@perform_deliveries = true @@perform_deliveries = true
cattr_accessor :perform_deliveries cattr_accessor :perform_deliveries
@ -299,11 +298,6 @@ module ActionMailer #:nodoc:
# This defaults to the value for the +default_implicit_parts_order+. # This defaults to the value for the +default_implicit_parts_order+.
adv_attr_accessor :implicit_parts_order adv_attr_accessor :implicit_parts_order
# Override the mailer name, which defaults to an inflected version of the
# mailer's class name. If you want to use a template in a non-standard
# location, you can use this to specify that location.
adv_attr_accessor :mailer_name
# Defaults to "1.0", but may be explicitly given if needed. # Defaults to "1.0", but may be explicitly given if needed.
adv_attr_accessor :mime_version adv_attr_accessor :mime_version
@ -323,10 +317,35 @@ module ActionMailer #:nodoc:
# have multiple mailer methods share the same template. # have multiple mailer methods share the same template.
adv_attr_accessor :template adv_attr_accessor :template
# Override the mailer name, which defaults to an inflected version of the
# mailer's class name. If you want to use a template in a non-standard
# location, you can use this to specify that location.
def mailer_name(value = nil)
if value
self.mailer_name = value
else
self.class.mailer_name
end
end
def mailer_name=(value)
self.class.mailer_name = value
end
# The mail object instance referenced by this mailer. # The mail object instance referenced by this mailer.
attr_reader :mail attr_reader :mail
class << self class << self
attr_writer :mailer_name
def mailer_name
@mailer_name ||= name.underscore
end
# for ActionView compatibility
alias_method :controller_name, :mailer_name
alias_method :controller_path, :mailer_name
def method_missing(method_symbol, *parameters)#:nodoc: def method_missing(method_symbol, *parameters)#:nodoc:
case method_symbol.id2name case method_symbol.id2name
when /^create_([_a-z]\w*)/ then new($1, *parameters).mail when /^create_([_a-z]\w*)/ then new($1, *parameters).mail
@ -364,17 +383,16 @@ module ActionMailer #:nodoc:
new.deliver!(mail) new.deliver!(mail)
end end
# Server Settings is the old name for <tt>smtp_settings</tt> # Register a template extension so mailer templates written in a
def server_settings # templating language other than rhtml or rxml are supported.
smtp_settings # To use this, include in your template-language plugin's init
# code or on a per-application basis, this can be invoked from
# config/environment.rb:
#
# ActionMailer::Base.register_template_extension('haml')
def register_template_extension(extension)
template_extensions << extension
end end
deprecate :server_settings=>"It's now named smtp_settings"
def server_settings=(settings)
ActiveSupport::Deprecation.warn("server_settings has been renamed smtp_settings, this warning will be removed with rails 2.0", caller)
self.smtp_settings=settings
end
end end
# Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
@ -389,20 +407,20 @@ module ActionMailer #:nodoc:
# rendered and a new TMail::Mail object created. # rendered and a new TMail::Mail object created.
def create!(method_name, *parameters) #:nodoc: def create!(method_name, *parameters) #:nodoc:
initialize_defaults(method_name) initialize_defaults(method_name)
send(method_name, *parameters) __send__(method_name, *parameters)
# If an explicit, textual body has not been set, we check assumptions. # If an explicit, textual body has not been set, we check assumptions.
unless String === @body unless String === @body
# First, we look to see if there are any likely templates that match, # First, we look to see if there are any likely templates that match,
# which include the content-type in their file name (i.e., # which include the content-type in their file name (i.e.,
# "the_template_file.text.html.rhtml", etc.). Only do this if parts # "the_template_file.text.html.erb", etc.). Only do this if parts
# have not already been specified manually. # have not already been specified manually.
if @parts.empty? if @parts.empty?
templates = Dir.glob("#{template_path}/#{@template}.*") templates = Dir.glob("#{template_path}/#{@template}.*")
templates.each do |path| templates.each do |path|
# TODO: don't hardcode rhtml|rxml
basename = File.basename(path) basename = File.basename(path)
next unless md = /^([^\.]+)\.([^\.]+\.[^\.]+)\.(rhtml|rxml)$/.match(basename) template_regex = Regexp.new("^([^\\\.]+)\\\.([^\\\.]+\\\.[^\\\.]+)\\\.(" + template_extensions.join('|') + ")$")
next unless md = template_regex.match(basename)
template_name = basename template_name = basename
content_type = md.captures[1].gsub('.', '/') content_type = md.captures[1].gsub('.', '/')
@parts << Part.new(:content_type => content_type, @parts << Part.new(:content_type => content_type,
@ -448,7 +466,7 @@ module ActionMailer #:nodoc:
logger.info "Sent mail:\n #{mail.encoded}" unless logger.nil? logger.info "Sent mail:\n #{mail.encoded}" unless logger.nil?
begin begin
send("perform_delivery_#{delivery_method}", mail) if perform_deliveries __send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries
rescue Exception => e # Net::SMTP errors or sendmail pipe errors rescue Exception => e # Net::SMTP errors or sendmail pipe errors
raise e if raise_delivery_errors raise e if raise_delivery_errors
end end
@ -478,6 +496,9 @@ module ActionMailer #:nodoc:
def render(opts) def render(opts)
body = opts.delete(:body) body = opts.delete(:body)
if opts[:file] && opts[:file] !~ /\//
opts[:file] = "#{mailer_name}/#{opts[:file]}"
end
initialize_template_class(body).render(opts) initialize_template_class(body).render(opts)
end end
@ -486,7 +507,7 @@ module ActionMailer #:nodoc:
end end
def initialize_template_class(assigns) def initialize_template_class(assigns)
ActionView::Base.new(template_path, assigns, self) ActionView::Base.new([template_root], assigns, self)
end end
def sort_parts(parts, order = []) def sort_parts(parts, order = [])

View file

@ -49,7 +49,7 @@ module ActionMailer
begin begin
require_dependency(file_name) require_dependency(file_name)
rescue LoadError => load_error rescue LoadError => load_error
requiree = / -- (.*?)(\.rb)?$/.match(load_error).to_a[1] requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}" msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
raise LoadError.new(msg).copy_blame!(load_error) raise LoadError.new(msg).copy_blame!(load_error)
end end
@ -72,7 +72,7 @@ module ActionMailer
methods.flatten.each do |method| methods.flatten.each do |method|
master_helper_module.module_eval <<-end_eval master_helper_module.module_eval <<-end_eval
def #{method}(*args, &block) def #{method}(*args, &block)
controller.send(%(#{method}), *args, &block) controller.send!(%(#{method}), *args, &block)
end end
end_eval end_eval
end end
@ -92,7 +92,7 @@ module ActionMailer
inherited_without_helper(child) inherited_without_helper(child)
begin begin
child.master_helper_module = Module.new child.master_helper_module = Module.new
child.master_helper_module.send :include, master_helper_module child.master_helper_module.send! :include, master_helper_module
child.helper child.name.underscore child.helper child.name.underscore
rescue MissingSourceFile => e rescue MissingSourceFile => e
raise unless e.is_missing?("helpers/#{child.name.underscore}_helper") raise unless e.is_missing?("helpers/#{child.name.underscore}_helper")

View file

@ -84,11 +84,8 @@ module ActionMailer
end end
else else
if String === body if String === body
part = TMail::Mail.new @parts.unshift Part.new(:charset => charset, :body => @body, :content_type => 'text/plain')
part.body = body @body = nil
part.set_content_type(real_content_type, nil, ctype_attrs)
part.set_content_disposition "inline"
m.parts << part
end end
@parts.each do |p| @parts.each do |p|

View file

@ -0,0 +1,59 @@
require 'active_support/test_case'
module ActionMailer
class NonInferrableMailerError < ::StandardError
def initialize(name)
super "Unable to determine the mailer to test from #{name}. " +
"You'll need to specify it using tests YourMailer in your " +
"test case definition"
end
end
# New Test Super class for forward compatibility.
# To override
class TestCase < ActiveSupport::TestCase
include ActionMailer::Quoting
class << self
def tests(mailer)
write_inheritable_attribute(:mailer_class, mailer)
end
def mailer_class
if mailer = read_inheritable_attribute(:mailer_class)
mailer
else
tests determine_default_mailer(name)
end
end
def determine_default_mailer(name)
name.sub(/Test$/, '').constantize
rescue NameError => e
raise NonInferrableMailerError.new(name)
end
end
def setup
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries = []
@expected = TMail::Mail.new
@expected.set_content_type "text", "plain", { "charset" => charset }
@expected.mime_version = '1.0'
end
private
def charset
"utf-8"
end
def encode(subject)
quoted_printable(subject, charset)
end
def read_fixture(action)
IO.readlines(File.join(RAILS_ROOT, 'test', 'fixtures', self.class.mailer_class.name.underscore, action))
end
end
end

View file

@ -0,0 +1,67 @@
module ActionMailer
module TestHelper
# Asserts that the number of emails sent matches the given number.
#
# def test_emails
# assert_emails 0
# ContactMailer.deliver_contact
# assert_emails 1
# ContactMailer.deliver_contact
# assert_emails 2
# end
#
# If a block is passed, that block should cause the specified number of emails to be sent.
#
# def test_emails_again
# assert_emails 1 do
# ContactMailer.deliver_contact
# end
#
# assert_emails 2 do
# ContactMailer.deliver_contact
# ContactMailer.deliver_contact
# end
# end
def assert_emails(number)
if block_given?
original_count = ActionMailer::Base.deliveries.size
yield
new_count = ActionMailer::Base.deliveries.size
assert_equal original_count + number, new_count, "#{number} emails expected, but #{new_count - original_count} were sent"
else
assert_equal number, ActionMailer::Base.deliveries.size
end
end
# Assert that no emails have been sent.
#
# def test_emails
# assert_no_emails
# ContactMailer.deliver_contact
# assert_emails 1
# end
#
# If a block is passed, that block should not cause any emails to be sent.
#
# def test_emails_again
# assert_no_emails do
# # No emails should be sent from this block
# end
# end
#
# Note: This assertion is simply a shortcut for:
#
# assert_emails 0
def assert_no_emails(&block)
assert_emails 0, &block
end
end
end
module Test
module Unit
class TestCase
include ActionMailer::TestHelper
end
end
end

View file

@ -0,0 +1,14 @@
# Prefer gems to the bundled libs.
require 'rubygems'
begin
gem 'tmail', '~> 1.1.0'
rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/tmail-1.1.0"
end
begin
gem 'text-format', '>= 0.6.3'
rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/text-format-0.6.3"
end

View file

@ -0,0 +1,4 @@
require 'tmail/version'
require 'tmail/mail'
require 'tmail/mailbox'
require 'tmail/core_extensions'

View file

@ -0,0 +1,19 @@
#
# lib/tmail/Makefile
#
debug:
rm -f parser.rb
make parser.rb DEBUG=true
parser.rb: parser.y
if [ "$(DEBUG)" = true ]; then \
racc -v -g -o$@ parser.y ;\
else \
racc -E -o$@ parser.y ;\
fi
clean:
rm -f parser.rb parser.output
distclean: clean

View file

@ -1,5 +1,8 @@
# =begin rdoc
# address.rb
= Address handling class
=end
# #
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
@ -51,6 +54,7 @@ module TMail
raise SyntaxError, 'empty word in domain' if s.empty? raise SyntaxError, 'empty word in domain' if s.empty?
end end
end end
@local = local @local = local
@domain = domain @domain = domain
@name = nil @name = nil
@ -96,7 +100,6 @@ module TMail
alias address spec alias address spec
def ==( other ) def ==( other )
other.respond_to? :spec and self.spec == other.spec other.respond_to? :spec and self.spec == other.spec
end end

View file

@ -1,3 +1,9 @@
=begin rdoc
= Attachment handling class
=end
require 'stringio' require 'stringio'
module TMail module TMail
@ -18,7 +24,9 @@ module TMail
def attachments def attachments
if multipart? if multipart?
parts.collect { |part| parts.collect { |part|
if attachment?(part) if part.multipart?
part.attachments
elsif attachment?(part)
content = part.body # unquoted automatically by TMail#body content = part.body # unquoted automatically by TMail#body
file_name = (part['content-location'] && file_name = (part['content-location'] &&
part['content-location'].body) || part['content-location'].body) ||
@ -32,7 +40,7 @@ module TMail
attachment.content_type = part.content_type attachment.content_type = part.content_type
attachment attachment
end end
}.compact }.flatten.compact
end end
end end
end end

View file

@ -0,0 +1,52 @@
# = TITLE:
#
# Base64
#
# = COPYRIGHT:
#
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
# with permission of Minero Aoki.
#
module TMail
module Base64
module_function
def folding_encode( str, eol = "\n", limit = 60 )
[str].pack('m')
end
def encode( str )
[str].pack('m').tr( "\r\n", '' )
end
def decode( str, strict = false )
str.unpack('m').first
end
end
end

View file

@ -0,0 +1,39 @@
unless Enumerable.method_defined?(:map)
module Enumerable
alias map collect
end
end
unless Enumerable.method_defined?(:select)
module Enumerable
alias select find_all
end
end
unless Enumerable.method_defined?(:reject)
module Enumerable
def reject
result = []
each do |i|
result.push i unless yield(i)
end
result
end
end
end
unless Enumerable.method_defined?(:sort_by)
module Enumerable
def sort_by
map {|i| [yield(i), i] }.sort.map {|val, i| i }
end
end
end
unless File.respond_to?(:read)
def File.read(fname)
File.open(fname) {|f|
return f.read
}
end
end

View file

@ -1,6 +1,8 @@
# =begin rdoc
# config.rb
# = Configuration Class
=end
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
# #

View file

@ -0,0 +1,67 @@
=begin rdoc
= Ruby on Rails Core Extensions
provides .blank?
=end
unless Object.respond_to?(:blank?) #:nodoc:
# Check first to see if we are in a Rails environment, no need to
# define these methods if we are
class Object
# An object is blank if it's nil, empty, or a whitespace string.
# For example, "", " ", nil, [], and {} are blank.
#
# This simplifies
# if !address.nil? && !address.empty?
# to
# if !address.blank?
def blank?
if respond_to?(:empty?) && respond_to?(:strip)
empty? or strip.empty?
elsif respond_to?(:empty?)
empty?
else
!self
end
end
end
class NilClass #:nodoc:
def blank?
true
end
end
class FalseClass #:nodoc:
def blank?
true
end
end
class TrueClass #:nodoc:
def blank?
false
end
end
class Array #:nodoc:
alias_method :blank?, :empty?
end
class Hash #:nodoc:
alias_method :blank?, :empty?
end
class String #:nodoc:
def blank?
empty? || strip.empty?
end
end
class Numeric #:nodoc:
def blank?
false
end
end
end

View file

@ -1,6 +1,8 @@
# =begin rdoc
# encode.rb
# = Text Encoding class
=end
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
# #
@ -56,6 +58,8 @@ module TMail
end end
def decoded( eol = "\n", charset = 'e', dest = nil ) def decoded( eol = "\n", charset = 'e', dest = nil )
# Turn the E-Mail into a string and return it with all
# encoded characters decoded. alias for to_s
accept_strategy Decoder, eol, charset, dest accept_strategy Decoder, eol, charset, dest
end end
@ -63,7 +67,7 @@ module TMail
def accept_strategy( klass, eol, charset, dest = nil ) def accept_strategy( klass, eol, charset, dest = nil )
dest ||= '' dest ||= ''
accept klass.new(create_dest(dest), charset, eol) accept klass.new( create_dest(dest), charset, eol )
dest dest
end end
@ -141,6 +145,7 @@ module TMail
end end
def kv_pair( k, v ) def kv_pair( k, v )
v = dquote(v) unless token_safe?(v)
@f << k << '=' << v @f << k << '=' << v
end end
@ -190,9 +195,18 @@ module TMail
@f = StrategyInterface.create_dest(dest) @f = StrategyInterface.create_dest(dest)
@opt = OPTIONS[$KCODE] @opt = OPTIONS[$KCODE]
@eol = eol @eol = eol
@preserve_quotes = true
reset reset
end end
def preserve_quotes=( bool )
@preserve_quotes
end
def preserve_quotes
@preserve_quotes
end
def normalize_encoding( str ) def normalize_encoding( str )
if @opt if @opt
then NKF.nkf(@opt, str) then NKF.nkf(@opt, str)

View file

@ -1,5 +1,10 @@
=begin rdoc
= Header handling class
=end
# RFC #822 ftp://ftp.isi.edu/in-notes/rfc822.txt
# #
# header.rb
# #
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
@ -76,6 +81,7 @@ module TMail
@illegal = false @illegal = false
@parsed = false @parsed = false
if intern if intern
@parsed = true @parsed = true
parse_init parse_init
@ -129,7 +135,7 @@ module TMail
include StrategyInterface include StrategyInterface
def accept( strategy, dummy1 = nil, dummy2 = nil ) def accept( strategy )
ensure_parsed ensure_parsed
do_accept strategy do_accept strategy
strategy.terminate strategy.terminate
@ -207,6 +213,7 @@ module TMail
end end
def do_parse def do_parse
quote_boundary
obj = Parser.parse(self.class::PARSE_TYPE, @body, @comments) obj = Parser.parse(self.class::PARSE_TYPE, @body, @comments)
set obj if obj set obj if obj
end end
@ -739,12 +746,17 @@ module TMail
def params def params
ensure_parsed ensure_parsed
unless @params.blank?
@params.each do |k, v|
@params[k] = unquote(v)
end
end
@params @params
end end
def []( key ) def []( key )
ensure_parsed ensure_parsed
@params and @params[key] @params and unquote(@params[key])
end end
def []=( key, val ) def []=( key, val )
@ -835,12 +847,17 @@ module TMail
def params def params
ensure_parsed ensure_parsed
unless @params.blank?
@params.each do |k, v|
@params[k] = unquote(v)
end
end
@params @params
end end
def []( key ) def []( key )
ensure_parsed ensure_parsed
@params and @params[key] @params and unquote(@params[key])
end end
def []=( key, val ) def []=( key, val )
@ -867,7 +884,7 @@ module TMail
@params.each do |k,v| @params.each do |k,v|
strategy.meta ';' strategy.meta ';'
strategy.space strategy.space
strategy.kv_pair k, v strategy.kv_pair k, unquote(v)
end end
end end

View file

@ -0,0 +1,540 @@
=begin rdoc
= Facade.rb Provides an interface to the TMail object
=end
#--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
# with permission of Minero Aoki.
#++
require 'tmail/utils'
module TMail
class Mail
def header_string( name, default = nil )
h = @header[name.downcase] or return default
h.to_s
end
###
### attributes
###
include TextUtils
def set_string_array_attr( key, strs )
strs.flatten!
if strs.empty?
@header.delete key.downcase
else
store key, strs.join(', ')
end
strs
end
private :set_string_array_attr
def set_string_attr( key, str )
if str
store key, str
else
@header.delete key.downcase
end
str
end
private :set_string_attr
def set_addrfield( name, arg )
if arg
h = HeaderField.internal_new(name, @config)
h.addrs.replace [arg].flatten
@header[name] = h
else
@header.delete name
end
arg
end
private :set_addrfield
def addrs2specs( addrs )
return nil unless addrs
list = addrs.map {|addr|
if addr.address_group?
then addr.map {|a| a.spec }
else addr.spec
end
}.flatten
return nil if list.empty?
list
end
private :addrs2specs
#
# date time
#
def date( default = nil )
if h = @header['date']
h.date
else
default
end
end
def date=( time )
if time
store 'Date', time2str(time)
else
@header.delete 'date'
end
time
end
def strftime( fmt, default = nil )
if t = date
t.strftime(fmt)
else
default
end
end
#
# destination
#
def to_addrs( default = nil )
if h = @header['to']
h.addrs
else
default
end
end
def cc_addrs( default = nil )
if h = @header['cc']
h.addrs
else
default
end
end
def bcc_addrs( default = nil )
if h = @header['bcc']
h.addrs
else
default
end
end
def to_addrs=( arg )
set_addrfield 'to', arg
end
def cc_addrs=( arg )
set_addrfield 'cc', arg
end
def bcc_addrs=( arg )
set_addrfield 'bcc', arg
end
def to( default = nil )
addrs2specs(to_addrs(nil)) || default
end
def cc( default = nil )
addrs2specs(cc_addrs(nil)) || default
end
def bcc( default = nil )
addrs2specs(bcc_addrs(nil)) || default
end
def to=( *strs )
set_string_array_attr 'To', strs
end
def cc=( *strs )
set_string_array_attr 'Cc', strs
end
def bcc=( *strs )
set_string_array_attr 'Bcc', strs
end
#
# originator
#
def from_addrs( default = nil )
if h = @header['from']
h.addrs
else
default
end
end
def from_addrs=( arg )
set_addrfield 'from', arg
end
def from( default = nil )
addrs2specs(from_addrs(nil)) || default
end
def from=( *strs )
set_string_array_attr 'From', strs
end
def friendly_from( default = nil )
h = @header['from']
a, = h.addrs
return default unless a
return a.phrase if a.phrase
return h.comments.join(' ') unless h.comments.empty?
a.spec
end
def reply_to_addrs( default = nil )
if h = @header['reply-to']
h.addrs
else
default
end
end
def reply_to_addrs=( arg )
set_addrfield 'reply-to', arg
end
def reply_to( default = nil )
addrs2specs(reply_to_addrs(nil)) || default
end
def reply_to=( *strs )
set_string_array_attr 'Reply-To', strs
end
def sender_addr( default = nil )
f = @header['sender'] or return default
f.addr or return default
end
def sender_addr=( addr )
if addr
h = HeaderField.internal_new('sender', @config)
h.addr = addr
@header['sender'] = h
else
@header.delete 'sender'
end
addr
end
def sender( default )
f = @header['sender'] or return default
a = f.addr or return default
a.spec
end
def sender=( str )
set_string_attr 'Sender', str
end
#
# subject
#
def subject( default = nil )
if h = @header['subject']
h.body
else
default
end
end
alias quoted_subject subject
def subject=( str )
set_string_attr 'Subject', str
end
#
# identity & threading
#
def message_id( default = nil )
if h = @header['message-id']
h.id || default
else
default
end
end
def message_id=( str )
set_string_attr 'Message-Id', str
end
def in_reply_to( default = nil )
if h = @header['in-reply-to']
h.ids
else
default
end
end
def in_reply_to=( *idstrs )
set_string_array_attr 'In-Reply-To', idstrs
end
def references( default = nil )
if h = @header['references']
h.refs
else
default
end
end
def references=( *strs )
set_string_array_attr 'References', strs
end
#
# MIME headers
#
def mime_version( default = nil )
if h = @header['mime-version']
h.version || default
else
default
end
end
def mime_version=( m, opt = nil )
if opt
if h = @header['mime-version']
h.major = m
h.minor = opt
else
store 'Mime-Version', "#{m}.#{opt}"
end
else
store 'Mime-Version', m
end
m
end
def content_type( default = nil )
if h = @header['content-type']
h.content_type || default
else
default
end
end
def main_type( default = nil )
if h = @header['content-type']
h.main_type || default
else
default
end
end
def sub_type( default = nil )
if h = @header['content-type']
h.sub_type || default
else
default
end
end
def set_content_type( str, sub = nil, param = nil )
if sub
main, sub = str, sub
else
main, sub = str.split(%r</>, 2)
raise ArgumentError, "sub type missing: #{str.inspect}" unless sub
end
if h = @header['content-type']
h.main_type = main
h.sub_type = sub
h.params.clear
else
store 'Content-Type', "#{main}/#{sub}"
end
@header['content-type'].params.replace param if param
str
end
alias content_type= set_content_type
def type_param( name, default = nil )
if h = @header['content-type']
h[name] || default
else
default
end
end
def charset( default = nil )
if h = @header['content-type']
h['charset'] or default
else
default
end
end
def charset=( str )
if str
if h = @header[ 'content-type' ]
h['charset'] = str
else
store 'Content-Type', "text/plain; charset=#{str}"
end
end
str
end
def transfer_encoding( default = nil )
if h = @header['content-transfer-encoding']
h.encoding || default
else
default
end
end
def transfer_encoding=( str )
set_string_attr 'Content-Transfer-Encoding', str
end
alias encoding transfer_encoding
alias encoding= transfer_encoding=
alias content_transfer_encoding transfer_encoding
alias content_transfer_encoding= transfer_encoding=
def disposition( default = nil )
if h = @header['content-disposition']
h.disposition || default
else
default
end
end
alias content_disposition disposition
def set_disposition( str, params = nil )
if h = @header['content-disposition']
h.disposition = str
h.params.clear
else
store('Content-Disposition', str)
h = @header['content-disposition']
end
h.params.replace params if params
end
alias disposition= set_disposition
alias set_content_disposition set_disposition
alias content_disposition= set_disposition
def disposition_param( name, default = nil )
if h = @header['content-disposition']
h[name] || default
else
default
end
end
###
### utils
###
def create_reply
mail = TMail::Mail.parse('')
mail.subject = 'Re: ' + subject('').sub(/\A(?:\[[^\]]+\])?(?:\s*Re:)*\s*/i, '')
mail.to_addrs = reply_addresses([])
mail.in_reply_to = [message_id(nil)].compact
mail.references = references([]) + [message_id(nil)].compact
mail.mime_version = '1.0'
mail
end
def base64_encode
store 'Content-Transfer-Encoding', 'Base64'
self.body = Base64.folding_encode(self.body)
end
def base64_decode
if /base64/i === self.transfer_encoding('')
store 'Content-Transfer-Encoding', '8bit'
self.body = Base64.decode(self.body, @config.strict_base64decode?)
end
end
def destinations( default = nil )
ret = []
%w( to cc bcc ).each do |nm|
if h = @header[nm]
h.addrs.each {|i| ret.push i.address }
end
end
ret.empty? ? default : ret
end
def each_destination( &block )
destinations([]).each do |i|
if Address === i
yield i
else
i.each(&block)
end
end
end
alias each_dest each_destination
def reply_addresses( default = nil )
reply_to_addrs(nil) or from_addrs(nil) or default
end
def error_reply_addresses( default = nil )
if s = sender(nil)
[s]
else
from_addrs(default)
end
end
def multipart?
main_type('').downcase == 'multipart'
end
end # class Mail
end # module TMail

View file

@ -1,6 +1,8 @@
# =begin rdoc
# mail.rb
# = Mail class
=end
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
# #
@ -27,7 +29,7 @@
# with permission of Minero Aoki. # with permission of Minero Aoki.
#++ #++
require 'tmail/facade' require 'tmail/interface'
require 'tmail/encode' require 'tmail/encode'
require 'tmail/header' require 'tmail/header'
require 'tmail/port' require 'tmail/port'
@ -37,7 +39,6 @@ require 'tmail/attachments'
require 'tmail/quoting' require 'tmail/quoting'
require 'socket' require 'socket'
module TMail module TMail
class Mail class Mail
@ -53,6 +54,7 @@ module TMail
def parse( str ) def parse( str )
new(StringPort.new(str)) new(StringPort.new(str))
end end
end end
def initialize( port = nil, conf = DEFAULT_CONFIG ) def initialize( port = nil, conf = DEFAULT_CONFIG )
@ -355,6 +357,19 @@ module TMail
end end
def body=( str ) def body=( str )
# Sets the body of the email to a new (encoded) string.
#
# We also reparses the email if the body is ever reassigned, this is a performance hit, however when
# you assign the body, you usually want to be able to make sure that you can access the attachments etc.
#
# Usage:
#
# mail.body = "Hello, this is\nthe body text"
# # => "Hello, this is\nthe body"
# mail.body
# # => "Hello, this is\nthe body"
@body_parsed = false
parse_body(StringInput.new(str))
parse_body parse_body
@body_port.wopen {|f| f.write str } @body_port.wopen {|f| f.write str }
str str

View file

@ -1,6 +1,8 @@
# =begin rdoc
# mailbox.rb
# = Mailbox and Mbox interaction class
=end
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
# #

View file

@ -1,6 +1,8 @@
# =begin rdoc
# net.rb
# = Net provides SMTP wrapping
=end
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
# #

View file

@ -1,6 +1,8 @@
# =begin rdoc
# obsolete.rb
# = Obsolete methods that are depriciated
=end
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
# #

View file

@ -0,0 +1,381 @@
#
# parser.y
#
# Copyright (c) 1998-2007 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU Lesser General Public License version 2.1.
#
class TMail::Parser
options no_result_var
rule
content : DATETIME datetime { val[1] }
| RECEIVED received { val[1] }
| MADDRESS addrs_TOP { val[1] }
| RETPATH retpath { val[1] }
| KEYWORDS keys { val[1] }
| ENCRYPTED enc { val[1] }
| MIMEVERSION version { val[1] }
| CTYPE ctype { val[1] }
| CENCODING cencode { val[1] }
| CDISPOSITION cdisp { val[1] }
| ADDRESS addr_TOP { val[1] }
| MAILBOX mbox { val[1] }
datetime : day DIGIT ATOM DIGIT hour zone
# 0 1 2 3 4 5
# date month year
{
t = Time.gm(val[3].to_i, val[2], val[1].to_i, 0, 0, 0)
(t + val[4] - val[5]).localtime
}
day : /* none */
| ATOM ','
hour : DIGIT ':' DIGIT
{
(val[0].to_i * 60 * 60) +
(val[2].to_i * 60)
}
| DIGIT ':' DIGIT ':' DIGIT
{
(val[0].to_i * 60 * 60) +
(val[2].to_i * 60) +
(val[4].to_i)
}
zone : ATOM
{
timezone_string_to_unixtime(val[0])
}
received : from by via with id for received_datetime
{
val
}
from : /* none */
| FROM received_domain
{
val[1]
}
by : /* none */
| BY received_domain
{
val[1]
}
received_domain
: domain
{
join_domain(val[0])
}
| domain '@' domain
{
join_domain(val[2])
}
| domain DOMLIT
{
join_domain(val[0])
}
via : /* none */
| VIA ATOM
{
val[1]
}
with : /* none */
{
[]
}
| with WITH ATOM
{
val[0].push val[2]
val[0]
}
id : /* none */
| ID msgid
{
val[1]
}
| ID ATOM
{
val[1]
}
for : /* none */
| FOR received_addrspec
{
val[1]
}
received_addrspec
: routeaddr
{
val[0].spec
}
| spec
{
val[0].spec
}
received_datetime
: /* none */
| ';' datetime
{
val[1]
}
addrs_TOP : addrs
| group_bare
| addrs commas group_bare
addr_TOP : mbox
| group
| group_bare
retpath : addrs_TOP
| '<' '>' { [ Address.new(nil, nil) ] }
addrs : addr
{
val
}
| addrs commas addr
{
val[0].push val[2]
val[0]
}
addr : mbox
| group
mboxes : mbox
{
val
}
| mboxes commas mbox
{
val[0].push val[2]
val[0]
}
mbox : spec
| routeaddr
| addr_phrase routeaddr
{
val[1].phrase = Decoder.decode(val[0])
val[1]
}
group : group_bare ';'
group_bare: addr_phrase ':' mboxes
{
AddressGroup.new(val[0], val[2])
}
| addr_phrase ':' { AddressGroup.new(val[0], []) }
addr_phrase
: local_head { val[0].join('.') }
| addr_phrase local_head { val[0] << ' ' << val[1].join('.') }
routeaddr : '<' routes spec '>'
{
val[2].routes.replace val[1]
val[2]
}
| '<' spec '>'
{
val[1]
}
routes : at_domains ':'
at_domains: '@' domain { [ val[1].join('.') ] }
| at_domains ',' '@' domain { val[0].push val[3].join('.'); val[0] }
spec : local '@' domain { Address.new( val[0], val[2] ) }
| local { Address.new( val[0], nil ) }
local: local_head
| local_head '.' { val[0].push ''; val[0] }
local_head: word
{ val }
| local_head dots word
{
val[1].times do
val[0].push ''
end
val[0].push val[2]
val[0]
}
domain : domword
{ val }
| domain dots domword
{
val[1].times do
val[0].push ''
end
val[0].push val[2]
val[0]
}
dots : '.' { 0 }
| '.' '.' { 1 }
word : atom
| QUOTED
| DIGIT
domword : atom
| DOMLIT
| DIGIT
commas : ','
| commas ','
msgid : '<' spec '>'
{
val[1] = val[1].spec
val.join('')
}
keys : phrase { val }
| keys ',' phrase { val[0].push val[2]; val[0] }
phrase : word
| phrase word { val[0] << ' ' << val[1] }
enc : word
{
val.push nil
val
}
| word word
{
val
}
version : DIGIT '.' DIGIT
{
[ val[0].to_i, val[2].to_i ]
}
ctype : TOKEN '/' TOKEN params opt_semicolon
{
[ val[0].downcase, val[2].downcase, decode_params(val[3]) ]
}
| TOKEN params opt_semicolon
{
[ val[0].downcase, nil, decode_params(val[1]) ]
}
params : /* none */
{
{}
}
| params ';' TOKEN '=' QUOTED
{
val[0][ val[2].downcase ] = ('"' + val[4].to_s + '"')
val[0]
}
| params ';' TOKEN '=' TOKEN
{
val[0][ val[2].downcase ] = val[4]
val[0]
}
cencode : TOKEN
{
val[0].downcase
}
cdisp : TOKEN params opt_semicolon
{
[ val[0].downcase, decode_params(val[1]) ]
}
opt_semicolon
:
| ';'
atom : ATOM
| FROM
| BY
| VIA
| WITH
| ID
| FOR
end
---- header
#
# parser.rb
#
# Copyright (c) 1998-2007 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU Lesser General Public License version 2.1.
#
require 'tmail/scanner'
require 'tmail/utils'
---- inner
include TextUtils
def self.parse( ident, str, cmt = nil )
new.parse(ident, str, cmt)
end
MAILP_DEBUG = false
def initialize
self.debug = MAILP_DEBUG
end
def debug=( flag )
@yydebug = flag && Racc_debug_parser
@scanner_debug = flag
end
def debug
@yydebug
end
def parse( ident, str, comments = nil )
@scanner = Scanner.new(str, ident, comments)
@scanner.debug = @scanner_debug
@first = [ident, ident]
result = yyparse(self, :parse_in)
comments.map! {|c| to_kcode(c) } if comments
result
end
private
def parse_in( &block )
yield @first
@scanner.scan(&block)
end
def on_error( t, val, vstack )
raise SyntaxError, "parse error on token #{racc_token2str t}"
end

View file

@ -1,6 +1,8 @@
# =begin rdoc
# port.rb
# = Port class
=end
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
# #

View file

@ -1,3 +1,8 @@
=begin rdoc
= Quoting methods
=end
module TMail module TMail
class Mail class Mail
def subject(to_charset = 'utf-8') def subject(to_charset = 'utf-8')
@ -8,6 +13,12 @@ module TMail
from_charset = sub_header("content-type", "charset") from_charset = sub_header("content-type", "charset")
case (content_transfer_encoding || "7bit").downcase case (content_transfer_encoding || "7bit").downcase
when "quoted-printable" when "quoted-printable"
# the default charset is set to iso-8859-1 instead of 'us-ascii'.
# This is needed as many mailer do not set the charset but send in ISO. This is only used if no charset is set.
if !from_charset.blank? && from_charset.downcase == 'us-ascii'
from_charset = 'iso-8859-1'
end
Unquoter.unquote_quoted_printable_and_convert_to(quoted_body, Unquoter.unquote_quoted_printable_and_convert_to(quoted_body,
to_charset, from_charset, true) to_charset, from_charset, true)
when "base64" when "base64"
@ -78,7 +89,7 @@ module TMail
end end
def unquote_base64_and_convert_to(text, to, from) def unquote_base64_and_convert_to(text, to, from)
convert_to(Base64.decode(text).first, to, from) convert_to(Base64.decode(text), to, from)
end end
begin begin

View file

@ -1,6 +1,8 @@
# =begin rdoc
# scanner.rb
# = Scanner for TMail
=end
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
# #

View file

@ -44,7 +44,7 @@ module TMail
} }
alnum = 'a-zA-Z0-9' alnum = 'a-zA-Z0-9'
atomsyms = %q[ _#!$%&`'*+-{|}~^@/=? ].strip atomsyms = %q[ _#!$%&`'*+-{|}~^/=? ].strip
tokensyms = %q[ _#!$%&`'*+-{|}~^@. ].strip tokensyms = %q[ _#!$%&`'*+-{|}~^@. ].strip
atomchars = alnum + Regexp.quote(atomsyms) atomchars = alnum + Regexp.quote(atomsyms)

View file

@ -1,6 +1,8 @@
# =begin rdoc
# stringio.rb
# = String handling class
=end
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
# #
@ -218,7 +220,7 @@ class StringOutput#:nodoc:
alias pos size alias pos size
def inspect def inspect
"#<#{self.class}:#{@dest ? 'open' : 'closed'},#{id}>" "#<#{self.class}:#{@dest ? 'open' : 'closed'},#{object_id}>"
end end
def print( *args ) def print( *args )

View file

@ -1,6 +1,8 @@
# =begin rdoc
# utils.rb
# = General Purpose TMail Utilities
=end
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
# #
@ -52,8 +54,8 @@ module TMail
@uniq = 0 @uniq = 0
module TextUtils module TextUtils
# Defines characters per RFC that are OK for TOKENs, ATOMs, PHRASEs and CONTROL characters.
aspecial = '()<>[]:;.\\,"' aspecial = '()<>[]:;.\\,"'
tspecial = '()<>[];:\\,"/?=' tspecial = '()<>[];:\\,"/?='
@ -66,30 +68,49 @@ module TMail
CONTROL_CHAR = /[#{control}]/n CONTROL_CHAR = /[#{control}]/n
def atom_safe?( str ) def atom_safe?( str )
# Returns true if the string supplied is free from characters not allowed as an ATOM
not ATOM_UNSAFE === str not ATOM_UNSAFE === str
end end
def quote_atom( str ) def quote_atom( str )
# If the string supplied has ATOM unsafe characters in it, will return the string quoted
# in double quotes, otherwise returns the string unmodified
(ATOM_UNSAFE === str) ? dquote(str) : str (ATOM_UNSAFE === str) ? dquote(str) : str
end end
def quote_phrase( str ) def quote_phrase( str )
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
# in double quotes, otherwise returns the string unmodified
(PHRASE_UNSAFE === str) ? dquote(str) : str (PHRASE_UNSAFE === str) ? dquote(str) : str
end end
def token_safe?( str ) def token_safe?( str )
# Returns true if the string supplied is free from characters not allowed as a TOKEN
not TOKEN_UNSAFE === str not TOKEN_UNSAFE === str
end end
def quote_token( str ) def quote_token( str )
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
# in double quotes, otherwise returns the string unmodified
(TOKEN_UNSAFE === str) ? dquote(str) : str (TOKEN_UNSAFE === str) ? dquote(str) : str
end end
def dquote( str ) def dquote( str )
'"' + str.gsub(/["\\]/n) {|s| '\\' + s } + '"' # Wraps supplied string in double quotes unless it is already wrapped
# Returns double quoted string
unless str =~ /^".*?"$/
'"' + str.gsub(/["\\]/n) {|s| '\\' + s } + '"'
else
str
end
end end
private :dquote private :dquote
def unquote( str )
# Unwraps supplied string from inside double quotes
# Returns unquoted string
str =~ /^"(.*?)"$/ ? $1 : str
end
def join_domain( arr ) def join_domain( arr )
arr.map {|i| arr.map {|i|
@ -149,6 +170,7 @@ module TMail
} }
def timezone_string_to_unixtime( str ) def timezone_string_to_unixtime( str )
# Takes a time zone string from an EMail and converts it to Unix Time (seconds)
if m = /([\+\-])(\d\d?)(\d\d)/.match(str) if m = /([\+\-])(\d\d?)(\d\d)/.match(str)
sec = (m[2].to_i * 60 + m[3].to_i) * 60 sec = (m[2].to_i * 60 + m[3].to_i) * 60
m[1] == '-' ? -sec : sec m[1] == '-' ? -sec : sec
@ -233,6 +255,27 @@ module TMail
end end
end end
def quote_boundary
# Make sure the Content-Type boundary= parameter is quoted if it contains illegal characters
# (to ensure any special characters in the boundary text are escaped from the parser
# (such as = in MS Outlook's boundary text))
if @body =~ /^(.*)boundary=(.*)$/m
preamble = $1
remainder = $2
if remainder =~ /;/
remainder =~ /^(.*)(;.*)$/m
boundary_text = $1
post = $2.chomp
else
boundary_text = remainder.chomp
end
if boundary_text =~ /[\/\?\=]/
boundary_text = "\"#{boundary_text}\"" unless boundary_text =~ /^".*?"$/
@body = "#{preamble}boundary=#{boundary_text}#{post}"
end
end
end
end end
end end

View file

@ -1,5 +1,5 @@
# #
# base64.rb # version.rb
# #
#-- #--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net> # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
@ -27,45 +27,12 @@
# with permission of Minero Aoki. # with permission of Minero Aoki.
#++ #++
module TMail module TMail #:nodoc:
module VERSION #:nodoc:
module Base64 MAJOR = 1
MINOR = 1
module_function TINY = 1
def rb_folding_encode( str, eol = "\n", limit = 60 )
[str].pack('m')
end
def rb_encode( str )
[str].pack('m').tr( "\r\n", '' )
end
def rb_decode( str, strict = false )
str.unpack('m')
end
begin
require 'tmail/base64.so'
alias folding_encode c_folding_encode
alias encode c_encode
alias decode c_decode
class << self
alias folding_encode c_folding_encode
alias encode c_encode
alias decode c_decode
end
rescue LoadError
alias folding_encode rb_folding_encode
alias encode rb_encode
alias decode rb_decode
class << self
alias folding_encode rb_folding_encode
alias encode rb_encode
alias decode rb_decode
end
end
STRING = [MAJOR, MINOR, TINY].join('.')
end end
end end

View file

@ -1,3 +0,0 @@
require 'tmail/info'
require 'tmail/mail'
require 'tmail/mailbox'

View file

@ -1,8 +1,8 @@
module ActionMailer module ActionMailer
module VERSION #:nodoc: module VERSION #:nodoc:
MAJOR = 1 MAJOR = 2
MINOR = 3 MINOR = 0
TINY = 5 TINY = 2
STRING = [MAJOR, MINOR, TINY].join('.') STRING = [MAJOR, MINOR, TINY].join('.')
end end

View file

@ -0,0 +1 @@
require 'action_mailer'

View file

@ -2,6 +2,7 @@ require 'test/unit'
$:.unshift "#{File.dirname(__FILE__)}/../lib" $:.unshift "#{File.dirname(__FILE__)}/../lib"
require 'action_mailer' require 'action_mailer'
require 'action_mailer/test_case'
# Show backtraces for deprecated behavior for quicker cleanup. # Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true ActiveSupport::Deprecation.debug = true
@ -28,3 +29,21 @@ class Net::SMTP
yield MockSMTP.new yield MockSMTP.new
end end
end end
# Wrap tests that use Mocha and skip if unavailable.
def uses_mocha(test_name)
gem 'mocha', ">=0.5"
require 'stubba'
yield
rescue Gem::LoadError
$stderr.puts "Skipping #{test_name} tests (Mocha >= 0.5 is required). `gem install mocha` and try again."
end
def set_delivery_method(delivery_method)
@old_delivery_method = ActionMailer::Base.delivery_method
ActionMailer::Base.delivery_method = delivery_method
end
def restore_delivery_method
ActionMailer::Base.delivery_method = @old_delivery_method
end

View file

@ -0,0 +1,51 @@
require "#{File.dirname(__FILE__)}/abstract_unit"
class DefaultDeliveryMethodMailer < ActionMailer::Base
end
class NonDefaultDeliveryMethodMailer < ActionMailer::Base
self.delivery_method = :sendmail
end
class ActionMailerBase_delivery_method_Test < Test::Unit::TestCase
def setup
set_delivery_method :smtp
end
def teardown
restore_delivery_method
end
def test_should_be_the_default_smtp
assert_equal :smtp, ActionMailer::Base.delivery_method
end
end
class DefaultDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase
def setup
set_delivery_method :smtp
end
def teardown
restore_delivery_method
end
def test_should_be_the_default_smtp
assert_equal :smtp, DefaultDeliveryMethodMailer.delivery_method
end
end
class NonDefaultDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase
def setup
set_delivery_method :smtp
end
def teardown
restore_delivery_method
end
def test_should_be_the_set_delivery_method
assert_equal :sendmail, NonDefaultDeliveryMethodMailer.delivery_method
end
end

View file

@ -0,0 +1 @@
first mail

View file

@ -1 +0,0 @@
first mail

View file

@ -0,0 +1 @@
So, <%= example_format(@text) %>

View file

@ -0,0 +1 @@
Hello, <%= person_name %>. Thanks for registering!

View file

@ -1 +0,0 @@
Hello, <%= person_name %>. Thanks for registering!

View file

@ -0,0 +1 @@
This message brought to you by <%= name_of_the_mailer_class %>.

View file

@ -1 +0,0 @@
This message brought to you by <%= name_of_the_mailer_class %>.

View file

@ -0,0 +1,5 @@
From "Romeo and Juliet":
<%= block_format @text %>
Good ol' Shakespeare.

View file

@ -1,5 +0,0 @@
From "Romeo and Juliet":
<%= block_format @text %>
Good ol' Shakespeare.

View file

@ -1 +0,0 @@
So, <%= test_format(@text) %>

View file

@ -1,5 +1,5 @@
module TestHelper module ExampleHelper
def test_format(text) def example_format(text)
"<em><strong><small>#{text}</small></strong></em>" "<em><strong><small>#{text}</small></strong></em>"
end end
end end

View file

@ -0,0 +1 @@
Have a lovely picture, from me. Enjoy!

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

File diff suppressed because one or more lines are too long

View file

@ -1,34 +0,0 @@
From xxx@xxxx.com Wed Apr 27 14:15:31 2005
Mime-Version: 1.0 (Apple Message framework v619.2)
To: xxxxx@xxxxx <matmail>
Message-Id: <416eaebec6d333ec6939eaf8a7d80724@xxxxx>
Content-Type: multipart/alternative;
boundary=Apple-Mail-5-1037861608
From: xxxxx@xxxxx <xxxxx@xxxxx>
Subject: worse when you use them.
Date: Wed, 27 Apr 2005 14:15:31 -0700
--Apple-Mail-5-1037861608
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
format=flowed
XXXXX Xxxxx
--Apple-Mail-5-1037861608
Content-Transfer-Encoding: 7bit
Content-Type: text/enriched;
charset=US-ASCII
<bold>XXXXX Xxxxx</bold>
--Apple-Mail-5-1037861608--

View file

@ -0,0 +1,104 @@
Return-Path: <mikel.other@baci>
Received: from some.isp.com by baci with ESMTP id 632BD5758 for <mikel.lindsaar@baci>; Sun, 21 Oct 2007 19:38:21 +1000
Date: Sun, 21 Oct 2007 19:38:13 +1000
From: Mikel Lindsaar <mikel.other@baci>
Reply-To: Mikel Lindsaar <mikel.other@baci>
To: mikel.lindsaar@baci
Message-Id: <009601c813c6$19df3510$0437d30a@mikel091a>
Subject: Testing outlook
Mime-Version: 1.0
Content-Type: multipart/alternative; boundary=----=_NextPart_000_0093_01C81419.EB75E850
Delivered-To: mikel.lindsaar@baci
X-Mimeole: Produced By Microsoft MimeOLE V6.00.2900.3138
X-Msmail-Priority: Normal
This is a multi-part message in MIME format.
------=_NextPart_000_0093_01C81419.EB75E850
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: Quoted-printable
Hello
This is an outlook test
So there.
Me.
------=_NextPart_000_0093_01C81419.EB75E850
Content-Type: text/html; charset=iso-8859-1
Content-Transfer-Encoding: Quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; =
charset=3Diso-8859-1">
<META content=3D"MSHTML 6.00.6000.16525" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=3D#ffffff>
<DIV><FONT face=3DArial size=3D2>Hello</FONT></DIV>
<DIV><FONT face=3DArial size=3D2><STRONG>This is an outlook=20
test</STRONG></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><STRONG></STRONG></FONT>&nbsp;</DIV>
<DIV><FONT face=3DArial size=3D2><STRONG>So there.</STRONG></FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT>&nbsp;</DIV>
<DIV><FONT face=3DArial size=3D2>Me.</FONT></DIV></BODY></HTML>
------=_NextPart_000_0093_01C81419.EB75E850--
Return-Path: <mikel.other@baci>
Received: from some.isp.com by baci with ESMTP id 632BD5758 for <mikel.lindsaar@baci>; Sun, 21 Oct 2007 19:38:21 +1000
Date: Sun, 21 Oct 2007 19:38:13 +1000
From: Mikel Lindsaar <mikel.other@baci>
Reply-To: Mikel Lindsaar <mikel.other@baci>
To: mikel.lindsaar@baci
Message-Id: <009601c813c6$19df3510$0437d30a@mikel091a>
Subject: Testing outlook
Mime-Version: 1.0
Content-Type: multipart/alternative; boundary=----=_NextPart_000_0093_01C81419.EB75E850
Delivered-To: mikel.lindsaar@baci
X-Mimeole: Produced By Microsoft MimeOLE V6.00.2900.3138
X-Msmail-Priority: Normal
This is a multi-part message in MIME format.
------=_NextPart_000_0093_01C81419.EB75E850
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: Quoted-printable
Hello
This is an outlook test
So there.
Me.
------=_NextPart_000_0093_01C81419.EB75E850
Content-Type: text/html; charset=iso-8859-1
Content-Transfer-Encoding: Quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; =
charset=3Diso-8859-1">
<META content=3D"MSHTML 6.00.6000.16525" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=3D#ffffff>
<DIV><FONT face=3DArial size=3D2>Hello</FONT></DIV>
<DIV><FONT face=3DArial size=3D2><STRONG>This is an outlook=20
test</STRONG></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><STRONG></STRONG></FONT>&nbsp;</DIV>
<DIV><FONT face=3DArial size=3D2><STRONG>So there.</STRONG></FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT>&nbsp;</DIV>
<DIV><FONT face=3DArial size=3D2>Me.</FONT></DIV></BODY></HTML>
------=_NextPart_000_0093_01C81419.EB75E850--

View file

@ -0,0 +1,100 @@
From jamis@37signals.com Thu Feb 22 11:20:31 2007
Mime-Version: 1.0 (Apple Message framework v752.3)
Message-Id: <2CCE0408-10C7-4045-9B16-A1C11C31469B@37signals.com>
Content-Type: multipart/signed;
micalg=sha1;
boundary=Apple-Mail-42-587703407;
protocol="application/pkcs7-signature"
To: Jamis Buck <jamis@jamisbuck.org>
Subject: Testing attachments
From: Jamis Buck <jamis@37signals.com>
Date: Thu, 22 Feb 2007 11:20:31 -0700
--Apple-Mail-42-587703407
Content-Type: multipart/mixed;
boundary=Apple-Mail-41-587703287
--Apple-Mail-41-587703287
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
format=flowed
Here is a test of an attachment via email.
- Jamis
--Apple-Mail-41-587703287
Content-Transfer-Encoding: base64
Content-Type: image/png;
x-unix-mode=0644;
name=byo-ror-cover.png
Content-Disposition: inline;
filename=truncated.png
iVBORw0KGgoAAAANSUhEUgAAAKUAAADXCAYAAAB7wZEQAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz
AAALEgAACxIB0t1+/AAAABd0RVh0Q3JlYXRpb24gVGltZQAxLzI1LzIwMDeD9CJVAAAAGHRFWHRT
b2Z0d2FyZQBBZG9iZSBGaXJld29ya3NPsx9OAAAyBWlUWHRYTUw6Y29tLmFkb2JlLnhtcDw/eHBh
Y2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1l
dGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDQuMS1j
MDIwIDEuMjU1NzE2LCBUdWUgT2N0IDEwIDIwMDYgMjM6MTY6MzQiPgogICA8cmRmOlJERiB4bWxu
czpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAg
ICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXA9Imh0
dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eGFwOkNyZWF0b3JUb29sPkFk
b2JlIEZpcmV3b3JrcyBDUzM8L3hhcDpDcmVhdG9yVG9vbD4KICAgICAgICAgPHhhcDpDcmVhdGVE
YXRlPjIwMDctMDEtMjVUMDU6Mjg6MjFaPC94YXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhhcDpN
b2RpZnlEYXRlPjIwMDctMDEtMjVUMDU6Mjg6MjFaPC94YXA6TW9kaWZ5RGF0ZT4KICAgICAgPC9y
ZGY6RGVzY3JpcHRpb24+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAg
ICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyI+CiAgICAg
ICAgIDxkYzpmb3JtYXQ+aW1hZ2UvcG5nPC9kYzpmb3JtYXQ+CiAgICAgIDwvcmRmOkRlc2NyaXB0
hhojpmnJMfaYFmSkXWg5PGCmHXVj/c9At0hSK2xGdd8F3muk0VFjb4f5Ue0ksQ8qAcq0delaXhdb
DjKNnF+3B3t9kObZYmk7AZgWYqO9anpR3wpM9sQ5XslB9a+kWyTtNb0fOmudzGHfPFBQDKesyycm
DBL7Cw5bXjIEuci+SSOm/LYnXDZu6iuPEj8lYBb+OU8xx1f9m+e5rhJiYKqjo5vHfiZp+VUkW9xc
Ufd6JHNWc47PkQqb9ie3SLEZB/ZqyAssiqURY+G35iOMZUrHbasHnb80QAPv9FHtAbJIyro7bi5b
ai2TEAKen5+LJNWrglZjm3UbZvt7KryA2J5b5J1jZF8kL6GzvG1Zqx54Y1y7J7n20wMOt9frG2sW
uwGP07kNz3732vf6bfvAvLldfS+9fts2euXY37D+R29FGZdlnhzV4TTFmPJduBP2RbNNua4rTqcT
Qt7Xy1KUB0AHSdP5AZQYvHZg7WD1XvYeMO1A9HhZPqMX5KXbMBrn2efxns/ee21674efxz4Tp/fq
2HZ648dgYaC1i3Vq1IbNPq3PvDTPezY9FaRISjvnzWqdgcWN8EJgjnNq+Z7ktOm9l2Nfth28EZi4
bG/we5JwxM+Tql47/D/X6b38I8/RyxvxPJrX6zvQbo3h9jyJx+C0ALX327QETHl5eYlaYCT5rPTb
+5/rAq26t3lKIxV/p88hq6ptngdgCzoPjJqndiLfc/6y5A14WeDFGNPct4iUsJBV2bYzLEV7m83s
6Rp63VPhHKC/g/LzaU9qexJRr56043JWinqAtfZqsSm1sjoznthl54dtCqv+uL4nIY+oYWuc3+nH
kGfn8b0HQpvOYLQAZUDanbJs3jQhITZEgdarZK+cO6ySlL13rut5nFaN23s7u3Snz6eRPTkCoc2/
Vp1zHfZVFpZ87FiMVLV1iqyK5rlzfji2GzjfDsodlD+Weo5UD4h6PwKqzQMqID0tq2VjjFVSMpis
ZLRAs7sePZBZAHI+gIanB8I7MD+femAceeUe2Kxa5jS950kZ1p5eNEdeX1+jFmSpZ+1EdWCsDcne
NPNgUHNw3aYpnzv9PGTX0uo94EtN9qq1rOdxe3kc79T8ukeHJJ8Fnxej6qlylbLLsjQLOy6Xy2a1
kefs/N+nM7+S7IG5/E5Yc7F003pWErLjbH0O5cGadiMptSB/DZ5U5DI9yeg5MFYyMj8lC/Y7/Xjq
OZlWcnpg9aQfXz2HRq+Wn5xOp6gN8tWq8R44e2pfyzLYemEgprst+XXk2Zj2nXlbsG05BprndTMv
C3QRaXczshhVsHnMgfYn80Y2g5JureA6wBasPeP7LkE/jvZMJAaf/g/U2RelHsisvan5FqweIAHg
Pwc7L68GxvVDAAAAAElFTkSuQmCC
--Apple-Mail-41-587703287--
--Apple-Mail-42-587703407
Content-Transfer-Encoding: base64
Content-Type: application/pkcs7-signature;
name=smime.p7s
Content-Disposition: attachment;
filename=smime.p7s
MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIIGJzCCAuAw
ggJJoAMCAQICEFjnFNYXwDEZRWY5EkfzopUwDQYJKoZIhvcNAQEFBQAwYjELMAkGA1UEBhMCWkEx
JTAjBgNVBAoTHFRoYXd0ZSBDb25zdWx0aW5nIChQdHkpIEx0ZC4xLDAqBgNVBAMTI1RoYXd0ZSBQ
ZXJzb25hbCBGcmVlbWFpbCBJc3N1aW5nIENBMB4XDTA2MDkxMjE3MDExMloXDTA3MDkxMjE3MDEx
MlowRTEfMB0GA1UEAxMWVGhhd3RlIEZyZWVtYWlsIE1lbWJlcjEiMCAGCSqGSIb3DQEJARYTamFt
aXNAMzdzaWduYWxzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO2A9JeOFIFJ
G6z8pTcAldrZ2nMe+Xb1tNrbHgoVzN/QhHXM4qst2Ml93cmFLjMmwG7P9RJeU4oNx+jTqVoBB7NV
Ne1/o56Do0KhfMZ9iUDQdPLbkZMq4EEpFMdm6PyM3muRKwPhj66iAWe/osCb8DowUK2f66vaRx0Z
Y0MQHIIrXE02Ta4IfAhIfPqBLkZ4WgTYBHN9vMdYea1jF0GO4gqGk1wqwb3yxv2QMYMbwJ6SI+k/
ZjkSR/OilTCBhwYLKoZIhvcNAQkQAgsxeKB2MGIxCzAJBgNVBAYTAlpBMSUwIwYDVQQKExxUaGF3
dGUgQ29uc3VsdGluZyAoUHR5KSBMdGQuMSwwKgYDVQQDEyNUaGF3dGUgUGVyc29uYWwgRnJlZW1h
aWwgSXNzdWluZyBDQQIQWOcU1hfAMRlFZjkSR/OilTANBgkqhkiG9w0BAQEFAASCAQCfwQiC3v6/
yleRDGv3bJ4nQYQ+c3mz3+mn3Xi6uU35n3piwxZZaWRdmLyiXPvU+QReHpSf3l2qsEZM3sdE0XF9
eRul/+QTFJcDNXOEAxG1zC2Gpz+6c6RrX4Ou12Pwkp+pNrZWTSY/mZgdqcArupOBcZi7qBjoWcy5
wb54dfvSSjrjmqLbkH/E8ww/6gGQuU/xXpAUZgUrTmQHrNKeIdSh5oDkOxFaFWvnmb8Z/2ixKqW/
Ux6WqamyvBtTs/5YBEtnpZOk+uVoscYEUBhU+DVJ2OSvTdXSivMtBdXmGTsG22k+P1NGUHi/A7ev
xPaO0uk4V8xyjNlN4HPuGpkrlXwPAAAAAAAA
--Apple-Mail-42-587703407--

View file

@ -0,0 +1 @@
second mail

View file

@ -1 +0,0 @@
second mail

View file

@ -0,0 +1,3 @@
Hello there,
Mr. <%= @recipient %>

View file

@ -1,3 +0,0 @@
Hello there,
Mr. <%= @recipient %>

View file

@ -0,0 +1 @@
let's go!

View file

@ -0,0 +1,6 @@
%p Hello there,
%p
Mr.
= @recipient
from haml

View file

@ -0,0 +1,6 @@
%p Hello there,
%p
Mr.
= @recipient
from haml

View file

@ -0,0 +1 @@
Ignored when searching for implicitly multipart parts.

View file

@ -1 +0,0 @@
Ignored when searching for implicitly multipart parts.

View file

@ -0,0 +1,10 @@
<html>
<body>
HTML formatted message to <strong><%= @recipient %></strong>.
</body>
</html>
<html>
<body>
HTML formatted message to <strong><%= @recipient %></strong>.
</body>
</html>

View file

@ -1,10 +0,0 @@
<html>
<body>
HTML formatted message to <strong><%= @recipient %></strong>.
</body>
</html>
<html>
<body>
HTML formatted message to <strong><%= @recipient %></strong>.
</body>
</html>

View file

@ -0,0 +1,2 @@
Plain text to <%= @recipient %>.
Plain text to <%= @recipient %>.

View file

@ -1,2 +0,0 @@
Plain text to <%= @recipient %>.
Plain text to <%= @recipient %>.

View file

@ -0,0 +1 @@
yaml to: <%= @recipient %>

View file

@ -1 +0,0 @@
yaml to: <%= @recipient %>

View file

@ -0,0 +1 @@
Hey Ho, <%= render :partial => "subtemplate" %>

View file

@ -0,0 +1,2 @@
xml.instruct!
xml.test

View file

@ -0,0 +1,2 @@
xml.instruct!
xml.test

View file

@ -0,0 +1,3 @@
Hello there,
Mr. <%= @recipient %>

View file

@ -1,3 +0,0 @@
Hello there,
Mr. <%= @recipient %>

View file

@ -0,0 +1,5 @@
Hello there,
Mr. <%= @recipient %>. Please see our greeting at <%= @welcome_url %> <%= welcome_url %>
<%= image_tag "somelogo.png" %>

View file

@ -1,3 +0,0 @@
Hello there,
Mr. <%= @recipient %>. Please see our greeting at <%= @welcome_url %>

View file

@ -8,7 +8,7 @@ end
class HelperMailer < ActionMailer::Base class HelperMailer < ActionMailer::Base
helper MailerHelper helper MailerHelper
helper :test helper :example
def use_helper(recipient) def use_helper(recipient)
recipients recipient recipients recipient
@ -16,7 +16,7 @@ class HelperMailer < ActionMailer::Base
from "tester@example.com" from "tester@example.com"
end end
def use_test_helper(recipient) def use_example_helper(recipient)
recipients recipient recipients recipient
subject "using helpers" subject "using helpers"
from "tester@example.com" from "tester@example.com"
@ -60,20 +60,24 @@ class MailerHelperTest < Test::Unit::TestCase
end end
def setup def setup
ActionMailer::Base.delivery_method = :test set_delivery_method :test
ActionMailer::Base.perform_deliveries = true ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries = [] ActionMailer::Base.deliveries = []
@recipient = 'test@localhost' @recipient = 'test@localhost'
end end
def teardown
restore_delivery_method
end
def test_use_helper def test_use_helper
mail = HelperMailer.create_use_helper(@recipient) mail = HelperMailer.create_use_helper(@recipient)
assert_match %r{Mr. Joe Person}, mail.encoded assert_match %r{Mr. Joe Person}, mail.encoded
end end
def test_use_test_helper def test_use_example_helper
mail = HelperMailer.create_use_test_helper(@recipient) mail = HelperMailer.create_use_example_helper(@recipient)
assert_match %r{<em><strong><small>emphasize me!}, mail.encoded assert_match %r{<em><strong><small>emphasize me!}, mail.encoded
end end

Some files were not shown because too many files have changed in this diff Show more