actionmailer_csi 2.3.5.p6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|