rhizmail 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|