rhizmail 0.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/lib/rhizmail.rb +290 -0
- data/lib/rhizmail.rb~ +260 -0
- metadata +60 -0
data/lib/rhizmail.rb
ADDED
@@ -0,0 +1,290 @@
|
|
1
|
+
# RhizMail is a test-friendly library for sending out customized emails. This
|
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
|
+
#
|
5
|
+
# You can view the Rubyforge project page at
|
6
|
+
# http://rubyforge.org/projects/rhizmail.
|
7
|
+
#
|
8
|
+
# To use RhizMail, create an email and deliver it.
|
9
|
+
#
|
10
|
+
# def send_site_message( to_address, to_name, body )
|
11
|
+
# msg = RhizMail::Message.new(
|
12
|
+
# 'New features at website.com', to_address, 'webmaster@mysite.com',
|
13
|
+
# body
|
14
|
+
# )
|
15
|
+
# msg.to_name = to_name
|
16
|
+
# msg.deliver
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# If you want to test this message, you can set the MockMailer to take place
|
20
|
+
# of the default Mailer:
|
21
|
+
#
|
22
|
+
# def test_send_site_message
|
23
|
+
# mock_mailer = RhizMail::MockMailer.new
|
24
|
+
# RhizMail::set_mailer mock_mailer
|
25
|
+
# send_site_message( 'bill@yahoo.com', 'Bill Smith', 'Hi, Bill!' )
|
26
|
+
# assert_equal( 1, mock_mailer.messages_sent )
|
27
|
+
# assert_equal( 'Bill Smith', mock_mailer.messages_sent.first.to_name )
|
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
|
+
# * Lafcadio: http://lafcadio.rubyforge.org
|
38
|
+
# * MockFS: http://mockfs.rubyforge.org
|
39
|
+
|
40
|
+
require 'lafcadio'
|
41
|
+
require 'mockfs'
|
42
|
+
require 'net/smtp'
|
43
|
+
|
44
|
+
module RhizMail
|
45
|
+
Version = '0.1.0'
|
46
|
+
|
47
|
+
# Returns a boolean value describing whether <tt>address</tt> is a plausible
|
48
|
+
# email address format.
|
49
|
+
def self.valid_address(address); (address =~ /\w@\w*\./) != nil; end
|
50
|
+
|
51
|
+
# InvalidStateError is raised by SimpleTemplateMessage if you try to send it
|
52
|
+
# out without doing all the necessary template substitutions.
|
53
|
+
class InvalidStateError < RuntimeError
|
54
|
+
end
|
55
|
+
|
56
|
+
# The Mailer is the class used to handle mail delivery. However, you usually
|
57
|
+
# don't need to manipulate it directly; it's usually sufficient to call
|
58
|
+
# Message#deliver.
|
59
|
+
class Mailer < Lafcadio::ContextualService
|
60
|
+
@@smtpServer = 'localhost'
|
61
|
+
@@smtpClass = Net::SMTP
|
62
|
+
@@messagesSent = []
|
63
|
+
|
64
|
+
# Resets record of messages sent.
|
65
|
+
def self.reset; @@messagesSent = []; end
|
66
|
+
|
67
|
+
def self.smtp_class=(smtpClass) # :nodoc:
|
68
|
+
@@smtpClass = smtpClass;
|
69
|
+
end
|
70
|
+
|
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.
|
74
|
+
def send_email(email)
|
75
|
+
email.verify_sendable
|
76
|
+
msg = []
|
77
|
+
email.headers.each { |header| msg << "#{header}\n" }
|
78
|
+
msg << "\n"
|
79
|
+
msg << email.body
|
80
|
+
begin
|
81
|
+
@@smtpClass.start( @@smtpServer ) { |smtp|
|
82
|
+
smtp.sendmail(msg, email.from_address, [ email.to_address ])
|
83
|
+
}
|
84
|
+
@@messagesSent << email
|
85
|
+
rescue Net::ProtoFatalError, TimeoutError, Errno::ECONNREFUSED,
|
86
|
+
Errno::ECONNRESET
|
87
|
+
# whatever
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns an array of what messages have been sent.
|
92
|
+
def messages_sent; @@messagesSent; end
|
93
|
+
end
|
94
|
+
|
95
|
+
class Message
|
96
|
+
HTML_CONTENT_TYPE = 0
|
97
|
+
MULTIPART_CONTENT_TYPE = 1
|
98
|
+
|
99
|
+
attr_accessor :body, :char_set, :content_type, :from_address, :from_name,
|
100
|
+
:subject, :to_address, :to_name
|
101
|
+
|
102
|
+
# Unless they are explicitly set using accessors, the following defaults
|
103
|
+
# apply:
|
104
|
+
# [to_name] nil
|
105
|
+
# [from_name] nil
|
106
|
+
# [content_type] nil
|
107
|
+
# [char_set] 'iso-8859-1'
|
108
|
+
def initialize( subject, to_address, from_address, body = nil )
|
109
|
+
@subject = subject
|
110
|
+
@to_address = to_address
|
111
|
+
@from_address = from_address
|
112
|
+
@char_set = 'iso-8859-1'
|
113
|
+
@body = body
|
114
|
+
end
|
115
|
+
|
116
|
+
# Sends the email.
|
117
|
+
def deliver; Mailer.get_mailer.send_email( self ); end
|
118
|
+
|
119
|
+
# Returns an array of strings describing the headers for this email
|
120
|
+
# message.
|
121
|
+
def headers
|
122
|
+
headers = []
|
123
|
+
headers << "Subject: #{@subject}"
|
124
|
+
toHeader = "To: "
|
125
|
+
if @to_name
|
126
|
+
toHeader += " #{@to_name} <#{@to_address}>"
|
127
|
+
else
|
128
|
+
toHeader += " #{@to_address}"
|
129
|
+
end
|
130
|
+
headers << toHeader
|
131
|
+
fromHeader = "From: "
|
132
|
+
if @from_name
|
133
|
+
fromHeader += "#{@from_name} <#{@from_address}>"
|
134
|
+
else
|
135
|
+
fromHeader += "#{@from_address}"
|
136
|
+
end
|
137
|
+
headers << fromHeader
|
138
|
+
if content_type == HTML_CONTENT_TYPE
|
139
|
+
headers << "Content-Type: text/html; charset=\"#{@char_set}\""
|
140
|
+
headers << "MIME-Version: 1.0"
|
141
|
+
elsif content_type == MULTIPART_CONTENT_TYPE
|
142
|
+
headers << "Content-Type: Multipart/Alternative; charset=\"#{@char_set}\""
|
143
|
+
headers << "MIME-Version: 1.0"
|
144
|
+
end
|
145
|
+
headers
|
146
|
+
end
|
147
|
+
|
148
|
+
# Mailer calls this before sending a message; subclasses can override this
|
149
|
+
# if they want to ensure that certain parts of the message are valid before
|
150
|
+
# sending.
|
151
|
+
def verify_sendable
|
152
|
+
[ :subject, :from_address, :to_address, :body ].each { |field|
|
153
|
+
if self.send( field ).nil?
|
154
|
+
raise( InvalidStateError,
|
155
|
+
"Can't send email with blank #{ field.id2name }" )
|
156
|
+
end
|
157
|
+
}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# The MockMailer can be swapped out for the Mailer in test code, and then
|
162
|
+
# queried to make sure that the right emails were sent out. To set this, call
|
163
|
+
#
|
164
|
+
# mock_mailer = RhizMail::MockMailer.new
|
165
|
+
# RhizMail::set_mailer mock_mailer
|
166
|
+
#
|
167
|
+
# Afterwards, every time you call Message#deliver it will be delivered by the
|
168
|
+
# MockMailer.
|
169
|
+
class MockMailer
|
170
|
+
attr_reader :messages_sent
|
171
|
+
|
172
|
+
def initialize; flush; end
|
173
|
+
|
174
|
+
# Clear out the history of messages sent.
|
175
|
+
def flush; @messages_sent = []; end
|
176
|
+
|
177
|
+
# Pretends to send an email, recording it in +messages_sent+ to allow
|
178
|
+
# inspection later.
|
179
|
+
def send_email(email)
|
180
|
+
email.verify_sendable
|
181
|
+
@messages_sent << email
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# The SimpleTemplateMessage is designed to let a programmer define a simple
|
186
|
+
# set of tags for a certain sort of message and then substitute them in-code.
|
187
|
+
# It's intended to be simple enough that non-programmers can edit it without
|
188
|
+
# too much confusion.
|
189
|
+
#
|
190
|
+
# As an example, let's say you've got an email to send out whenever somebody
|
191
|
+
# signs up to your website. The template could look like this:
|
192
|
+
#
|
193
|
+
# $ cat /Users/francis/Desktop/template.txt
|
194
|
+
# Subject: Thanks for joining Website.com!
|
195
|
+
#
|
196
|
+
# Hi! Thanks for joining Website.com. For your reference, here's your signup
|
197
|
+
# information:
|
198
|
+
#
|
199
|
+
# Email: <% email %>
|
200
|
+
# Password: <% password %>
|
201
|
+
#
|
202
|
+
# Thanks!
|
203
|
+
#
|
204
|
+
# Note that the first line needs to be in the format "Subject: [ SUBJECT ]",
|
205
|
+
# and should be followed by a blank line.
|
206
|
+
#
|
207
|
+
# Then you create an instance of SimpleTemplateMessage and use
|
208
|
+
# SimpleTemplateMessage#substitute to change the contents:
|
209
|
+
#
|
210
|
+
# irb> msg = RhizMail::SimpleTemplateMessage.new(
|
211
|
+
# 'john.doe@email.com', 'webmaster@website.com',
|
212
|
+
# '/Users/francis/Desktop/template.txt'
|
213
|
+
# )
|
214
|
+
# irb> msg.substitute( 'email', 'john.doe@email.com' )
|
215
|
+
# irb> msg.substitute( 'password', 'p4ssw0rd' )
|
216
|
+
# irb> puts msg.body
|
217
|
+
# Hi! Thanks for joining Website.com. For your reference, here's your signup
|
218
|
+
# information:
|
219
|
+
#
|
220
|
+
# Email: john.doe@email.com
|
221
|
+
# Password: p4ssw0rd
|
222
|
+
#
|
223
|
+
# Thanks!
|
224
|
+
# irb> msg.deliver
|
225
|
+
#
|
226
|
+
# If you call SimpleTemplateMessage#deliver without doing all the
|
227
|
+
# substitutions required by the template, it will raise an InvalidStateError.
|
228
|
+
# (Getting an exception is probably better than sending a customer an email
|
229
|
+
# with funny symbols in it.)
|
230
|
+
class SimpleTemplateMessage < Message
|
231
|
+
def initialize( to_address, from_address, template_file )
|
232
|
+
template = ''
|
233
|
+
MockFS.file.open( template_file ) { |file| template = file.gets nil }
|
234
|
+
template =~ /Subject: (.*)/
|
235
|
+
subject = $1
|
236
|
+
super( subject, to_address, from_address )
|
237
|
+
@body = generate_body template
|
238
|
+
end
|
239
|
+
|
240
|
+
def body_template( template ) # :nodoc:
|
241
|
+
body = ''
|
242
|
+
blank_line_found = false
|
243
|
+
template.each { |line|
|
244
|
+
if blank_line_found
|
245
|
+
body += line
|
246
|
+
else
|
247
|
+
blank_line_found = true if line =~ /^\n/
|
248
|
+
end
|
249
|
+
}
|
250
|
+
body
|
251
|
+
end
|
252
|
+
|
253
|
+
def generate_body( template ) # :nodoc:
|
254
|
+
body = body_template( template )
|
255
|
+
tokens = body.scan(/<%\s*(\S*)\s*%>/).collect { |matchArray|
|
256
|
+
matchArray[0]
|
257
|
+
}
|
258
|
+
tokens.each { |token|
|
259
|
+
if ( proc = substitutions[token] ); substitute( token, proc ); end
|
260
|
+
}
|
261
|
+
body
|
262
|
+
end
|
263
|
+
|
264
|
+
def substitute(token, value_or_proc )
|
265
|
+
regexp = Regexp.new( "<%\s*#{ token }\s*%>", true )
|
266
|
+
@body.gsub!( regexp ){ |match|
|
267
|
+
value_or_proc.class <= Proc ? value_or_proc.call : value_or_proc
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
# If you want to define a subclass of SimpleTemplateMessage, you can
|
272
|
+
# override +substitutions+ to automatically define a set of substitutions
|
273
|
+
# to make when you create a new instance. +substitutions+ should always
|
274
|
+
# return a hash of tokens to either values or Procs.
|
275
|
+
def substitutions; {}; end
|
276
|
+
|
277
|
+
def verify_sendable
|
278
|
+
super
|
279
|
+
if @body =~ /<%/ || @body =~ /%>/
|
280
|
+
raise InvalidStateError, "substitution failed: #{ @body }", caller
|
281
|
+
elsif @subject =~/<%/ || @subject =~ /%>/
|
282
|
+
raise( InvalidStateError,
|
283
|
+
"substitution failed with subject: #{ @subject }",
|
284
|
+
caller )
|
285
|
+
elsif @body == ''
|
286
|
+
raise( InvalidStateError, "can't send email with blank body", caller )
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
data/lib/rhizmail.rb~
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
# RhizMail is a test-friendly library for sending out customized emails. This
|
2
|
+
# is the library we use day-to-day at Rhizome.org, where we send out more than
|
3
|
+
# 100 customized emails a day.
|
4
|
+
#
|
5
|
+
# You can view the Rubyforge project page at
|
6
|
+
# http://rubyforge.org/projects/rhizmail.
|
7
|
+
#
|
8
|
+
# To use RhizMail, create an email and deliver it.
|
9
|
+
#
|
10
|
+
# def send_site_message( to_address, to_name, body )
|
11
|
+
# msg = RhizMail::Message.new(
|
12
|
+
# 'New features at website.com', to_address, 'webmaster@mysite.com',
|
13
|
+
# body
|
14
|
+
# )
|
15
|
+
# msg.to_name = to_name
|
16
|
+
# msg.deliver
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# If you want to test this message, you can set the MockMailer to take place
|
20
|
+
# of the default Mailer:
|
21
|
+
#
|
22
|
+
# def test_send_site_message
|
23
|
+
# mock_mailer = RhizMail::MockMailer.new
|
24
|
+
# RhizMail::set_mailer mock_mailer
|
25
|
+
# send_site_message( 'bill@yahoo.com', 'Bill Smith', 'Hi, Bill1' )
|
26
|
+
# assert_equal( 1, mock_mailer.messages_sent )
|
27
|
+
# assert_equal( 'Bill Smith', mock_mailer.messages_sent.first.to_name )
|
28
|
+
# end
|
29
|
+
|
30
|
+
require 'lafcadio'
|
31
|
+
require 'mockfs'
|
32
|
+
require 'net/smtp'
|
33
|
+
|
34
|
+
module RhizMail
|
35
|
+
Version = '0.1.0'
|
36
|
+
|
37
|
+
# Returns a boolean value describing whether <tt>address</tt> is a plausible
|
38
|
+
# email address format.
|
39
|
+
def self.valid_address(address); (address =~ /\w@\w*\./) != nil; end
|
40
|
+
|
41
|
+
# InvalidStateError is raised by SimpleTemplateMessage if you try to send it
|
42
|
+
# out without doing all the necessary template substitutions.
|
43
|
+
class InvalidStateError < RuntimeError
|
44
|
+
end
|
45
|
+
|
46
|
+
# The Mailer is the class used to handle mail delivery. However, you usually
|
47
|
+
# don't need to manipulate it directly; it's usually sufficient to call
|
48
|
+
# Message#deliver.
|
49
|
+
class Mailer < Lafcadio::ContextualService
|
50
|
+
@@smtpServer = 'localhost'
|
51
|
+
@@smtpClass = Net::SMTP
|
52
|
+
@@messagesSent = []
|
53
|
+
|
54
|
+
# Resets record of messages sent.
|
55
|
+
def self.reset; @@messagesSent = []; end
|
56
|
+
|
57
|
+
def self.set_smtp_class(smtpClass) # :nodoc:
|
58
|
+
@@smtpClass = smtpClass;
|
59
|
+
end
|
60
|
+
|
61
|
+
# Sends an email message.
|
62
|
+
def send_email(email)
|
63
|
+
email.verify_sendable
|
64
|
+
msg = []
|
65
|
+
email.headers.each { |header| msg << "#{header}\n" }
|
66
|
+
msg << "\n"
|
67
|
+
msg << email.body
|
68
|
+
begin
|
69
|
+
@@smtpClass.start( @@smtpServer ) { |smtp|
|
70
|
+
smtp.sendmail(msg, email.from_address, [ email.to_address ])
|
71
|
+
}
|
72
|
+
@@messagesSent << email
|
73
|
+
rescue Net::ProtoFatalError, TimeoutError, Errno::ECONNREFUSED,
|
74
|
+
Errno::ECONNRESET
|
75
|
+
# whatever
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns an array of what messages have been sent.
|
80
|
+
def messages_sent; @@messagesSent; end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Message
|
84
|
+
HTML_CONTENT_TYPE = 0
|
85
|
+
MULTIPART_CONTENT_TYPE = 1
|
86
|
+
|
87
|
+
attr_accessor :body, :char_set, :content_type, :from_address, :from_name,
|
88
|
+
:subject, :to_address, :to_name
|
89
|
+
|
90
|
+
# Unless they are explicitly set using accessors, the following defaults
|
91
|
+
# apply:
|
92
|
+
# [to_name] nil
|
93
|
+
# [from_name] nil
|
94
|
+
# [content_type] nil
|
95
|
+
# [char_set] 'iso-8859-1'
|
96
|
+
def initialize( subject, to_address, from_address, body = nil )
|
97
|
+
@subject = subject
|
98
|
+
@to_address = to_address
|
99
|
+
@from_address = from_address
|
100
|
+
@char_set = 'iso-8859-1'
|
101
|
+
@body = body
|
102
|
+
end
|
103
|
+
|
104
|
+
# Sends the email.
|
105
|
+
def deliver; Mailer.get_mailer.send_email( self ); end
|
106
|
+
|
107
|
+
# Returns an array of strings describing the headers for this email
|
108
|
+
# message.
|
109
|
+
def headers
|
110
|
+
headers = []
|
111
|
+
headers << "Subject: #{@subject}"
|
112
|
+
toHeader = "To: "
|
113
|
+
if @to_name
|
114
|
+
toHeader += " #{@to_name} <#{@to_address}>"
|
115
|
+
else
|
116
|
+
toHeader += " #{@to_address}"
|
117
|
+
end
|
118
|
+
headers << toHeader
|
119
|
+
fromHeader = "From: "
|
120
|
+
if @from_name
|
121
|
+
fromHeader += "#{@from_name} <#{@from_address}>"
|
122
|
+
else
|
123
|
+
fromHeader += "#{@from_address}"
|
124
|
+
end
|
125
|
+
headers << fromHeader
|
126
|
+
if content_type == HTML_CONTENT_TYPE
|
127
|
+
headers << "Content-Type: text/html; charset=\"#{@char_set}\""
|
128
|
+
headers << "MIME-Version: 1.0"
|
129
|
+
elsif content_type == MULTIPART_CONTENT_TYPE
|
130
|
+
headers << "Content-Type: Multipart/Alternative; charset=\"#{@char_set}\""
|
131
|
+
headers << "MIME-Version: 1.0"
|
132
|
+
end
|
133
|
+
headers
|
134
|
+
end
|
135
|
+
|
136
|
+
# Emailer calls this before sending a message; subclasses can override this
|
137
|
+
# if they want to ensure that certain parts of the message are valid before
|
138
|
+
# sending.
|
139
|
+
def verify_sendable
|
140
|
+
[ :subject, :from_address, :to_address, :body ].each { |field|
|
141
|
+
if self.send( field ).nil?
|
142
|
+
raise( InvalidStateError,
|
143
|
+
"Can't send email with blank #{ field.id2name }" )
|
144
|
+
end
|
145
|
+
}
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# The MockMailer can be swapped out for the Mailer in test code, and then
|
150
|
+
# queried to make sure that the right emails were sent out. To set this, call
|
151
|
+
#
|
152
|
+
# mock_mailer = RhizMail::MockMailer.new
|
153
|
+
# RhizMail::set_mailer mock_mailer
|
154
|
+
#
|
155
|
+
# Afterwards, every time you call Message#deliver it will be delivered by the
|
156
|
+
# MockMailer.
|
157
|
+
class MockMailer
|
158
|
+
attr_reader :messages_sent
|
159
|
+
|
160
|
+
def initialize; flush; end
|
161
|
+
|
162
|
+
# Clear out the history of messages sent.
|
163
|
+
def flush; @messages_sent = []; end
|
164
|
+
|
165
|
+
# Pretends to send an email.
|
166
|
+
def send_email(email)
|
167
|
+
email.verify_sendable
|
168
|
+
@messages_sent << email
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# The SimpleTemplateMessage is designed to let a programmer define a simple
|
173
|
+
# set of tags for a certain sort of message and then substitute them in-code.
|
174
|
+
# It's intended to be simple enough that non-programmers can edit it without
|
175
|
+
# too much confusion.
|
176
|
+
#
|
177
|
+
# As an example, let's say you've got an email to send out whenever somebody
|
178
|
+
# signs up to your website. The template could look like this:
|
179
|
+
#
|
180
|
+
# $ cat ~/Desktop/template.txt
|
181
|
+
# Subject: Thanks for joining Website.com!
|
182
|
+
#
|
183
|
+
# Hi! Thanks for joining Website.com. For your reference, here's your signup
|
184
|
+
# information:
|
185
|
+
#
|
186
|
+
# Email: <% email %>
|
187
|
+
# Password: <% password %>
|
188
|
+
#
|
189
|
+
# Thanks!
|
190
|
+
#
|
191
|
+
# Note that the first line needs to be in the format "Subject: [ SUBJECT ]",
|
192
|
+
# and should be followed by a blank line.
|
193
|
+
#
|
194
|
+
# Then you create an instance of SimpleTemplateMessage and use
|
195
|
+
# SimpleTemplateMessage#substitute to change the :
|
196
|
+
#
|
197
|
+
#
|
198
|
+
|
199
|
+
pointing to that file
|
200
|
+
class SimpleTemplateMessage < Message
|
201
|
+
def initialize( to_address, from_address, template_file )
|
202
|
+
template = ''
|
203
|
+
MockFS.get_file.open( template_file ) { |file|
|
204
|
+
template = file.gets nil
|
205
|
+
}
|
206
|
+
template =~ /Subject: (.*)/
|
207
|
+
subject = $1
|
208
|
+
super( subject, to_address, from_address )
|
209
|
+
set_body( template )
|
210
|
+
end
|
211
|
+
|
212
|
+
def get_body( template )
|
213
|
+
body = ''
|
214
|
+
blank_line_found = false
|
215
|
+
template.each { |line|
|
216
|
+
if blank_line_found
|
217
|
+
body += line
|
218
|
+
else
|
219
|
+
blank_line_found = true if line =~ /^\n/
|
220
|
+
end
|
221
|
+
}
|
222
|
+
body
|
223
|
+
end
|
224
|
+
|
225
|
+
def get_regexp( token ); Regexp.new( "<%\s*#{ token }\s*%>", true ); end
|
226
|
+
|
227
|
+
def get_substitions; {}; end
|
228
|
+
|
229
|
+
def set_body( template )
|
230
|
+
@body = get_body( template )
|
231
|
+
tokens = @body.scan(/<%\s*(\S*)\s*%>/).collect { |matchArray|
|
232
|
+
matchArray[0]
|
233
|
+
}
|
234
|
+
substitutions = get_substitions
|
235
|
+
tokens.each { |token|
|
236
|
+
if ( proc = substitutions[token] ); substitute( token, proc ); end
|
237
|
+
}
|
238
|
+
end
|
239
|
+
|
240
|
+
def substitute(token, value_or_proc )
|
241
|
+
regexp = get_regexp( token )
|
242
|
+
@body.gsub!( regexp ){ |match|
|
243
|
+
value_or_proc.class <= Proc ? value_or_proc.call : value_or_proc
|
244
|
+
}
|
245
|
+
end
|
246
|
+
|
247
|
+
def verify_sendable
|
248
|
+
super
|
249
|
+
if @body =~ /<%/ || @body =~ /%>/
|
250
|
+
raise InvalidStateError, "substitution failed: #{ @body }", caller
|
251
|
+
elsif @subject =~/<%/ || @subject =~ /%>/
|
252
|
+
raise( InvalidStateError,
|
253
|
+
"substitution failed with subject: #{ @subject }",
|
254
|
+
caller )
|
255
|
+
elsif @body == ''
|
256
|
+
raise( InvalidStateError, "can't send email with blank body", caller )
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.6
|
3
|
+
specification_version: 1
|
4
|
+
name: rhizmail
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2005-03-19
|
8
|
+
summary: RhizMail is a test-friendly library for sending out customized emails.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: sera@fhwang.net
|
12
|
+
homepage: http://rhizmail.rubyforge.org/
|
13
|
+
rubyforge_project:
|
14
|
+
description: "RhizMail is a test-friendly library for sending out customized emails. This is
|
15
|
+
the library we use day-to-day at http://rhizome.org, where we send out more than
|
16
|
+
100 customized emails a day"
|
17
|
+
autorequire: rhizmail
|
18
|
+
default_executable:
|
19
|
+
bindir: bin
|
20
|
+
has_rdoc: false
|
21
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
22
|
+
requirements:
|
23
|
+
-
|
24
|
+
- ">"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.0
|
27
|
+
version:
|
28
|
+
platform: ruby
|
29
|
+
authors:
|
30
|
+
- Francis Hwang
|
31
|
+
files:
|
32
|
+
- lib/rhizmail.rb
|
33
|
+
- lib/rhizmail.rb~
|
34
|
+
test_files: []
|
35
|
+
rdoc_options: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
executables: []
|
38
|
+
extensions: []
|
39
|
+
requirements: []
|
40
|
+
dependencies:
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: lafcadio
|
43
|
+
version_requirement:
|
44
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
45
|
+
requirements:
|
46
|
+
-
|
47
|
+
- ">"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 0.0.0
|
50
|
+
version:
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: mockfs
|
53
|
+
version_requirement:
|
54
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
55
|
+
requirements:
|
56
|
+
-
|
57
|
+
- ">"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 0.0.0
|
60
|
+
version:
|