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.
Files changed (3) hide show
  1. data/lib/rhizmail.rb +209 -46
  2. data/lib/rhizmail.rb~ +149 -52
  3. 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
- # * Lafcadio: http://lafcadio.rubyforge.org
37
+ # * ContextualService: http://contxtlservice.rubyforge.org
38
38
  # * MockFS: http://mockfs.rubyforge.org
39
39
 
40
- require 'lafcadio'
40
+ require 'contxtlservice'
41
41
  require 'mockfs'
42
42
  require 'net/smtp'
43
43
 
44
44
  module RhizMail
45
- Version = '0.1.0'
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 < Lafcadio::ContextualService
59
+ class Mailer < ContextualService::Service
60
60
  @@smtpServer = 'localhost'
61
61
  @@smtpClass = Net::SMTP
62
62
  @@messagesSent = []
63
63
 
64
- # Resets record of messages sent.
65
- def self.reset; @@messagesSent = []; end
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. Will call +email+ needs to respond to
72
- # +verify_sendable+; Mailer##send_email will call +verify_sendable+ before
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
- HTML_CONTENT_TYPE = 0
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
- # Unless they are explicitly set using accessors, the following defaults
103
- # apply:
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
- # [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
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 == HTML_CONTENT_TYPE
139
- headers << "Content-Type: text/html; charset=\"#{@char_set}\""
154
+ if content_type == :html
155
+ headers << "Content-Type: text/html; charset=\"#{@charset}\""
140
156
  headers << "MIME-Version: 1.0"
141
- elsif content_type == MULTIPART_CONTENT_TYPE
142
- headers << "Content-Type: Multipart/Alternative; charset=\"#{@char_set}\""
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
- # irb> msg = RhizMail::SimpleTemplateMessage.new(
211
- # 'john.doe@email.com', 'webmaster@website.com',
212
- # '/Users/francis/Desktop/template.txt'
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
- # irb> msg.substitute( 'email', 'john.doe@email.com' )
215
- # irb> msg.substitute( 'password', 'p4ssw0rd' )
216
- # irb> puts msg.body
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
- # irb> msg.deliver
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
- 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
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] ); substitute( token, proc ); end
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.class <= Proc ? value_or_proc.call : 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 Rhizome.org, where we send out more than
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, Bill1' )
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 'lafcadio'
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 < Lafcadio::ContextualService
59
+ class Mailer < ContextualService::Service
50
60
  @@smtpServer = 'localhost'
51
61
  @@smtpClass = Net::SMTP
52
62
  @@messagesSent = []
53
63
 
54
- # Resets record of messages sent.
55
- def self.reset; @@messagesSent = []; end
64
+ # Flushes record of messages sent.
65
+ def self.flush; @@messagesSent = []; end
56
66
 
57
- def self.set_smtp_class(smtpClass) # :nodoc:
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
- HTML_CONTENT_TYPE = 0
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
- # [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
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 == HTML_CONTENT_TYPE
127
- headers << "Content-Type: text/html; charset=\"#{@char_set}\""
140
+ if content_type == :html
141
+ headers << "Content-Type: text/html; charset=\"#{@charset}\""
128
142
  headers << "MIME-Version: 1.0"
129
- elsif content_type == MULTIPART_CONTENT_TYPE
130
- headers << "Content-Type: Multipart/Alternative; charset=\"#{@char_set}\""
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
- # Emailer calls this before sending a message; subclasses can override this
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 ~/Desktop/template.txt
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
- pointing to that file
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
- 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 )
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 get_body( template )
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 get_regexp( token ); Regexp.new( "<%\s*#{ token }\s*%>", true ); end
226
-
227
- def get_substitions; {}; end
299
+ def class_attributes; self.class.attributes; end
228
300
 
229
- def set_body( template )
230
- @body = get_body( template )
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] ); substitute( token, proc ); end
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 = get_regexp( token )
324
+ regexp = Regexp.new( "<%\s*#{ token }\s*%>", true )
242
325
  @body.gsub!( regexp ){ |match|
243
- value_or_proc.class <= Proc ? value_or_proc.call : 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.0
7
- date: 2005-03-19
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: lafcadio
42
+ name: contxtlservice
43
43
  version_requirement:
44
44
  version_requirements: !ruby/object:Gem::Version::Requirement
45
45
  requirements: