mail_form 1.0.0 → 1.1.0
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/README.rdoc +105 -51
- data/Rakefile +1 -0
- data/lib/mail_form.rb +48 -32
- data/lib/mail_form/base.rb +6 -124
- data/lib/mail_form/delivery.rb +239 -0
- data/lib/mail_form/shim.rb +50 -0
- data/lib/mail_form/version.rb +1 -1
- data/lib/views/mail_form/contact.erb +38 -0
- data/test/mail_form_test.rb +190 -0
- data/test/{base_test.rb → resource_test.rb} +31 -7
- data/test/test_helper.rb +25 -8
- metadata +7 -9
- data/lib/mail_form/dsl.rb +0 -171
- data/lib/mail_form/errors.rb +0 -59
- data/lib/mail_form/notifier.rb +0 -48
- data/test/errors_test.rb +0 -85
- data/test/notifier_test.rb +0 -183
- data/views/mail_form/notifier/contact.erb +0 -30
@@ -0,0 +1,239 @@
|
|
1
|
+
module MailForm::Delivery
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
ACCESSORS = [ :mail_attributes, :mail_subject, :mail_captcha,
|
5
|
+
:mail_attachments, :mail_recipients, :mail_sender,
|
6
|
+
:mail_headers, :mail_template, :mail_appendable ]
|
7
|
+
|
8
|
+
included do
|
9
|
+
class_inheritable_reader *ACCESSORS
|
10
|
+
protected *ACCESSORS
|
11
|
+
|
12
|
+
# Initialize arrays and hashes
|
13
|
+
write_inheritable_array :mail_captcha, []
|
14
|
+
write_inheritable_array :mail_appendable, []
|
15
|
+
write_inheritable_array :mail_attributes, []
|
16
|
+
write_inheritable_array :mail_attachments, []
|
17
|
+
|
18
|
+
headers({})
|
19
|
+
sender {|c| c.email }
|
20
|
+
subject{|c| c.class.model_name.human }
|
21
|
+
template 'contact'
|
22
|
+
|
23
|
+
before_create :not_spam?
|
24
|
+
after_create :deliver!
|
25
|
+
|
26
|
+
attr_accessor :request
|
27
|
+
alias :deliver :create
|
28
|
+
end
|
29
|
+
|
30
|
+
module ClassMethods
|
31
|
+
# Declare your form attributes. All attributes declared here will be appended
|
32
|
+
# to the e-mail, except the ones captcha is true.
|
33
|
+
#
|
34
|
+
# == Options
|
35
|
+
#
|
36
|
+
# * :validate - A hook to validates_*_of. When true is given, validates the
|
37
|
+
# presence of the attribute. When a regexp, validates format. When array,
|
38
|
+
# validates the inclusion of the attribute in the array.
|
39
|
+
#
|
40
|
+
# Whenever :validate is given, the presence is automatically checked. Give
|
41
|
+
# :allow_blank => true to override.
|
42
|
+
#
|
43
|
+
# Finally, when :validate is a symbol, the method given as symbol will be
|
44
|
+
# called. Then you can add validations as you do in ActiveRecord (errors.add).
|
45
|
+
#
|
46
|
+
# * <tt>:attachment</tt> - When given, expects a file to be sent and attaches
|
47
|
+
# it to the e-mail. Don't forget to set your form to multitype.
|
48
|
+
#
|
49
|
+
# * <tt>:captcha</tt> - When true, validates the attributes must be blank
|
50
|
+
# This is a simple way to avoid spam
|
51
|
+
#
|
52
|
+
# == Examples
|
53
|
+
#
|
54
|
+
# class ContactForm < MailForm
|
55
|
+
# attributes :name, :validate => true
|
56
|
+
# attributes :email, :validate => /^([^@]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
|
57
|
+
# attributes :type, :validate => ["General", "Interface bug"]
|
58
|
+
# attributes :message
|
59
|
+
# attributes :screenshot, :attachment => true, :validate => :interface_bug?
|
60
|
+
# attributes :nickname, :captcha => true
|
61
|
+
#
|
62
|
+
# def interface_bug?
|
63
|
+
# if type == 'Interface bug' && screenshot.nil?
|
64
|
+
# self.errors.add(:screenshot, "can't be blank when you are reporting an interface bug")
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
def attribute(*accessors)
|
70
|
+
options = accessors.extract_options!
|
71
|
+
attr_accessor *(accessors - instance_methods.map(&:to_sym))
|
72
|
+
|
73
|
+
if options[:attachment]
|
74
|
+
write_inheritable_array(:mail_attachments, accessors)
|
75
|
+
elsif options[:captcha]
|
76
|
+
write_inheritable_array(:mail_captcha, accessors)
|
77
|
+
else
|
78
|
+
write_inheritable_array(:mail_attributes, accessors)
|
79
|
+
end
|
80
|
+
|
81
|
+
validation = options.delete(:validate)
|
82
|
+
return unless validation
|
83
|
+
|
84
|
+
accessors.each do |accessor|
|
85
|
+
case validation
|
86
|
+
when Symbol, Class
|
87
|
+
validate validation
|
88
|
+
break
|
89
|
+
when Regexp
|
90
|
+
validates_format_of accessor, :with => validation, :allow_blank => true
|
91
|
+
when Array
|
92
|
+
validates_inclusion_of accessor, :in => validation, :allow_blank => true
|
93
|
+
when Range
|
94
|
+
validates_length_of accessor, :within => validation, :allow_blank => true
|
95
|
+
end
|
96
|
+
|
97
|
+
validates_presence_of accessor unless options[:allow_blank] == true
|
98
|
+
end
|
99
|
+
end
|
100
|
+
alias :attributes :attribute
|
101
|
+
|
102
|
+
# Declares contact email sender. It can be a string or a proc or a symbol.
|
103
|
+
#
|
104
|
+
# When a symbol is given, it will call a method on the form object with
|
105
|
+
# the same name as the symbol. As a proc, it receives a simple form
|
106
|
+
# instance. By default is the class human name.
|
107
|
+
#
|
108
|
+
# == Examples
|
109
|
+
#
|
110
|
+
# class ContactForm < MailForm
|
111
|
+
# subject "My Contact Form"
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
def subject(duck=nil, &block)
|
115
|
+
write_inheritable_attribute(:mail_subject, duck || block)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Declares contact email sender. It can be a string or a proc or a symbol.
|
119
|
+
#
|
120
|
+
# When a symbol is given, it will call a method on the form object with
|
121
|
+
# the same name as the symbol. As a proc, it receives a simple form
|
122
|
+
# instance. By default is:
|
123
|
+
#
|
124
|
+
# sender{ |c| c.email }
|
125
|
+
#
|
126
|
+
# This requires that your MailForm object have an email attribute.
|
127
|
+
#
|
128
|
+
# == Examples
|
129
|
+
#
|
130
|
+
# class ContactForm < MailForm
|
131
|
+
# # Change sender to include also the name
|
132
|
+
# sender { |c| %{"#{c.name}" <#{c.email}>} }
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
def sender(duck=nil, &block)
|
136
|
+
write_inheritable_attribute(:mail_sender, duck || block)
|
137
|
+
end
|
138
|
+
alias :from :sender
|
139
|
+
|
140
|
+
# Who will receive the e-mail. Can be a string or array or a symbol or a proc.
|
141
|
+
#
|
142
|
+
# When a symbol is given, it will call a method on the form object with
|
143
|
+
# the same name as the symbol. As a proc, it receives a simple form instance.
|
144
|
+
#
|
145
|
+
# Both the proc and the symbol must return a string or an array. By default
|
146
|
+
# is nil.
|
147
|
+
#
|
148
|
+
# == Examples
|
149
|
+
#
|
150
|
+
# class ContactForm < MailForm
|
151
|
+
# recipients [ "first.manager@domain.com", "second.manager@domain.com" ]
|
152
|
+
# end
|
153
|
+
#
|
154
|
+
def recipients(duck=nil, &block)
|
155
|
+
write_inheritable_attribute(:mail_recipients, duck || block)
|
156
|
+
end
|
157
|
+
alias :to :recipients
|
158
|
+
|
159
|
+
# Additional headers to your e-mail.
|
160
|
+
#
|
161
|
+
# == Examples
|
162
|
+
#
|
163
|
+
# class ContactForm < MailForm
|
164
|
+
# headers { :content_type => 'text/html' }
|
165
|
+
# end
|
166
|
+
#
|
167
|
+
def headers(hash)
|
168
|
+
write_inheritable_hash(:mail_headers, hash)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Customized template for your e-mail, if you don't want to use default
|
172
|
+
# 'contact' template or need more than one contact form with different
|
173
|
+
# template layouts.
|
174
|
+
#
|
175
|
+
# When a symbol is given, it will call a method on the form object with
|
176
|
+
# the same name as the symbol. As a proc, it receives a simple form
|
177
|
+
# instance. Both method and proc must return a string with the template
|
178
|
+
# name. Defaults to 'contact'.
|
179
|
+
#
|
180
|
+
# == Examples
|
181
|
+
#
|
182
|
+
# class ContactForm < MailForm
|
183
|
+
# # look for a template in views/mail_form/notifier/my_template.erb
|
184
|
+
# template 'my_template'
|
185
|
+
# end
|
186
|
+
#
|
187
|
+
def template(new_template)
|
188
|
+
write_inheritable_attribute(:mail_template, new_template)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Values from request object to be appended to the contact form.
|
192
|
+
# Whenever used, you have to send the request object when initializing the object:
|
193
|
+
#
|
194
|
+
# @contact_form = ContactForm.new(params[:contact_form], request)
|
195
|
+
#
|
196
|
+
# You can get the values to be appended from the AbstractRequest
|
197
|
+
# documentation (http://api.rubyonrails.org/classes/ActionController/AbstractRequest.html)
|
198
|
+
#
|
199
|
+
# == Examples
|
200
|
+
#
|
201
|
+
# class ContactForm < MailForm
|
202
|
+
# append :remote_ip, :user_agent, :session, :cookies
|
203
|
+
# end
|
204
|
+
#
|
205
|
+
def append(*values)
|
206
|
+
write_inheritable_array(:mail_appendable, values)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# In development, raises an error if the captcha field is not blank. This is
|
211
|
+
# is good to remember that the field should be hidden with CSS and shown only
|
212
|
+
# to robots.
|
213
|
+
#
|
214
|
+
# In test and in production, it returns true if all captcha fields are blank,
|
215
|
+
# returns false otherwise.
|
216
|
+
#
|
217
|
+
def spam?
|
218
|
+
mail_captcha.each do |field|
|
219
|
+
next if send(field).blank?
|
220
|
+
|
221
|
+
if defined?(Rails) && Rails.env.development?
|
222
|
+
raise ScriptError, "The captcha field #{field} was supposed to be blank"
|
223
|
+
else
|
224
|
+
return true
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
false
|
229
|
+
end
|
230
|
+
|
231
|
+
def not_spam?
|
232
|
+
!spam?
|
233
|
+
end
|
234
|
+
|
235
|
+
# Deliver the resource without checking any condition.
|
236
|
+
def deliver!
|
237
|
+
MailForm.contact(self).deliver
|
238
|
+
end
|
239
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
# This the module which makes any class behave like ActiveModel.
|
4
|
+
module MailForm::Shim
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
extend ActiveModel::Naming
|
8
|
+
extend ActiveModel::Translation
|
9
|
+
extend ActiveModel::Callbacks
|
10
|
+
include ActiveModel::Validations
|
11
|
+
include ActiveModel::Conversion
|
12
|
+
|
13
|
+
extend MailForm::Shim::ClassMethods
|
14
|
+
define_model_callbacks :create
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def i18n_scope
|
20
|
+
:mail_form
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Initialize assigning the parameters given as hash (just as in ActiveRecord).
|
25
|
+
def initialize(params={})
|
26
|
+
params.each_pair do |attr, value|
|
27
|
+
self.send(:"#{attr}=", value)
|
28
|
+
end unless params.blank?
|
29
|
+
end
|
30
|
+
|
31
|
+
# Always return true so when using form_for, the default method will be post.
|
32
|
+
def new_record?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
# Always return nil so when using form_for, the default method will be post.
|
37
|
+
def id
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# Create just check validity, and if so, trigger callbacks.
|
42
|
+
def create
|
43
|
+
if valid?
|
44
|
+
_run_create_callbacks { true }
|
45
|
+
else
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
alias :save :create
|
50
|
+
end
|
data/lib/mail_form/version.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
<h4 style="text-decoration:underline"><%= mailer.subject %></h4>
|
2
|
+
|
3
|
+
<% @resource.class.mail_attributes.each do |attribute|
|
4
|
+
value = @resource.send(attribute)
|
5
|
+
next if value.blank? %>
|
6
|
+
|
7
|
+
<p><b><%= @resource.class.human_attribute_name(attribute) %>:</b>
|
8
|
+
<%= case value
|
9
|
+
when /\n/
|
10
|
+
raw(simple_format(h(value)))
|
11
|
+
when Time, DateTime, Date
|
12
|
+
I18n.l value
|
13
|
+
else
|
14
|
+
value
|
15
|
+
end
|
16
|
+
%></p>
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<% unless @resource.class.mail_appendable.blank? %>
|
20
|
+
<br /><h4 style="text-decoration:underline"><%= I18n.t :title, :scope => [ :mail_form, :request ], :default => 'Request information' %></h4>
|
21
|
+
|
22
|
+
<% @resource.class.mail_appendable.each do |attribute|
|
23
|
+
value = @resource.request.send(attribute)
|
24
|
+
|
25
|
+
value = if value.is_a?(Hash) && !value.empty?
|
26
|
+
content_tag(:ul, value.to_a.map{|k,v| content_tag(:li, h("#{k}: #{v.inspect}")) }.join("\n"), :style => "list-style:none;")
|
27
|
+
elsif value.is_a?(String)
|
28
|
+
h(value)
|
29
|
+
else
|
30
|
+
h(value.inspect)
|
31
|
+
end
|
32
|
+
%>
|
33
|
+
|
34
|
+
<p><b><%= I18n.t attribute, :scope => [ :mail_form, :request ], :default => attribute.to_s.humanize %>:</b>
|
35
|
+
<%= value.include?("\n") ? raw(simple_format(value)) : value %></p>
|
36
|
+
<% end %>
|
37
|
+
<br />
|
38
|
+
<% end %>
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class MailFormNotifierTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@form = ContactForm.new(:name => 'José', :email => 'my.email@my.domain.com', :message => 'Cool')
|
7
|
+
|
8
|
+
@request = ActionController::TestRequest.new
|
9
|
+
@valid_attributes = { :name => 'José', :email => 'my.email@my.domain.com', :message => "Cool\nno?" }
|
10
|
+
@advanced = AdvancedForm.new(@valid_attributes)
|
11
|
+
@advanced.request = @request
|
12
|
+
|
13
|
+
test_file = Rack::Test::UploadedFile.new(File.join(File.dirname(__FILE__), 'test_file.txt'))
|
14
|
+
@with_file = FileForm.new(:name => 'José', :email => 'my.email@my.domain.com', :message => "Cool", :file => test_file)
|
15
|
+
|
16
|
+
@template = TemplateForm.new(@valid_attributes)
|
17
|
+
|
18
|
+
ActionMailer::Base.deliveries = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_email_is_sent
|
22
|
+
@form.deliver
|
23
|
+
assert_equal 1, ActionMailer::Base.deliveries.size
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_subject_defaults_to_class_human_name
|
27
|
+
@form.deliver
|
28
|
+
assert_equal 'Contact form', first_delivery.subject
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_subject_is_a_string
|
32
|
+
@advanced.deliver
|
33
|
+
assert_equal 'My Advanced Form', first_delivery.subject
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_sender_defaults_to_form_email
|
37
|
+
@form.deliver
|
38
|
+
assert_equal ['my.email@my.domain.com'], first_delivery.from
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_error_is_raised_when_recipients_is_nil
|
42
|
+
assert_raise ScriptError do
|
43
|
+
NullRecipient.new.deliver
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_recipients_is_a_string
|
48
|
+
@form.deliver
|
49
|
+
assert_equal ['my.email@my.domain.com'], first_delivery.to
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_recipients_is_an_array
|
53
|
+
@advanced.deliver
|
54
|
+
assert_equal ['my.first@email.com', 'my.second@email.com'], first_delivery.to
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_recipients_is_a_symbold
|
58
|
+
@with_file.deliver
|
59
|
+
assert_equal ['contact_file@my.domain.com'], first_delivery.to
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_headers_is_a_hash
|
63
|
+
@advanced.deliver
|
64
|
+
assert_equal 'mypath', first_delivery.header['return-path'].to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_body_contains_subject
|
68
|
+
@form.deliver
|
69
|
+
assert_match /Contact form/, first_delivery.body.to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_body_contains_attributes_values
|
73
|
+
@form.deliver
|
74
|
+
assert_match /José/, first_delivery.body.to_s
|
75
|
+
assert_match /my.email@my.domain.com/, first_delivery.body.to_s
|
76
|
+
assert_match /Cool/, first_delivery.body.to_s
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_body_contains_attributes_names
|
80
|
+
@form.deliver
|
81
|
+
assert_match /Name:/, first_delivery.body.to_s
|
82
|
+
assert_match /Email:/, first_delivery.body.to_s
|
83
|
+
assert_match /Message:/, first_delivery.body.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_body_contains_localized_attributes_names
|
87
|
+
I18n.backend.store_translations(:en, :mail_form => { :attributes => { :contact_form => { :message => 'Sent message' } } })
|
88
|
+
@form.deliver
|
89
|
+
assert_match /Sent message:/, first_delivery.body.to_s
|
90
|
+
assert_no_match /Message:/, first_delivery.body.to_s
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_body_mail_format_messages_with_break_line
|
94
|
+
@form.deliver
|
95
|
+
assert_no_match /<p>Cool/, first_delivery.body.to_s
|
96
|
+
|
97
|
+
@advanced.deliver
|
98
|
+
assert_match /<p>Cool/, last_delivery.body.to_s
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_body_mail_format_dates_with_i18n
|
102
|
+
@form.deliver
|
103
|
+
assert_no_match /I18n.l(Date.today)/, first_delivery.body.to_s
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_body_does_not_append_request_if_append_is_not_called
|
107
|
+
@form.deliver
|
108
|
+
assert_no_match /Request information/, first_delivery.body.to_s
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_body_does_append_request_if_append_is_called
|
112
|
+
@advanced.deliver
|
113
|
+
assert_match /Request information/, last_delivery.body.to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_request_title_is_localized
|
117
|
+
I18n.backend.store_translations(:en, :mail_form => { :request => { :title => 'Information about the request' } })
|
118
|
+
@advanced.deliver
|
119
|
+
assert_no_match /Request information/, last_delivery.body.to_s
|
120
|
+
assert_match /Information about the request/, last_delivery.body.to_s
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_request_info_attributes_are_printed
|
124
|
+
@advanced.deliver
|
125
|
+
assert_match /Remote ip/, last_delivery.body.to_s
|
126
|
+
assert_match /User agent/, last_delivery.body.to_s
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_request_info_attributes_are_localized
|
130
|
+
I18n.backend.store_translations(:en, :mail_form => { :request => { :remote_ip => 'IP Address' } })
|
131
|
+
@advanced.deliver
|
132
|
+
assert_match /IP Address/, last_delivery.body.to_s
|
133
|
+
assert_no_match /Remote ip/, last_delivery.body.to_s
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_request_info_values_are_printed
|
137
|
+
@advanced.deliver
|
138
|
+
assert_match /0\.0\.0\.0/, last_delivery.body.to_s
|
139
|
+
assert_match /Rails Testing/, last_delivery.body.to_s
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_request_info_hashes_are_print_inside_lis
|
143
|
+
@request.session = { :my => :session, :user => "data" }
|
144
|
+
@advanced.deliver
|
145
|
+
assert_match /<li>my: :session<\/li>/, last_delivery.body.to_s
|
146
|
+
assert_match /<li>user: "data"<\/li>/, last_delivery.body.to_s
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_error_is_raised_when_append_is_given_but_no_request_is_given
|
150
|
+
assert_raise ScriptError do
|
151
|
+
@advanced.request = nil
|
152
|
+
@advanced.deliver
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_form_with_file_includes_an_attachment
|
157
|
+
@with_file.deliver
|
158
|
+
assert_equal 1, first_delivery.attachments.size
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_form_with_file_does_not_output_attachment_as_attribute
|
162
|
+
@with_file.deliver
|
163
|
+
assert_no_match /File:/, first_delivery.body.to_s
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_form_with_customized_template_render_correct_template
|
167
|
+
begin
|
168
|
+
previous_view_path = MailForm.view_paths
|
169
|
+
MailForm.prepend_view_path File.join(File.dirname(__FILE__), 'views')
|
170
|
+
@template.deliver
|
171
|
+
assert_match 'Hello from my custom template!', last_delivery.body.to_s
|
172
|
+
ensure
|
173
|
+
MailForm.view_paths = previous_view_path
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
protected
|
178
|
+
|
179
|
+
def first_delivery
|
180
|
+
ActionMailer::Base.deliveries.first
|
181
|
+
end
|
182
|
+
|
183
|
+
def last_delivery
|
184
|
+
ActionMailer::Base.deliveries.last
|
185
|
+
end
|
186
|
+
|
187
|
+
def teardown
|
188
|
+
I18n.reload!
|
189
|
+
end
|
190
|
+
end
|