rhizmail 0.1.0 → 0.1.1
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/lib/rhizmail.rb +209 -46
- data/lib/rhizmail.rb~ +149 -52
- metadata +3 -3
data/lib/rhizmail.rb
CHANGED
@@ -21,7 +21,7 @@
|
|
21
21
|
#
|
22
22
|
# def test_send_site_message
|
23
23
|
# mock_mailer = RhizMail::MockMailer.new
|
24
|
-
# RhizMail::set_mailer mock_mailer
|
24
|
+
# RhizMail::Mailer.set_mailer mock_mailer
|
25
25
|
# send_site_message( 'bill@yahoo.com', 'Bill Smith', 'Hi, Bill!' )
|
26
26
|
# assert_equal( 1, mock_mailer.messages_sent )
|
27
27
|
# assert_equal( 'Bill Smith', mock_mailer.messages_sent.first.to_name )
|
@@ -34,15 +34,15 @@
|
|
34
34
|
#
|
35
35
|
# RhizMail depends on two external libraries:
|
36
36
|
#
|
37
|
-
# *
|
37
|
+
# * ContextualService: http://contxtlservice.rubyforge.org
|
38
38
|
# * MockFS: http://mockfs.rubyforge.org
|
39
39
|
|
40
|
-
require '
|
40
|
+
require 'contxtlservice'
|
41
41
|
require 'mockfs'
|
42
42
|
require 'net/smtp'
|
43
43
|
|
44
44
|
module RhizMail
|
45
|
-
Version = '0.1.
|
45
|
+
Version = '0.1.1'
|
46
46
|
|
47
47
|
# Returns a boolean value describing whether <tt>address</tt> is a plausible
|
48
48
|
# email address format.
|
@@ -56,21 +56,20 @@ module RhizMail
|
|
56
56
|
# The Mailer is the class used to handle mail delivery. However, you usually
|
57
57
|
# don't need to manipulate it directly; it's usually sufficient to call
|
58
58
|
# Message#deliver.
|
59
|
-
class Mailer <
|
59
|
+
class Mailer < ContextualService::Service
|
60
60
|
@@smtpServer = 'localhost'
|
61
61
|
@@smtpClass = Net::SMTP
|
62
62
|
@@messagesSent = []
|
63
63
|
|
64
|
-
#
|
65
|
-
def self.
|
64
|
+
# Flushes record of messages sent.
|
65
|
+
def self.flush; @@messagesSent = []; end
|
66
66
|
|
67
67
|
def self.smtp_class=(smtpClass) # :nodoc:
|
68
68
|
@@smtpClass = smtpClass;
|
69
69
|
end
|
70
70
|
|
71
|
-
# Sends an email message.
|
72
|
-
#
|
73
|
-
# sending the email.
|
71
|
+
# Sends an email message. +email+ needs to respond to +verify_sendable+;
|
72
|
+
# Mailer##send_email will call +verify_sendable+ before sending the email.
|
74
73
|
def send_email(email)
|
75
74
|
email.verify_sendable
|
76
75
|
msg = []
|
@@ -93,24 +92,41 @@ module RhizMail
|
|
93
92
|
end
|
94
93
|
|
95
94
|
class Message
|
96
|
-
|
97
|
-
MULTIPART_CONTENT_TYPE = 1
|
98
|
-
|
99
|
-
attr_accessor :body, :char_set, :content_type, :from_address, :from_name,
|
95
|
+
attr_accessor :body, :charset, :content_type, :from_address, :from_name,
|
100
96
|
:subject, :to_address, :to_name
|
101
97
|
|
102
|
-
#
|
103
|
-
#
|
98
|
+
# Pass a hash to Message.new to create it:
|
99
|
+
#
|
100
|
+
# Message.new(
|
101
|
+
# :body => 'body', :subject => "subject here",
|
102
|
+
# :from_address => 'from@email.com', :to_address => 'to@email.com'
|
103
|
+
# )
|
104
|
+
#
|
105
|
+
# Any of the attributes listed above (under "Attributes") are valid for
|
106
|
+
# this hash.
|
107
|
+
#
|
108
|
+
# The older, deprecated style of instantiation is to take specific
|
109
|
+
# parameters:
|
110
|
+
#
|
111
|
+
# Message.new( subject, to_address, from_address, body )
|
112
|
+
#
|
113
|
+
# Unless they are explicitly set, during creation or afterwards using
|
114
|
+
# accessors, the following defaults apply:
|
104
115
|
# [to_name] nil
|
105
116
|
# [from_name] nil
|
106
117
|
# [content_type] nil
|
107
|
-
# [
|
108
|
-
def initialize(
|
109
|
-
@
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
118
|
+
# [charset] 'iso-8859-1'
|
119
|
+
def initialize( *args )
|
120
|
+
@charset = 'iso-8859-1'
|
121
|
+
if args.first.is_a? Hash
|
122
|
+
args.first.each do |key, val| self.send( key.to_s + '=', val ); end
|
123
|
+
else
|
124
|
+
subject, to_address, from_address, body = args
|
125
|
+
@subject = subject
|
126
|
+
@to_address = to_address
|
127
|
+
@from_address = from_address
|
128
|
+
@body = body
|
129
|
+
end
|
114
130
|
end
|
115
131
|
|
116
132
|
# Sends the email.
|
@@ -135,11 +151,11 @@ module RhizMail
|
|
135
151
|
fromHeader += "#{@from_address}"
|
136
152
|
end
|
137
153
|
headers << fromHeader
|
138
|
-
if content_type ==
|
139
|
-
headers << "Content-Type: text/html; charset=\"#{@
|
154
|
+
if content_type == :html
|
155
|
+
headers << "Content-Type: text/html; charset=\"#{@charset}\""
|
140
156
|
headers << "MIME-Version: 1.0"
|
141
|
-
elsif content_type ==
|
142
|
-
headers << "Content-Type: Multipart/Alternative; charset=\"#{@
|
157
|
+
elsif content_type == :multipart
|
158
|
+
headers << "Content-Type: Multipart/Alternative; charset=\"#{@charset}\""
|
143
159
|
headers << "MIME-Version: 1.0"
|
144
160
|
end
|
145
161
|
headers
|
@@ -155,6 +171,13 @@ module RhizMail
|
|
155
171
|
"Can't send email with blank #{ field.id2name }" )
|
156
172
|
end
|
157
173
|
}
|
174
|
+
content_types = [ :html, :multipart ]
|
175
|
+
unless content_type.nil? or content_types.include?( content_type )
|
176
|
+
raise(
|
177
|
+
InvalidStateError,
|
178
|
+
"I don't recognize the content-type #{ content_type }"
|
179
|
+
)
|
180
|
+
end
|
158
181
|
end
|
159
182
|
end
|
160
183
|
|
@@ -162,7 +185,7 @@ module RhizMail
|
|
162
185
|
# queried to make sure that the right emails were sent out. To set this, call
|
163
186
|
#
|
164
187
|
# mock_mailer = RhizMail::MockMailer.new
|
165
|
-
# RhizMail::set_mailer mock_mailer
|
188
|
+
# RhizMail::Mailer.set_mailer mock_mailer
|
166
189
|
#
|
167
190
|
# Afterwards, every time you call Message#deliver it will be delivered by the
|
168
191
|
# MockMailer.
|
@@ -207,13 +230,17 @@ module RhizMail
|
|
207
230
|
# Then you create an instance of SimpleTemplateMessage and use
|
208
231
|
# SimpleTemplateMessage#substitute to change the contents:
|
209
232
|
#
|
210
|
-
#
|
211
|
-
# 'john.doe@email.com',
|
212
|
-
# '
|
233
|
+
# msg = RhizMail::SimpleTemplateMessage.new(
|
234
|
+
# :to_address => 'john.doe@email.com',
|
235
|
+
# :from_address => 'webmaster@website.com',
|
236
|
+
# :template_file => '/Users/francis/Desktop/template.txt'
|
213
237
|
# )
|
214
|
-
#
|
215
|
-
#
|
216
|
-
#
|
238
|
+
# msg.substitute( 'email', 'john.doe@email.com' )
|
239
|
+
# msg.substitute( 'password', 'p4ssw0rd' )
|
240
|
+
# puts msg.body
|
241
|
+
#
|
242
|
+
# Which prints this:
|
243
|
+
#
|
217
244
|
# Hi! Thanks for joining Website.com. For your reference, here's your signup
|
218
245
|
# information:
|
219
246
|
#
|
@@ -221,20 +248,130 @@ module RhizMail
|
|
221
248
|
# Password: p4ssw0rd
|
222
249
|
#
|
223
250
|
# Thanks!
|
224
|
-
#
|
251
|
+
#
|
252
|
+
# This is the sort of email you're likely to send out a lot, so you can
|
253
|
+
# encapsulate a lot of specific information when you define a subclass:
|
254
|
+
#
|
255
|
+
# class IntroEmail < RhizMail::SimpleTemplateMessage
|
256
|
+
# from_address 'webmaster@website.com'
|
257
|
+
# template '/Users/francis/Desktop/template.txt'
|
258
|
+
# substitute 'email', Proc.new { |msg| msg.user.email }
|
259
|
+
# substitute 'password', Proc.new { |msg| msg.user.password }
|
260
|
+
#
|
261
|
+
# attr_reader :user
|
262
|
+
#
|
263
|
+
# def initialize( user )
|
264
|
+
# @user = user
|
265
|
+
# super( :to_address => user.email )
|
266
|
+
# end
|
267
|
+
# end
|
268
|
+
#
|
269
|
+
# User = Struct.new( :email, :password )
|
270
|
+
#
|
271
|
+
# u = User.new( 'john.doe@email.com', 'p4ssw0rd' )
|
272
|
+
# email = IntroEmail.new u
|
273
|
+
#
|
274
|
+
# This accomplishes the same thing.
|
275
|
+
#
|
276
|
+
# Note that a Proc passed to SimpleTemplateMessage.substitute needs to take
|
277
|
+
# the message itself as a parameter; this is because the Proc belongs to the
|
278
|
+
# class, and doesn't know which instance to use unless it's specified. Don't
|
279
|
+
# forget to use +attr_reader+ to give the Proc access to the message
|
280
|
+
# variables it needs.
|
281
|
+
#
|
282
|
+
# A child of SimpleTemplateMessage should call +super+ at the end of its
|
283
|
+
# +initialize+ method; this will automatically take care of the defined
|
284
|
+
# substitutions.
|
225
285
|
#
|
226
286
|
# If you call SimpleTemplateMessage#deliver without doing all the
|
227
287
|
# substitutions required by the template, it will raise an InvalidStateError.
|
228
288
|
# (Getting an exception is probably better than sending a customer an email
|
229
289
|
# with funny symbols in it.)
|
230
290
|
class SimpleTemplateMessage < Message
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
291
|
+
SubclassAttributes = Struct.new( :substitutions, :template, :from_address )
|
292
|
+
|
293
|
+
@@subclass_attributes = Hash.new { |hash, key|
|
294
|
+
hash[key] = SubclassAttributes.new( {} )
|
295
|
+
}
|
296
|
+
|
297
|
+
def self.attributes # :nodoc:
|
298
|
+
@@subclass_attributes[self]
|
299
|
+
end
|
300
|
+
|
301
|
+
# Sets the +from_address+ of every message of this class; use this to
|
302
|
+
# parameterize children of SimpleTemplateMessage.
|
303
|
+
def self.from_address( from_address )
|
304
|
+
attributes.from_address = from_address
|
305
|
+
end
|
306
|
+
|
307
|
+
# Sets the +template+ for every message of this class; use this to
|
308
|
+
# parameterize children of SimpleTemplateMessage.
|
309
|
+
def self.template( template_file )
|
310
|
+
attributes.template = template_file
|
311
|
+
end
|
312
|
+
|
313
|
+
# Adds a substitution to use on every message of this class. Use this to
|
314
|
+
# parameterize children of SimpleTemplateMessage, like so:
|
315
|
+
#
|
316
|
+
# class IntroEmail < RhizMail::SimpleTemplateMessage
|
317
|
+
# substitute 'email', Proc.new { |msg| msg.user.email }
|
318
|
+
# substitute 'password', Proc.new { |msg| msg.user.password }
|
319
|
+
# ...
|
320
|
+
# end
|
321
|
+
#
|
322
|
+
# Note that a Proc passed to SimpleTemplateMessage.substitute needs to take
|
323
|
+
# the message itself as a parameter; this is because the Proc belongs to
|
324
|
+
# the class, and doesn't know which instance to use unless it's specified.
|
325
|
+
# Don't forget to use +attr_reader+ to give the Proc access to the message
|
326
|
+
# variables it needs.
|
327
|
+
def self.substitute( token, proc )
|
328
|
+
attributes.substitutions[token] = proc
|
329
|
+
end
|
330
|
+
|
331
|
+
# Pass a hash to SimpleTemplateMessage.new to create it:
|
332
|
+
#
|
333
|
+
# msg = SimpleTemplateMessage.new(
|
334
|
+
# :to_address => 'john.doe@email.com',
|
335
|
+
# :from_address => 'webmaster@website.com',
|
336
|
+
# :template_file => '/Users/francis/Desktop/template.txt'
|
337
|
+
# )
|
338
|
+
#
|
339
|
+
# :template_file is a required key. Other valid keys are :from_address,
|
340
|
+
# :from_name, :to_address, :to_name.
|
341
|
+
#
|
342
|
+
# Defaults for :template_file and :from_address can be set with
|
343
|
+
# SimpleTemplateMessage.template and SimpleTemplateMessage.from_address.
|
344
|
+
#
|
345
|
+
# An older, deprecated style of creation is to take specific parameters:
|
346
|
+
#
|
347
|
+
# msg = SimpleTemplateMessage.new(
|
348
|
+
# 'john.doe@email.com', 'webmaster@website.com',
|
349
|
+
# '/Users/francis/Desktop/template.txt'
|
350
|
+
# )
|
351
|
+
def initialize( *args )
|
352
|
+
if args.first.is_a? Hash
|
353
|
+
h = args.first
|
354
|
+
verboten_keys = [ :body, :charset, :content_type, :subject ]
|
355
|
+
raise InvalidStateError unless ( h.keys & verboten_keys ).empty?
|
356
|
+
template_file = nil
|
357
|
+
if h[:template_file]
|
358
|
+
template_file = h[:template_file]
|
359
|
+
h.delete :template_file
|
360
|
+
else
|
361
|
+
template_file = class_attributes.template
|
362
|
+
end
|
363
|
+
raise InvalidStateError if template_file.nil?
|
364
|
+
read_template template_file
|
365
|
+
if class_attributes.from_address and !h[:from_address]
|
366
|
+
h[:from_address] = class_attributes.from_address
|
367
|
+
end
|
368
|
+
super h
|
369
|
+
else
|
370
|
+
to_address, from_address, template_file = args
|
371
|
+
read_template template_file
|
372
|
+
super( @subject, to_address, from_address )
|
373
|
+
end
|
374
|
+
generate_body @template
|
238
375
|
end
|
239
376
|
|
240
377
|
def body_template( template ) # :nodoc:
|
@@ -250,21 +387,45 @@ module RhizMail
|
|
250
387
|
body
|
251
388
|
end
|
252
389
|
|
390
|
+
def class_attributes # :nodoc:
|
391
|
+
self.class.attributes
|
392
|
+
end
|
393
|
+
|
253
394
|
def generate_body( template ) # :nodoc:
|
254
|
-
body = body_template( template )
|
255
|
-
tokens = body.scan(/<%\s*(\S*)\s*%>/).collect { |matchArray|
|
395
|
+
@body = body_template( template )
|
396
|
+
tokens = @body.scan(/<%\s*(\S*)\s*%>/).collect { |matchArray|
|
256
397
|
matchArray[0]
|
257
398
|
}
|
258
399
|
tokens.each { |token|
|
259
|
-
if ( proc = substitutions[token] )
|
400
|
+
if ( proc = substitutions[token] )
|
401
|
+
substitute( token, proc )
|
402
|
+
elsif ( proc = class_attributes.substitutions[token] )
|
403
|
+
substitute( token, proc )
|
404
|
+
end
|
260
405
|
}
|
261
406
|
body
|
262
407
|
end
|
408
|
+
|
409
|
+
def read_template( template_file ) # :nodoc:
|
410
|
+
@template = ''
|
411
|
+
MockFS.file.open( template_file ) { |file| @template = file.gets nil }
|
412
|
+
@template =~ /Subject: (.*)/
|
413
|
+
@subject = $1
|
414
|
+
end
|
263
415
|
|
416
|
+
# Substitute a token with a value in the email body.
|
264
417
|
def substitute(token, value_or_proc )
|
265
418
|
regexp = Regexp.new( "<%\s*#{ token }\s*%>", true )
|
266
419
|
@body.gsub!( regexp ){ |match|
|
267
|
-
value_or_proc.
|
420
|
+
if value_or_proc.is_a? Proc
|
421
|
+
if value_or_proc.arity == 1
|
422
|
+
value_or_proc.call self
|
423
|
+
else
|
424
|
+
value_or_proc.call
|
425
|
+
end
|
426
|
+
else
|
427
|
+
value_or_proc
|
428
|
+
end
|
268
429
|
}
|
269
430
|
end
|
270
431
|
|
@@ -274,6 +435,8 @@ module RhizMail
|
|
274
435
|
# return a hash of tokens to either values or Procs.
|
275
436
|
def substitutions; {}; end
|
276
437
|
|
438
|
+
# Mailer will call this before sending this message; this will fail if any
|
439
|
+
# tokens are left unsubstituted.
|
277
440
|
def verify_sendable
|
278
441
|
super
|
279
442
|
if @body =~ /<%/ || @body =~ /%>/
|
data/lib/rhizmail.rb~
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# RhizMail is a test-friendly library for sending out customized emails. This
|
2
|
-
# is the library we use day-to-day at
|
3
|
-
# 100 customized emails a day.
|
2
|
+
# is the library we use day-to-day at http://rhizome.org, where we send out
|
3
|
+
# more than 100 customized emails a day.
|
4
4
|
#
|
5
5
|
# You can view the Rubyforge project page at
|
6
6
|
# http://rubyforge.org/projects/rhizmail.
|
@@ -21,13 +21,23 @@
|
|
21
21
|
#
|
22
22
|
# def test_send_site_message
|
23
23
|
# mock_mailer = RhizMail::MockMailer.new
|
24
|
-
# RhizMail::set_mailer mock_mailer
|
25
|
-
# send_site_message( 'bill@yahoo.com', 'Bill Smith', 'Hi,
|
24
|
+
# RhizMail::Mailer.set_mailer mock_mailer
|
25
|
+
# send_site_message( 'bill@yahoo.com', 'Bill Smith', 'Hi, Bill!' )
|
26
26
|
# assert_equal( 1, mock_mailer.messages_sent )
|
27
27
|
# assert_equal( 'Bill Smith', mock_mailer.messages_sent.first.to_name )
|
28
28
|
# end
|
29
|
+
#
|
30
|
+
# RhizMail comes with a SimpleTemplateMessage class, suited for times when you
|
31
|
+
# want email templating that is simpler than Amrita or ERB.
|
32
|
+
#
|
33
|
+
# == Dependencies
|
34
|
+
#
|
35
|
+
# RhizMail depends on two external libraries:
|
36
|
+
#
|
37
|
+
# * ContextualService: http://contxtlservice.rubyforge.org
|
38
|
+
# * MockFS: http://mockfs.rubyforge.org
|
29
39
|
|
30
|
-
require '
|
40
|
+
require 'contxtlservice'
|
31
41
|
require 'mockfs'
|
32
42
|
require 'net/smtp'
|
33
43
|
|
@@ -46,19 +56,21 @@ module RhizMail
|
|
46
56
|
# The Mailer is the class used to handle mail delivery. However, you usually
|
47
57
|
# don't need to manipulate it directly; it's usually sufficient to call
|
48
58
|
# Message#deliver.
|
49
|
-
class Mailer <
|
59
|
+
class Mailer < ContextualService::Service
|
50
60
|
@@smtpServer = 'localhost'
|
51
61
|
@@smtpClass = Net::SMTP
|
52
62
|
@@messagesSent = []
|
53
63
|
|
54
|
-
#
|
55
|
-
def self.
|
64
|
+
# Flushes record of messages sent.
|
65
|
+
def self.flush; @@messagesSent = []; end
|
56
66
|
|
57
|
-
def self.
|
67
|
+
def self.smtp_class=(smtpClass) # :nodoc:
|
58
68
|
@@smtpClass = smtpClass;
|
59
69
|
end
|
60
70
|
|
61
|
-
# Sends an email message.
|
71
|
+
# Sends an email message. Will call +email+ needs to respond to
|
72
|
+
# +verify_sendable+; Mailer##send_email will call +verify_sendable+ before
|
73
|
+
# sending the email.
|
62
74
|
def send_email(email)
|
63
75
|
email.verify_sendable
|
64
76
|
msg = []
|
@@ -81,10 +93,7 @@ module RhizMail
|
|
81
93
|
end
|
82
94
|
|
83
95
|
class Message
|
84
|
-
|
85
|
-
MULTIPART_CONTENT_TYPE = 1
|
86
|
-
|
87
|
-
attr_accessor :body, :char_set, :content_type, :from_address, :from_name,
|
96
|
+
attr_accessor :body, :charset, :content_type, :from_address, :from_name,
|
88
97
|
:subject, :to_address, :to_name
|
89
98
|
|
90
99
|
# Unless they are explicitly set using accessors, the following defaults
|
@@ -92,13 +101,18 @@ module RhizMail
|
|
92
101
|
# [to_name] nil
|
93
102
|
# [from_name] nil
|
94
103
|
# [content_type] nil
|
95
|
-
# [
|
96
|
-
def initialize(
|
97
|
-
@
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
104
|
+
# [charset] 'iso-8859-1'
|
105
|
+
def initialize( *args )
|
106
|
+
@charset = 'iso-8859-1'
|
107
|
+
if args.first.is_a? Hash
|
108
|
+
args.first.each do |key, val| self.send( key.to_s + '=', val ); end
|
109
|
+
else
|
110
|
+
subject, to_address, from_address, body = args
|
111
|
+
@subject = subject
|
112
|
+
@to_address = to_address
|
113
|
+
@from_address = from_address
|
114
|
+
@body = body
|
115
|
+
end
|
102
116
|
end
|
103
117
|
|
104
118
|
# Sends the email.
|
@@ -123,17 +137,17 @@ module RhizMail
|
|
123
137
|
fromHeader += "#{@from_address}"
|
124
138
|
end
|
125
139
|
headers << fromHeader
|
126
|
-
if content_type ==
|
127
|
-
headers << "Content-Type: text/html; charset=\"#{@
|
140
|
+
if content_type == :html
|
141
|
+
headers << "Content-Type: text/html; charset=\"#{@charset}\""
|
128
142
|
headers << "MIME-Version: 1.0"
|
129
|
-
elsif content_type ==
|
130
|
-
headers << "Content-Type: Multipart/Alternative; charset=\"#{@
|
143
|
+
elsif content_type == :multipart
|
144
|
+
headers << "Content-Type: Multipart/Alternative; charset=\"#{@charset}\""
|
131
145
|
headers << "MIME-Version: 1.0"
|
132
146
|
end
|
133
147
|
headers
|
134
148
|
end
|
135
149
|
|
136
|
-
#
|
150
|
+
# Mailer calls this before sending a message; subclasses can override this
|
137
151
|
# if they want to ensure that certain parts of the message are valid before
|
138
152
|
# sending.
|
139
153
|
def verify_sendable
|
@@ -143,6 +157,13 @@ module RhizMail
|
|
143
157
|
"Can't send email with blank #{ field.id2name }" )
|
144
158
|
end
|
145
159
|
}
|
160
|
+
content_types = [ :html, :multipart ]
|
161
|
+
unless content_type.nil? or content_types.include?( content_type )
|
162
|
+
raise(
|
163
|
+
InvalidStateError,
|
164
|
+
"I don't recognize the content-type #{ content_type }"
|
165
|
+
)
|
166
|
+
end
|
146
167
|
end
|
147
168
|
end
|
148
169
|
|
@@ -150,7 +171,7 @@ module RhizMail
|
|
150
171
|
# queried to make sure that the right emails were sent out. To set this, call
|
151
172
|
#
|
152
173
|
# mock_mailer = RhizMail::MockMailer.new
|
153
|
-
# RhizMail::set_mailer mock_mailer
|
174
|
+
# RhizMail::Mailer.set_mailer mock_mailer
|
154
175
|
#
|
155
176
|
# Afterwards, every time you call Message#deliver it will be delivered by the
|
156
177
|
# MockMailer.
|
@@ -162,7 +183,8 @@ module RhizMail
|
|
162
183
|
# Clear out the history of messages sent.
|
163
184
|
def flush; @messages_sent = []; end
|
164
185
|
|
165
|
-
# Pretends to send an email
|
186
|
+
# Pretends to send an email, recording it in +messages_sent+ to allow
|
187
|
+
# inspection later.
|
166
188
|
def send_email(email)
|
167
189
|
email.verify_sendable
|
168
190
|
@messages_sent << email
|
@@ -177,7 +199,7 @@ module RhizMail
|
|
177
199
|
# As an example, let's say you've got an email to send out whenever somebody
|
178
200
|
# signs up to your website. The template could look like this:
|
179
201
|
#
|
180
|
-
# $ cat
|
202
|
+
# $ cat /Users/francis/Desktop/template.txt
|
181
203
|
# Subject: Thanks for joining Website.com!
|
182
204
|
#
|
183
205
|
# Hi! Thanks for joining Website.com. For your reference, here's your signup
|
@@ -192,24 +214,76 @@ module RhizMail
|
|
192
214
|
# and should be followed by a blank line.
|
193
215
|
#
|
194
216
|
# Then you create an instance of SimpleTemplateMessage and use
|
195
|
-
# SimpleTemplateMessage#substitute to change the :
|
217
|
+
# SimpleTemplateMessage#substitute to change the contents:
|
196
218
|
#
|
197
|
-
#
|
198
|
-
|
199
|
-
|
219
|
+
# irb> msg = RhizMail::SimpleTemplateMessage.new(
|
220
|
+
# 'john.doe@email.com', 'webmaster@website.com',
|
221
|
+
# '/Users/francis/Desktop/template.txt'
|
222
|
+
# )
|
223
|
+
# irb> msg.substitute( 'email', 'john.doe@email.com' )
|
224
|
+
# irb> msg.substitute( 'password', 'p4ssw0rd' )
|
225
|
+
# irb> puts msg.body
|
226
|
+
# Hi! Thanks for joining Website.com. For your reference, here's your signup
|
227
|
+
# information:
|
228
|
+
#
|
229
|
+
# Email: john.doe@email.com
|
230
|
+
# Password: p4ssw0rd
|
231
|
+
#
|
232
|
+
# Thanks!
|
233
|
+
# irb> msg.deliver
|
234
|
+
#
|
235
|
+
# If you call SimpleTemplateMessage#deliver without doing all the
|
236
|
+
# substitutions required by the template, it will raise an InvalidStateError.
|
237
|
+
# (Getting an exception is probably better than sending a customer an email
|
238
|
+
# with funny symbols in it.)
|
200
239
|
class SimpleTemplateMessage < Message
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
240
|
+
SubclassAttributes = Struct.new( :substitutions, :template, :from_address )
|
241
|
+
|
242
|
+
@@subclass_attributes = Hash.new { |hash, key|
|
243
|
+
hash[key] = SubclassAttributes.new( {} )
|
244
|
+
}
|
245
|
+
|
246
|
+
def self.attributes; @@subclass_attributes[self]; end
|
247
|
+
|
248
|
+
def self.from_address( from_address )
|
249
|
+
attributes.from_address = from_address
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.template( template_file )
|
253
|
+
attributes.template = template_file
|
254
|
+
end
|
255
|
+
|
256
|
+
def self.substitute( token, proc )
|
257
|
+
attributes.substitutions[token] = proc
|
258
|
+
end
|
259
|
+
|
260
|
+
def initialize( *args )
|
261
|
+
if args.first.is_a? Hash
|
262
|
+
h = args.first
|
263
|
+
verboten_keys = [ :body, :charset, :content_type, :subject ]
|
264
|
+
raise InvalidStateError unless ( h.keys & verboten_keys ).empty?
|
265
|
+
template_file = nil
|
266
|
+
if h[:template_file]
|
267
|
+
template_file = h[:template_file]
|
268
|
+
h.delete :template_file
|
269
|
+
else
|
270
|
+
template_file = class_attributes.template
|
271
|
+
end
|
272
|
+
raise InvalidStateError if template_file.nil?
|
273
|
+
read_template template_file
|
274
|
+
if class_attributes.from_address and !h[:from_address]
|
275
|
+
h[:from_address] = class_attributes.from_address
|
276
|
+
end
|
277
|
+
super h
|
278
|
+
else
|
279
|
+
to_address, from_address, template_file = args
|
280
|
+
read_template template_file
|
281
|
+
super( @subject, to_address, from_address )
|
282
|
+
end
|
283
|
+
generate_body @template
|
210
284
|
end
|
211
285
|
|
212
|
-
def
|
286
|
+
def body_template( template ) # :nodoc:
|
213
287
|
body = ''
|
214
288
|
blank_line_found = false
|
215
289
|
template.each { |line|
|
@@ -222,28 +296,51 @@ module RhizMail
|
|
222
296
|
body
|
223
297
|
end
|
224
298
|
|
225
|
-
def
|
226
|
-
|
227
|
-
def get_substitions; {}; end
|
299
|
+
def class_attributes; self.class.attributes; end
|
228
300
|
|
229
|
-
def
|
230
|
-
@body =
|
301
|
+
def generate_body( template ) # :nodoc:
|
302
|
+
@body = body_template( template )
|
231
303
|
tokens = @body.scan(/<%\s*(\S*)\s*%>/).collect { |matchArray|
|
232
304
|
matchArray[0]
|
233
305
|
}
|
234
|
-
substitutions = get_substitions
|
235
306
|
tokens.each { |token|
|
236
|
-
if ( proc = substitutions[token] )
|
307
|
+
if ( proc = substitutions[token] )
|
308
|
+
substitute( token, proc )
|
309
|
+
elsif ( proc = class_attributes.substitutions[token] )
|
310
|
+
substitute( token, proc )
|
311
|
+
end
|
237
312
|
}
|
313
|
+
body
|
314
|
+
end
|
315
|
+
|
316
|
+
def read_template( template_file )
|
317
|
+
@template = ''
|
318
|
+
MockFS.file.open( template_file ) { |file| @template = file.gets nil }
|
319
|
+
@template =~ /Subject: (.*)/
|
320
|
+
@subject = $1
|
238
321
|
end
|
239
322
|
|
240
323
|
def substitute(token, value_or_proc )
|
241
|
-
regexp =
|
324
|
+
regexp = Regexp.new( "<%\s*#{ token }\s*%>", true )
|
242
325
|
@body.gsub!( regexp ){ |match|
|
243
|
-
value_or_proc.
|
326
|
+
if value_or_proc.is_a? Proc
|
327
|
+
if value_or_proc.arity == 1
|
328
|
+
value_or_proc.call self
|
329
|
+
else
|
330
|
+
value_or_proc.call
|
331
|
+
end
|
332
|
+
else
|
333
|
+
value_or_proc
|
334
|
+
end
|
244
335
|
}
|
245
336
|
end
|
246
337
|
|
338
|
+
# If you want to define a subclass of SimpleTemplateMessage, you can
|
339
|
+
# override +substitutions+ to automatically define a set of substitutions
|
340
|
+
# to make when you create a new instance. +substitutions+ should always
|
341
|
+
# return a hash of tokens to either values or Procs.
|
342
|
+
def substitutions; {}; end
|
343
|
+
|
247
344
|
def verify_sendable
|
248
345
|
super
|
249
346
|
if @body =~ /<%/ || @body =~ /%>/
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.6
|
|
3
3
|
specification_version: 1
|
4
4
|
name: rhizmail
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
7
|
-
date: 2005-
|
6
|
+
version: 0.1.1
|
7
|
+
date: 2005-08-14
|
8
8
|
summary: RhizMail is a test-friendly library for sending out customized emails.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -39,7 +39,7 @@ extensions: []
|
|
39
39
|
requirements: []
|
40
40
|
dependencies:
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: contxtlservice
|
43
43
|
version_requirement:
|
44
44
|
version_requirements: !ruby/object:Gem::Version::Requirement
|
45
45
|
requirements:
|