actionmailer_csi 2.3.5.p6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/CHANGELOG +370 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +149 -0
  4. data/Rakefile +99 -0
  5. data/install.rb +30 -0
  6. data/lib/action_mailer/adv_attr_accessor.rb +30 -0
  7. data/lib/action_mailer/base.rb +706 -0
  8. data/lib/action_mailer/helpers.rb +113 -0
  9. data/lib/action_mailer/mail_helper.rb +17 -0
  10. data/lib/action_mailer/part.rb +107 -0
  11. data/lib/action_mailer/part_container.rb +55 -0
  12. data/lib/action_mailer/quoting.rb +61 -0
  13. data/lib/action_mailer/test_case.rb +64 -0
  14. data/lib/action_mailer/test_helper.rb +68 -0
  15. data/lib/action_mailer/utils.rb +7 -0
  16. data/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb +1466 -0
  17. data/lib/action_mailer/vendor/text_format.rb +10 -0
  18. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb +426 -0
  19. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/attachments.rb +46 -0
  20. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/base64.rb +46 -0
  21. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/compat.rb +41 -0
  22. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/config.rb +67 -0
  23. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/core_extensions.rb +63 -0
  24. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/encode.rb +581 -0
  25. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb +960 -0
  26. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/index.rb +9 -0
  27. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb +1130 -0
  28. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/loader.rb +3 -0
  29. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb +578 -0
  30. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb +495 -0
  31. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/main.rb +6 -0
  32. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/mbox.rb +3 -0
  33. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/net.rb +248 -0
  34. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb +132 -0
  35. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/parser.rb +1478 -0
  36. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/port.rb +379 -0
  37. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/quoting.rb +118 -0
  38. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/require_arch.rb +58 -0
  39. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner.rb +49 -0
  40. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner_r.rb +261 -0
  41. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/stringio.rb +280 -0
  42. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/utils.rb +337 -0
  43. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/version.rb +39 -0
  44. data/lib/action_mailer/vendor/tmail-1.2.3/tmail.rb +5 -0
  45. data/lib/action_mailer/vendor/tmail.rb +17 -0
  46. data/lib/action_mailer/version.rb +9 -0
  47. data/lib/action_mailer.rb +62 -0
  48. data/lib/actionmailer.rb +2 -0
  49. data/test/abstract_unit.rb +62 -0
  50. data/test/asset_host_test.rb +54 -0
  51. data/test/delivery_method_test.rb +51 -0
  52. data/test/fixtures/asset_host_mailer/email_with_asset.html.erb +1 -0
  53. data/test/fixtures/auto_layout_mailer/hello.html.erb +1 -0
  54. data/test/fixtures/auto_layout_mailer/multipart.text.html.erb +1 -0
  55. data/test/fixtures/auto_layout_mailer/multipart.text.plain.erb +1 -0
  56. data/test/fixtures/explicit_layout_mailer/logout.html.erb +1 -0
  57. data/test/fixtures/explicit_layout_mailer/signup.html.erb +1 -0
  58. data/test/fixtures/first_mailer/share.erb +1 -0
  59. data/test/fixtures/helper_mailer/use_example_helper.erb +1 -0
  60. data/test/fixtures/helper_mailer/use_helper.erb +1 -0
  61. data/test/fixtures/helper_mailer/use_helper_method.erb +1 -0
  62. data/test/fixtures/helper_mailer/use_mail_helper.erb +5 -0
  63. data/test/fixtures/helpers/example_helper.rb +5 -0
  64. data/test/fixtures/layouts/auto_layout_mailer.html.erb +1 -0
  65. data/test/fixtures/layouts/auto_layout_mailer.text.erb +1 -0
  66. data/test/fixtures/layouts/spam.html.erb +1 -0
  67. data/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb +1 -0
  68. data/test/fixtures/raw_email +14 -0
  69. data/test/fixtures/raw_email10 +20 -0
  70. data/test/fixtures/raw_email12 +32 -0
  71. data/test/fixtures/raw_email13 +29 -0
  72. data/test/fixtures/raw_email2 +114 -0
  73. data/test/fixtures/raw_email3 +70 -0
  74. data/test/fixtures/raw_email4 +59 -0
  75. data/test/fixtures/raw_email5 +19 -0
  76. data/test/fixtures/raw_email6 +20 -0
  77. data/test/fixtures/raw_email7 +66 -0
  78. data/test/fixtures/raw_email8 +47 -0
  79. data/test/fixtures/raw_email9 +28 -0
  80. data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
  81. data/test/fixtures/raw_email_with_invalid_characters_in_content_type +104 -0
  82. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  83. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  84. data/test/fixtures/second_mailer/share.erb +1 -0
  85. data/test/fixtures/templates/signed_up.erb +3 -0
  86. data/test/fixtures/test_mailer/_subtemplate.text.plain.erb +1 -0
  87. data/test/fixtures/test_mailer/body_ivar.erb +2 -0
  88. data/test/fixtures/test_mailer/custom_templating_extension.text.html.haml +6 -0
  89. data/test/fixtures/test_mailer/custom_templating_extension.text.plain.haml +6 -0
  90. data/test/fixtures/test_mailer/implicitly_multipart_example.ignored.erb +1 -0
  91. data/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak +1 -0
  92. data/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb +10 -0
  93. data/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb~ +10 -0
  94. data/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb +2 -0
  95. data/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb +1 -0
  96. data/test/fixtures/test_mailer/included_subtemplate.text.plain.erb +1 -0
  97. data/test/fixtures/test_mailer/rxml_template.builder +2 -0
  98. data/test/fixtures/test_mailer/rxml_template.rxml +2 -0
  99. data/test/fixtures/test_mailer/signed_up.html.erb +3 -0
  100. data/test/fixtures/test_mailer/signed_up_with_url.erb +5 -0
  101. data/test/mail_helper_test.rb +95 -0
  102. data/test/mail_layout_test.rb +123 -0
  103. data/test/mail_render_test.rb +116 -0
  104. data/test/mail_service_test.rb +1081 -0
  105. data/test/quoting_test.rb +99 -0
  106. data/test/test_helper_test.rb +129 -0
  107. data/test/tmail_test.rb +22 -0
  108. data/test/url_test.rb +76 -0
  109. 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
@@ -0,0 +1,7 @@
1
+ module ActionMailer
2
+ module Utils #:nodoc:
3
+ def normalize_new_lines(text)
4
+ text.to_s.gsub(/\r\n?/, "\n")
5
+ end
6
+ end
7
+ end