actionmailer_csi 2.3.5.p6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +370 -0
- data/MIT-LICENSE +21 -0
- data/README +149 -0
- data/Rakefile +99 -0
- data/install.rb +30 -0
- data/lib/action_mailer/adv_attr_accessor.rb +30 -0
- data/lib/action_mailer/base.rb +706 -0
- data/lib/action_mailer/helpers.rb +113 -0
- data/lib/action_mailer/mail_helper.rb +17 -0
- data/lib/action_mailer/part.rb +107 -0
- data/lib/action_mailer/part_container.rb +55 -0
- data/lib/action_mailer/quoting.rb +61 -0
- data/lib/action_mailer/test_case.rb +64 -0
- data/lib/action_mailer/test_helper.rb +68 -0
- data/lib/action_mailer/utils.rb +7 -0
- data/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb +1466 -0
- data/lib/action_mailer/vendor/text_format.rb +10 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb +426 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/attachments.rb +46 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/base64.rb +46 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/compat.rb +41 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/config.rb +67 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/core_extensions.rb +63 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/encode.rb +581 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb +960 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/index.rb +9 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb +1130 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/loader.rb +3 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb +578 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb +495 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/main.rb +6 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/mbox.rb +3 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/net.rb +248 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb +132 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/parser.rb +1478 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/port.rb +379 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/quoting.rb +118 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/require_arch.rb +58 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner.rb +49 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner_r.rb +261 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/stringio.rb +280 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/utils.rb +337 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail/version.rb +39 -0
- data/lib/action_mailer/vendor/tmail-1.2.3/tmail.rb +5 -0
- data/lib/action_mailer/vendor/tmail.rb +17 -0
- data/lib/action_mailer/version.rb +9 -0
- data/lib/action_mailer.rb +62 -0
- data/lib/actionmailer.rb +2 -0
- data/test/abstract_unit.rb +62 -0
- data/test/asset_host_test.rb +54 -0
- data/test/delivery_method_test.rb +51 -0
- data/test/fixtures/asset_host_mailer/email_with_asset.html.erb +1 -0
- data/test/fixtures/auto_layout_mailer/hello.html.erb +1 -0
- data/test/fixtures/auto_layout_mailer/multipart.text.html.erb +1 -0
- data/test/fixtures/auto_layout_mailer/multipart.text.plain.erb +1 -0
- data/test/fixtures/explicit_layout_mailer/logout.html.erb +1 -0
- data/test/fixtures/explicit_layout_mailer/signup.html.erb +1 -0
- data/test/fixtures/first_mailer/share.erb +1 -0
- data/test/fixtures/helper_mailer/use_example_helper.erb +1 -0
- data/test/fixtures/helper_mailer/use_helper.erb +1 -0
- data/test/fixtures/helper_mailer/use_helper_method.erb +1 -0
- data/test/fixtures/helper_mailer/use_mail_helper.erb +5 -0
- data/test/fixtures/helpers/example_helper.rb +5 -0
- data/test/fixtures/layouts/auto_layout_mailer.html.erb +1 -0
- data/test/fixtures/layouts/auto_layout_mailer.text.erb +1 -0
- data/test/fixtures/layouts/spam.html.erb +1 -0
- data/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb +1 -0
- data/test/fixtures/raw_email +14 -0
- data/test/fixtures/raw_email10 +20 -0
- data/test/fixtures/raw_email12 +32 -0
- data/test/fixtures/raw_email13 +29 -0
- data/test/fixtures/raw_email2 +114 -0
- data/test/fixtures/raw_email3 +70 -0
- data/test/fixtures/raw_email4 +59 -0
- data/test/fixtures/raw_email5 +19 -0
- data/test/fixtures/raw_email6 +20 -0
- data/test/fixtures/raw_email7 +66 -0
- data/test/fixtures/raw_email8 +47 -0
- data/test/fixtures/raw_email9 +28 -0
- data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
- data/test/fixtures/raw_email_with_invalid_characters_in_content_type +104 -0
- data/test/fixtures/raw_email_with_nested_attachment +100 -0
- data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
- data/test/fixtures/second_mailer/share.erb +1 -0
- data/test/fixtures/templates/signed_up.erb +3 -0
- data/test/fixtures/test_mailer/_subtemplate.text.plain.erb +1 -0
- data/test/fixtures/test_mailer/body_ivar.erb +2 -0
- data/test/fixtures/test_mailer/custom_templating_extension.text.html.haml +6 -0
- data/test/fixtures/test_mailer/custom_templating_extension.text.plain.haml +6 -0
- data/test/fixtures/test_mailer/implicitly_multipart_example.ignored.erb +1 -0
- data/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak +1 -0
- data/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb +10 -0
- data/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb~ +10 -0
- data/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb +2 -0
- data/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb +1 -0
- data/test/fixtures/test_mailer/included_subtemplate.text.plain.erb +1 -0
- data/test/fixtures/test_mailer/rxml_template.builder +2 -0
- data/test/fixtures/test_mailer/rxml_template.rxml +2 -0
- data/test/fixtures/test_mailer/signed_up.html.erb +3 -0
- data/test/fixtures/test_mailer/signed_up_with_url.erb +5 -0
- data/test/mail_helper_test.rb +95 -0
- data/test/mail_layout_test.rb +123 -0
- data/test/mail_render_test.rb +116 -0
- data/test/mail_service_test.rb +1081 -0
- data/test/quoting_test.rb +99 -0
- data/test/test_helper_test.rb +129 -0
- data/test/tmail_test.rb +22 -0
- data/test/url_test.rb +76 -0
- metadata +195 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'active_support/dependencies'
|
2
|
+
|
3
|
+
module ActionMailer
|
4
|
+
module Helpers #:nodoc:
|
5
|
+
def self.included(base) #:nodoc:
|
6
|
+
# Initialize the base module to aggregate its helpers.
|
7
|
+
base.class_inheritable_accessor :master_helper_module
|
8
|
+
base.master_helper_module = Module.new
|
9
|
+
|
10
|
+
# Extend base with class methods to declare helpers.
|
11
|
+
base.extend(ClassMethods)
|
12
|
+
|
13
|
+
base.class_eval do
|
14
|
+
# Wrap inherited to create a new master helper module for subclasses.
|
15
|
+
class << self
|
16
|
+
alias_method_chain :inherited, :helper
|
17
|
+
end
|
18
|
+
|
19
|
+
# Wrap initialize_template_class to extend new template class
|
20
|
+
# instances with the master helper module.
|
21
|
+
alias_method_chain :initialize_template_class, :helper
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
# Makes all the (instance) methods in the helper module available to templates rendered through this controller.
|
27
|
+
# See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
|
28
|
+
# available to the templates.
|
29
|
+
def add_template_helper(helper_module) #:nodoc:
|
30
|
+
master_helper_module.module_eval "include #{helper_module}"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Declare a helper:
|
34
|
+
# helper :foo
|
35
|
+
# requires 'foo_helper' and includes FooHelper in the template class.
|
36
|
+
# helper FooHelper
|
37
|
+
# includes FooHelper in the template class.
|
38
|
+
# helper { def foo() "#{bar} is the very best" end }
|
39
|
+
# evaluates the block in the template class, adding method +foo+.
|
40
|
+
# helper(:three, BlindHelper) { def mice() 'mice' end }
|
41
|
+
# does all three.
|
42
|
+
def helper(*args, &block)
|
43
|
+
args.flatten.each do |arg|
|
44
|
+
case arg
|
45
|
+
when Module
|
46
|
+
add_template_helper(arg)
|
47
|
+
when String, Symbol
|
48
|
+
file_name = arg.to_s.underscore + '_helper'
|
49
|
+
class_name = file_name.camelize
|
50
|
+
|
51
|
+
begin
|
52
|
+
require_dependency(file_name)
|
53
|
+
rescue LoadError => load_error
|
54
|
+
requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
|
55
|
+
msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
|
56
|
+
raise LoadError.new(msg).copy_blame!(load_error)
|
57
|
+
end
|
58
|
+
|
59
|
+
add_template_helper(class_name.constantize)
|
60
|
+
else
|
61
|
+
raise ArgumentError, 'helper expects String, Symbol, or Module argument'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Evaluate block in template class if given.
|
66
|
+
master_helper_module.module_eval(&block) if block_given?
|
67
|
+
end
|
68
|
+
|
69
|
+
# Declare a controller method as a helper. For example,
|
70
|
+
# helper_method :link_to
|
71
|
+
# def link_to(name, options) ... end
|
72
|
+
# makes the link_to controller method available in the view.
|
73
|
+
def helper_method(*methods)
|
74
|
+
methods.flatten.each do |method|
|
75
|
+
master_helper_module.module_eval <<-end_eval
|
76
|
+
def #{method}(*args, &block)
|
77
|
+
controller.__send__(%(#{method}), *args, &block)
|
78
|
+
end
|
79
|
+
end_eval
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Declare a controller attribute as a helper. For example,
|
84
|
+
# helper_attr :name
|
85
|
+
# attr_accessor :name
|
86
|
+
# makes the name and name= controller methods available in the view.
|
87
|
+
# The is a convenience wrapper for helper_method.
|
88
|
+
def helper_attr(*attrs)
|
89
|
+
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def inherited_with_helper(child)
|
94
|
+
inherited_without_helper(child)
|
95
|
+
begin
|
96
|
+
child.master_helper_module = Module.new
|
97
|
+
child.master_helper_module.__send__(:include, master_helper_module)
|
98
|
+
child.helper child.name.to_s.underscore
|
99
|
+
rescue MissingSourceFile => e
|
100
|
+
raise unless e.is_missing?("helpers/#{child.name.to_s.underscore}_helper")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
# Extend the template class instance with our controller's helper module.
|
107
|
+
def initialize_template_class_with_helper(assigns)
|
108
|
+
returning(template = initialize_template_class_without_helper(assigns)) do
|
109
|
+
template.extend self.class.master_helper_module
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MailHelper
|
2
|
+
# Uses Text::Format to take the text and format it, indented two spaces for
|
3
|
+
# each line, and wrapped at 72 columns.
|
4
|
+
def block_format(text)
|
5
|
+
formatted = text.split(/\n\r\n/).collect { |paragraph|
|
6
|
+
Text::Format.new(
|
7
|
+
:columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph
|
8
|
+
).format
|
9
|
+
}.join("\n")
|
10
|
+
|
11
|
+
# Make list points stand on their own line
|
12
|
+
formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" }
|
13
|
+
formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" }
|
14
|
+
|
15
|
+
formatted
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module ActionMailer
|
2
|
+
# Represents a subpart of an email message. It shares many similar
|
3
|
+
# attributes of ActionMailer::Base. Although you can create parts manually
|
4
|
+
# and add them to the +parts+ list of the mailer, it is easier
|
5
|
+
# to use the helper methods in ActionMailer::PartContainer.
|
6
|
+
class Part
|
7
|
+
include AdvAttrAccessor, PartContainer, Utils
|
8
|
+
|
9
|
+
# Represents the body of the part, as a string. This should not be a
|
10
|
+
# Hash (like ActionMailer::Base), but if you want a template to be rendered
|
11
|
+
# into the body of a subpart you can do it with the mailer's +render+ method
|
12
|
+
# and assign the result here.
|
13
|
+
adv_attr_accessor :body
|
14
|
+
|
15
|
+
# Specify the charset for this subpart. By default, it will be the charset
|
16
|
+
# of the containing part or mailer.
|
17
|
+
adv_attr_accessor :charset
|
18
|
+
|
19
|
+
# The content disposition of this part, typically either "inline" or
|
20
|
+
# "attachment".
|
21
|
+
adv_attr_accessor :content_disposition
|
22
|
+
|
23
|
+
# The content type of the part.
|
24
|
+
adv_attr_accessor :content_type
|
25
|
+
|
26
|
+
# The filename to use for this subpart (usually for attachments).
|
27
|
+
adv_attr_accessor :filename
|
28
|
+
|
29
|
+
# Accessor for specifying additional headers to include with this part.
|
30
|
+
adv_attr_accessor :headers
|
31
|
+
|
32
|
+
# The transfer encoding to use for this subpart, like "base64" or
|
33
|
+
# "quoted-printable".
|
34
|
+
adv_attr_accessor :transfer_encoding
|
35
|
+
|
36
|
+
# Create a new part from the given +params+ hash. The valid params keys
|
37
|
+
# correspond to the accessors.
|
38
|
+
def initialize(params)
|
39
|
+
@content_type = params[:content_type]
|
40
|
+
@content_disposition = params[:disposition] || "inline"
|
41
|
+
@charset = params[:charset]
|
42
|
+
@body = params[:body]
|
43
|
+
@filename = params[:filename]
|
44
|
+
@transfer_encoding = params[:transfer_encoding] || "quoted-printable"
|
45
|
+
@headers = params[:headers] || {}
|
46
|
+
@parts = []
|
47
|
+
end
|
48
|
+
|
49
|
+
# Convert the part to a mail object which can be included in the parts
|
50
|
+
# list of another mail object.
|
51
|
+
def to_mail(defaults)
|
52
|
+
part = TMail::Mail.new
|
53
|
+
|
54
|
+
real_content_type, ctype_attrs = parse_content_type(defaults)
|
55
|
+
|
56
|
+
if @parts.empty?
|
57
|
+
part.content_transfer_encoding = transfer_encoding || "quoted-printable"
|
58
|
+
case (transfer_encoding || "").downcase
|
59
|
+
when "base64" then
|
60
|
+
part.body = TMail::Base64.folding_encode(body)
|
61
|
+
when "quoted-printable"
|
62
|
+
part.body = [normalize_new_lines(body)].pack("M*")
|
63
|
+
else
|
64
|
+
part.body = body
|
65
|
+
end
|
66
|
+
|
67
|
+
# Always set the content_type after setting the body and or parts!
|
68
|
+
# Also don't set filename and name when there is none (like in
|
69
|
+
# non-attachment parts)
|
70
|
+
if content_disposition == "attachment"
|
71
|
+
ctype_attrs.delete "charset"
|
72
|
+
part.set_content_type(real_content_type, nil,
|
73
|
+
squish("name" => filename).merge(ctype_attrs))
|
74
|
+
part.set_content_disposition(content_disposition,
|
75
|
+
squish("filename" => filename).merge(ctype_attrs))
|
76
|
+
else
|
77
|
+
part.set_content_type(real_content_type, nil, ctype_attrs)
|
78
|
+
part.set_content_disposition(content_disposition)
|
79
|
+
end
|
80
|
+
else
|
81
|
+
if String === body
|
82
|
+
@parts.unshift Part.new(:charset => charset, :body => @body, :content_type => 'text/plain')
|
83
|
+
@body = nil
|
84
|
+
end
|
85
|
+
|
86
|
+
@parts.each do |p|
|
87
|
+
prt = (TMail::Mail === p ? p : p.to_mail(defaults))
|
88
|
+
part.parts << prt
|
89
|
+
end
|
90
|
+
|
91
|
+
if real_content_type =~ /multipart/
|
92
|
+
ctype_attrs.delete 'charset'
|
93
|
+
part.set_content_type(real_content_type, nil, ctype_attrs)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
headers.each { |k,v| part[k] = v }
|
98
|
+
|
99
|
+
part
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
def squish(values={})
|
104
|
+
values.delete_if { |k,v| v.nil? }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module ActionMailer
|
2
|
+
# Accessors and helpers that ActionMailer::Base and ActionMailer::Part have
|
3
|
+
# in common. Using these helpers you can easily add subparts or attachments
|
4
|
+
# to your message:
|
5
|
+
#
|
6
|
+
# def my_mail_message(...)
|
7
|
+
# ...
|
8
|
+
# part "text/plain" do |p|
|
9
|
+
# p.body "hello, world"
|
10
|
+
# p.transfer_encoding "base64"
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# attachment "image/jpg" do |a|
|
14
|
+
# a.body = File.read("hello.jpg")
|
15
|
+
# a.filename = "hello.jpg"
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
module PartContainer
|
19
|
+
# The list of subparts of this container
|
20
|
+
attr_reader :parts
|
21
|
+
|
22
|
+
# Add a part to a multipart message, with the given content-type. The
|
23
|
+
# part itself is yielded to the block so that other properties (charset,
|
24
|
+
# body, headers, etc.) can be set on it.
|
25
|
+
def part(params)
|
26
|
+
params = {:content_type => params} if String === params
|
27
|
+
part = Part.new(params)
|
28
|
+
yield part if block_given?
|
29
|
+
@parts << part
|
30
|
+
end
|
31
|
+
|
32
|
+
# Add an attachment to a multipart message. This is simply a part with the
|
33
|
+
# content-disposition set to "attachment".
|
34
|
+
def attachment(params, &block)
|
35
|
+
params = { :content_type => params } if String === params
|
36
|
+
params = { :disposition => "attachment",
|
37
|
+
:transfer_encoding => "base64" }.merge(params)
|
38
|
+
part(params, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def parse_content_type(defaults=nil)
|
44
|
+
if content_type.blank?
|
45
|
+
return defaults ?
|
46
|
+
[ defaults.content_type, { 'charset' => defaults.charset } ] :
|
47
|
+
[ nil, {} ]
|
48
|
+
end
|
49
|
+
ctype, *attrs = content_type.split(/;\s*/)
|
50
|
+
attrs = attrs.inject({}) { |h,s| k,v = s.split(/=/, 2); h[k] = v; h }
|
51
|
+
[ctype, {"charset" => charset || defaults && defaults.charset}.merge(attrs)]
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ActionMailer
|
2
|
+
module Quoting #:nodoc:
|
3
|
+
# Convert the given text into quoted printable format, with an instruction
|
4
|
+
# that the text be eventually interpreted in the given charset.
|
5
|
+
def quoted_printable(text, charset)
|
6
|
+
text = text.gsub( /[^a-z ]/i ) { quoted_printable_encode($&) }.
|
7
|
+
gsub( / /, "_" )
|
8
|
+
"=?#{charset}?Q?#{text}?="
|
9
|
+
end
|
10
|
+
|
11
|
+
# Convert the given character to quoted printable format, taking into
|
12
|
+
# account multi-byte characters (if executing with $KCODE="u", for instance)
|
13
|
+
def quoted_printable_encode(character)
|
14
|
+
result = ""
|
15
|
+
character.each_byte { |b| result << "=%02X" % b }
|
16
|
+
result
|
17
|
+
end
|
18
|
+
|
19
|
+
# A quick-and-dirty regexp for determining whether a string contains any
|
20
|
+
# characters that need escaping.
|
21
|
+
if !defined?(CHARS_NEEDING_QUOTING)
|
22
|
+
CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
|
23
|
+
end
|
24
|
+
|
25
|
+
# Quote the given text if it contains any "illegal" characters
|
26
|
+
def quote_if_necessary(text, charset)
|
27
|
+
text = text.dup.force_encoding(Encoding::ASCII_8BIT) if text.respond_to?(:force_encoding)
|
28
|
+
|
29
|
+
(text =~ CHARS_NEEDING_QUOTING) ?
|
30
|
+
quoted_printable(text, charset) :
|
31
|
+
text
|
32
|
+
end
|
33
|
+
|
34
|
+
# Quote any of the given strings if they contain any "illegal" characters
|
35
|
+
def quote_any_if_necessary(charset, *args)
|
36
|
+
args.map { |v| quote_if_necessary(v, charset) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Quote the given address if it needs to be. The address may be a
|
40
|
+
# regular email address, or it can be a phrase followed by an address in
|
41
|
+
# brackets. The phrase is the only part that will be quoted, and only if
|
42
|
+
# it needs to be. This allows extended characters to be used in the
|
43
|
+
# "to", "from", "cc", "bcc" and "reply-to" headers.
|
44
|
+
def quote_address_if_necessary(address, charset)
|
45
|
+
if Array === address
|
46
|
+
address.map { |a| quote_address_if_necessary(a, charset) }
|
47
|
+
elsif address =~ /^(\S.*)\s+(<.*>)$/
|
48
|
+
address = $2
|
49
|
+
phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
|
50
|
+
"\"#{phrase}\" #{address}"
|
51
|
+
else
|
52
|
+
address
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Quote any of the given addresses, if they need to be.
|
57
|
+
def quote_any_address_if_necessary(charset, *args)
|
58
|
+
args.map { |v| quote_address_if_necessary(v, charset) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'active_support/test_case'
|
2
|
+
|
3
|
+
module ActionMailer
|
4
|
+
class NonInferrableMailerError < ::StandardError
|
5
|
+
def initialize(name)
|
6
|
+
super "Unable to determine the mailer to test from #{name}. " +
|
7
|
+
"You'll need to specify it using tests YourMailer in your " +
|
8
|
+
"test case definition"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class TestCase < ActiveSupport::TestCase
|
13
|
+
include Quoting, TestHelper
|
14
|
+
|
15
|
+
setup :initialize_test_deliveries
|
16
|
+
setup :set_expected_mail
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def tests(mailer)
|
20
|
+
write_inheritable_attribute(:mailer_class, mailer)
|
21
|
+
end
|
22
|
+
|
23
|
+
def mailer_class
|
24
|
+
if mailer = read_inheritable_attribute(:mailer_class)
|
25
|
+
mailer
|
26
|
+
else
|
27
|
+
tests determine_default_mailer(name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def determine_default_mailer(name)
|
32
|
+
name.sub(/Test$/, '').constantize
|
33
|
+
rescue NameError => e
|
34
|
+
raise NonInferrableMailerError.new(name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
def initialize_test_deliveries
|
40
|
+
ActionMailer::Base.delivery_method = :test
|
41
|
+
ActionMailer::Base.perform_deliveries = true
|
42
|
+
ActionMailer::Base.deliveries = []
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_expected_mail
|
46
|
+
@expected = TMail::Mail.new
|
47
|
+
@expected.set_content_type "text", "plain", { "charset" => charset }
|
48
|
+
@expected.mime_version = '1.0'
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def charset
|
53
|
+
"utf-8"
|
54
|
+
end
|
55
|
+
|
56
|
+
def encode(subject)
|
57
|
+
quoted_printable(subject, charset)
|
58
|
+
end
|
59
|
+
|
60
|
+
def read_fixture(action)
|
61
|
+
IO.readlines(File.join(RAILS_ROOT, 'test', 'fixtures', self.class.mailer_class.name.underscore, action))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module ActionMailer
|
2
|
+
module TestHelper
|
3
|
+
# Asserts that the number of emails sent matches the given number.
|
4
|
+
#
|
5
|
+
# def test_emails
|
6
|
+
# assert_emails 0
|
7
|
+
# ContactMailer.deliver_contact
|
8
|
+
# assert_emails 1
|
9
|
+
# ContactMailer.deliver_contact
|
10
|
+
# assert_emails 2
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# If a block is passed, that block should cause the specified number of emails to be sent.
|
14
|
+
#
|
15
|
+
# def test_emails_again
|
16
|
+
# assert_emails 1 do
|
17
|
+
# ContactMailer.deliver_contact
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# assert_emails 2 do
|
21
|
+
# ContactMailer.deliver_contact
|
22
|
+
# ContactMailer.deliver_contact
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
def assert_emails(number)
|
26
|
+
if block_given?
|
27
|
+
original_count = ActionMailer::Base.deliveries.size
|
28
|
+
yield
|
29
|
+
new_count = ActionMailer::Base.deliveries.size
|
30
|
+
assert_equal original_count + number, new_count, "#{number} emails expected, but #{new_count - original_count} were sent"
|
31
|
+
else
|
32
|
+
assert_equal number, ActionMailer::Base.deliveries.size
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Assert that no emails have been sent.
|
37
|
+
#
|
38
|
+
# def test_emails
|
39
|
+
# assert_no_emails
|
40
|
+
# ContactMailer.deliver_contact
|
41
|
+
# assert_emails 1
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# If a block is passed, that block should not cause any emails to be sent.
|
45
|
+
#
|
46
|
+
# def test_emails_again
|
47
|
+
# assert_no_emails do
|
48
|
+
# # No emails should be sent from this block
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# Note: This assertion is simply a shortcut for:
|
53
|
+
#
|
54
|
+
# assert_emails 0
|
55
|
+
def assert_no_emails(&block)
|
56
|
+
assert_emails 0, &block
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# TODO: Deprecate this
|
62
|
+
module Test
|
63
|
+
module Unit
|
64
|
+
class TestCase
|
65
|
+
include ActionMailer::TestHelper
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|