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