rhizmail 0.1.0 → 0.1.1

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