rhizmail 0.1.1 → 0.1.2

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 +89 -39
  2. data/lib/rhizmail.rb~ +134 -20
  3. metadata +12 -2
data/lib/rhizmail.rb CHANGED
@@ -32,21 +32,23 @@
32
32
  #
33
33
  # == Dependencies
34
34
  #
35
- # RhizMail depends on two external libraries:
35
+ # RhizMail depends on three external libraries:
36
36
  #
37
37
  # * ContextualService: http://contxtlservice.rubyforge.org
38
38
  # * MockFS: http://mockfs.rubyforge.org
39
+ # * Text::Format: http://rubyforge.org/projects/text-format
39
40
 
40
41
  require 'contxtlservice'
41
42
  require 'mockfs'
42
43
  require 'net/smtp'
44
+ require 'text/format'
43
45
 
44
46
  module RhizMail
45
- Version = '0.1.1'
47
+ Version = '0.1.2'
46
48
 
47
49
  # Returns a boolean value describing whether <tt>address</tt> is a plausible
48
50
  # email address format.
49
- def self.valid_address(address); (address =~ /\w@\w*\./) != nil; end
51
+ def self.valid_address?(address); (address =~ /\w@\w*\./) != nil; end
50
52
 
51
53
  # InvalidStateError is raised by SimpleTemplateMessage if you try to send it
52
54
  # out without doing all the necessary template substitutions.
@@ -114,7 +116,7 @@ module RhizMail
114
116
  # accessors, the following defaults apply:
115
117
  # [to_name] nil
116
118
  # [from_name] nil
117
- # [content_type] nil
119
+ # [content_type] nil (possible values for content_type are :html and :multipart)
118
120
  # [charset] 'iso-8859-1'
119
121
  def initialize( *args )
120
122
  @charset = 'iso-8859-1'
@@ -132,25 +134,23 @@ module RhizMail
132
134
  # Sends the email.
133
135
  def deliver; Mailer.get_mailer.send_email( self ); end
134
136
 
135
- # Returns an array of strings describing the headers for this email
136
- # message.
137
- def headers
138
- headers = []
139
- headers << "Subject: #{@subject}"
140
- toHeader = "To: "
141
- if @to_name
142
- toHeader += " #{@to_name} <#{@to_address}>"
143
- else
144
- toHeader += " #{@to_address}"
145
- end
146
- headers << toHeader
137
+ def from_header # :nodoc:
147
138
  fromHeader = "From: "
148
139
  if @from_name
149
140
  fromHeader += "#{@from_name} <#{@from_address}>"
150
141
  else
151
142
  fromHeader += "#{@from_address}"
152
143
  end
153
- headers << fromHeader
144
+ fromHeader
145
+ end
146
+
147
+ # Returns an array of strings describing the headers for this email
148
+ # message.
149
+ def headers
150
+ headers = []
151
+ headers << "Subject: #{@subject}"
152
+ headers << to_header
153
+ headers << from_header
154
154
  if content_type == :html
155
155
  headers << "Content-Type: text/html; charset=\"#{@charset}\""
156
156
  headers << "MIME-Version: 1.0"
@@ -160,6 +160,16 @@ module RhizMail
160
160
  end
161
161
  headers
162
162
  end
163
+
164
+ def to_header # :nodoc:
165
+ toHeader = "To: "
166
+ if @to_name
167
+ toHeader += " #{@to_name} <#{@to_address}>"
168
+ else
169
+ toHeader += " #{@to_address}"
170
+ end
171
+ toHeader
172
+ end
163
173
 
164
174
  # Mailer calls this before sending a message; subclasses can override this
165
175
  # if they want to ensure that certain parts of the message are valid before
@@ -224,8 +234,17 @@ module RhizMail
224
234
  #
225
235
  # Thanks!
226
236
  #
227
- # Note that the first line needs to be in the format "Subject: [ SUBJECT ]",
228
- # and should be followed by a blank line.
237
+ # The Subject and From information can be set explicitly in the template,
238
+ # with lines like:
239
+ #
240
+ # Subject: Thanks for joining Website.com!
241
+ # From: "Bill Smith" <bill.smith@email.com>
242
+ #
243
+ # These header lines need to be at the top of the template, and they should
244
+ # be followed by a blank line dividing them from the template body. Header
245
+ # lines are optional; you can leave them and set them explicitly using
246
+ # SimpleTemplateMessage#subject=, SimpleTemplateMessage#from_name=, and
247
+ # SimpleTemplateMessage#from_address= instead.
229
248
  #
230
249
  # Then you create an instance of SimpleTemplateMessage and use
231
250
  # SimpleTemplateMessage#substitute to change the contents:
@@ -285,7 +304,7 @@ module RhizMail
285
304
  #
286
305
  # If you call SimpleTemplateMessage#deliver without doing all the
287
306
  # substitutions required by the template, it will raise an InvalidStateError.
288
- # (Getting an exception is probably better than sending a customer an email
307
+ # (Getting an exception is probably better than sending somebody an email
289
308
  # with funny symbols in it.)
290
309
  class SimpleTemplateMessage < Message
291
310
  SubclassAttributes = Struct.new( :substitutions, :template, :from_address )
@@ -342,6 +361,11 @@ module RhizMail
342
361
  # Defaults for :template_file and :from_address can be set with
343
362
  # SimpleTemplateMessage.template and SimpleTemplateMessage.from_address.
344
363
  #
364
+ # By default, SimpleTemplateMessage#body wraps to 72 columns. You can
365
+ # change this behavior by passing :body_wrap in through the hash; pass
366
+ # +false+ to turn off wrapping, any other number to set the number of
367
+ # columns to wrap to.
368
+ #
345
369
  # An older, deprecated style of creation is to take specific parameters:
346
370
  #
347
371
  # msg = SimpleTemplateMessage.new(
@@ -350,21 +374,8 @@ module RhizMail
350
374
  # )
351
375
  def initialize( *args )
352
376
  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
377
+ h = args.first.clone
378
+ initialize_from_hash h
368
379
  super h
369
380
  else
370
381
  to_address, from_address, template_file = args
@@ -373,15 +384,29 @@ module RhizMail
373
384
  end
374
385
  generate_body @template
375
386
  end
387
+
388
+ def body
389
+ if @body_wrap or @body_wrap.nil?
390
+ format = Text::Format.new
391
+ format.columns = @body_wrap if @body_wrap
392
+ format.first_indent = 0
393
+ @body = format.paragraphs @body
394
+ end
395
+ @body
396
+ end
376
397
 
377
398
  def body_template( template ) # :nodoc:
378
399
  body = ''
379
400
  blank_line_found = false
401
+ headers_included = true
380
402
  template.each { |line|
381
- if blank_line_found
403
+ if blank_line_found or !headers_included
382
404
  body += line
383
- else
405
+ elsif line =~ /: /
384
406
  blank_line_found = true if line =~ /^\n/
407
+ else
408
+ headers_included = false
409
+ body += line
385
410
  end
386
411
  }
387
412
  body
@@ -403,14 +428,31 @@ module RhizMail
403
428
  substitute( token, proc )
404
429
  end
405
430
  }
406
- body
431
+ end
432
+
433
+ def initialize_from_hash( h ) # :nodoc:
434
+ verboten_keys = [ :body, :charset, :content_type, :subject ]
435
+ raise InvalidStateError unless ( h.keys & verboten_keys ).empty?
436
+ template_file = template_file_from_hash h
437
+ raise InvalidStateError if template_file.nil?
438
+ read_template template_file
439
+ if class_attributes.from_address and !h[:from_address]
440
+ h[:from_address] = class_attributes.from_address
441
+ end
442
+ unless h[:body_wrap].nil?
443
+ @body_wrap = h.delete :body_wrap
444
+ end
407
445
  end
408
446
 
409
447
  def read_template( template_file ) # :nodoc:
410
448
  @template = ''
411
449
  MockFS.file.open( template_file ) { |file| @template = file.gets nil }
412
450
  @template =~ /Subject: (.*)/
413
- @subject = $1
451
+ @subject = ( $1 or '' )
452
+ if @template =~ /^From: "(.*?)" \<(.*?)\>$/m
453
+ self.from_name = $1
454
+ self.from_address = $2
455
+ end
414
456
  end
415
457
 
416
458
  # Substitute a token with a value in the email body.
@@ -434,6 +476,14 @@ module RhizMail
434
476
  # to make when you create a new instance. +substitutions+ should always
435
477
  # return a hash of tokens to either values or Procs.
436
478
  def substitutions; {}; end
479
+
480
+ def template_file_from_hash( h ) # :nodoc:
481
+ if h[:template_file]
482
+ h.delete :template_file
483
+ else
484
+ template_file = class_attributes.template
485
+ end
486
+ end
437
487
 
438
488
  # Mailer will call this before sending this message; this will fail if any
439
489
  # tokens are left unsubstituted.
data/lib/rhizmail.rb~ CHANGED
@@ -40,13 +40,14 @@
40
40
  require 'contxtlservice'
41
41
  require 'mockfs'
42
42
  require 'net/smtp'
43
+ require 'text/format'
43
44
 
44
45
  module RhizMail
45
- Version = '0.1.0'
46
+ Version = '0.1.1'
46
47
 
47
48
  # Returns a boolean value describing whether <tt>address</tt> is a plausible
48
49
  # email address format.
49
- def self.valid_address(address); (address =~ /\w@\w*\./) != nil; end
50
+ def self.valid_address?(address); (address =~ /\w@\w*\./) != nil; end
50
51
 
51
52
  # InvalidStateError is raised by SimpleTemplateMessage if you try to send it
52
53
  # out without doing all the necessary template substitutions.
@@ -68,9 +69,8 @@ module RhizMail
68
69
  @@smtpClass = smtpClass;
69
70
  end
70
71
 
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.
72
+ # Sends an email message. +email+ needs to respond to +verify_sendable+;
73
+ # Mailer##send_email will call +verify_sendable+ before sending the email.
74
74
  def send_email(email)
75
75
  email.verify_sendable
76
76
  msg = []
@@ -96,11 +96,26 @@ module RhizMail
96
96
  attr_accessor :body, :charset, :content_type, :from_address, :from_name,
97
97
  :subject, :to_address, :to_name
98
98
 
99
- # Unless they are explicitly set using accessors, the following defaults
100
- # apply:
99
+ # Pass a hash to Message.new to create it:
100
+ #
101
+ # Message.new(
102
+ # :body => 'body', :subject => "subject here",
103
+ # :from_address => 'from@email.com', :to_address => 'to@email.com'
104
+ # )
105
+ #
106
+ # Any of the attributes listed above (under "Attributes") are valid for
107
+ # this hash.
108
+ #
109
+ # The older, deprecated style of instantiation is to take specific
110
+ # parameters:
111
+ #
112
+ # Message.new( subject, to_address, from_address, body )
113
+ #
114
+ # Unless they are explicitly set, during creation or afterwards using
115
+ # accessors, the following defaults apply:
101
116
  # [to_name] nil
102
117
  # [from_name] nil
103
- # [content_type] nil
118
+ # [content_type] nil (possible values for content_type are :html and :multipart)
104
119
  # [charset] 'iso-8859-1'
105
120
  def initialize( *args )
106
121
  @charset = 'iso-8859-1'
@@ -216,13 +231,17 @@ module RhizMail
216
231
  # Then you create an instance of SimpleTemplateMessage and use
217
232
  # SimpleTemplateMessage#substitute to change the contents:
218
233
  #
219
- # irb> msg = RhizMail::SimpleTemplateMessage.new(
220
- # 'john.doe@email.com', 'webmaster@website.com',
221
- # '/Users/francis/Desktop/template.txt'
234
+ # msg = RhizMail::SimpleTemplateMessage.new(
235
+ # :to_address => 'john.doe@email.com',
236
+ # :from_address => 'webmaster@website.com',
237
+ # :template_file => '/Users/francis/Desktop/template.txt'
222
238
  # )
223
- # irb> msg.substitute( 'email', 'john.doe@email.com' )
224
- # irb> msg.substitute( 'password', 'p4ssw0rd' )
225
- # irb> puts msg.body
239
+ # msg.substitute( 'email', 'john.doe@email.com' )
240
+ # msg.substitute( 'password', 'p4ssw0rd' )
241
+ # puts msg.body
242
+ #
243
+ # Which prints this:
244
+ #
226
245
  # Hi! Thanks for joining Website.com. For your reference, here's your signup
227
246
  # information:
228
247
  #
@@ -230,7 +249,40 @@ module RhizMail
230
249
  # Password: p4ssw0rd
231
250
  #
232
251
  # Thanks!
233
- # irb> msg.deliver
252
+ #
253
+ # This is the sort of email you're likely to send out a lot, so you can
254
+ # encapsulate a lot of specific information when you define a subclass:
255
+ #
256
+ # class IntroEmail < RhizMail::SimpleTemplateMessage
257
+ # from_address 'webmaster@website.com'
258
+ # template '/Users/francis/Desktop/template.txt'
259
+ # substitute 'email', Proc.new { |msg| msg.user.email }
260
+ # substitute 'password', Proc.new { |msg| msg.user.password }
261
+ #
262
+ # attr_reader :user
263
+ #
264
+ # def initialize( user )
265
+ # @user = user
266
+ # super( :to_address => user.email )
267
+ # end
268
+ # end
269
+ #
270
+ # User = Struct.new( :email, :password )
271
+ #
272
+ # u = User.new( 'john.doe@email.com', 'p4ssw0rd' )
273
+ # email = IntroEmail.new u
274
+ #
275
+ # This accomplishes the same thing.
276
+ #
277
+ # Note that a Proc passed to SimpleTemplateMessage.substitute needs to take
278
+ # the message itself as a parameter; this is because the Proc belongs to the
279
+ # class, and doesn't know which instance to use unless it's specified. Don't
280
+ # forget to use +attr_reader+ to give the Proc access to the message
281
+ # variables it needs.
282
+ #
283
+ # A child of SimpleTemplateMessage should call +super+ at the end of its
284
+ # +initialize+ method; this will automatically take care of the defined
285
+ # substitutions.
234
286
  #
235
287
  # If you call SimpleTemplateMessage#deliver without doing all the
236
288
  # substitutions required by the template, it will raise an InvalidStateError.
@@ -243,23 +295,68 @@ module RhizMail
243
295
  hash[key] = SubclassAttributes.new( {} )
244
296
  }
245
297
 
246
- def self.attributes; @@subclass_attributes[self]; end
298
+ def self.attributes # :nodoc:
299
+ @@subclass_attributes[self]
300
+ end
247
301
 
302
+ # Sets the +from_address+ of every message of this class; use this to
303
+ # parameterize children of SimpleTemplateMessage.
248
304
  def self.from_address( from_address )
249
305
  attributes.from_address = from_address
250
306
  end
251
307
 
308
+ # Sets the +template+ for every message of this class; use this to
309
+ # parameterize children of SimpleTemplateMessage.
252
310
  def self.template( template_file )
253
311
  attributes.template = template_file
254
312
  end
255
313
 
314
+ # Adds a substitution to use on every message of this class. Use this to
315
+ # parameterize children of SimpleTemplateMessage, like so:
316
+ #
317
+ # class IntroEmail < RhizMail::SimpleTemplateMessage
318
+ # substitute 'email', Proc.new { |msg| msg.user.email }
319
+ # substitute 'password', Proc.new { |msg| msg.user.password }
320
+ # ...
321
+ # end
322
+ #
323
+ # Note that a Proc passed to SimpleTemplateMessage.substitute needs to take
324
+ # the message itself as a parameter; this is because the Proc belongs to
325
+ # the class, and doesn't know which instance to use unless it's specified.
326
+ # Don't forget to use +attr_reader+ to give the Proc access to the message
327
+ # variables it needs.
256
328
  def self.substitute( token, proc )
257
329
  attributes.substitutions[token] = proc
258
330
  end
259
331
 
332
+ # Pass a hash to SimpleTemplateMessage.new to create it:
333
+ #
334
+ # msg = SimpleTemplateMessage.new(
335
+ # :to_address => 'john.doe@email.com',
336
+ # :from_address => 'webmaster@website.com',
337
+ # :template_file => '/Users/francis/Desktop/template.txt'
338
+ # )
339
+ #
340
+ # :template_file is a required key. Other valid keys are :from_address,
341
+ # :from_name, :to_address, :to_name.
342
+ #
343
+ # Defaults for :template_file and :from_address can be set with
344
+ # SimpleTemplateMessage.template and SimpleTemplateMessage.from_address.
345
+ #
346
+ # By default, SimpleTemplateMessage#body wraps to 72 columns. You can
347
+ # change this behavior by passing :body_wrap in through the hash; pass
348
+ # +false+ to turn off wrapping, any other number to set the number of
349
+ # columns to wrap to.
350
+ #
351
+ # An older, deprecated style of creation is to take specific parameters:
352
+ #
353
+ # msg = SimpleTemplateMessage.new(
354
+ # 'john.doe@email.com', 'webmaster@website.com',
355
+ # '/Users/francis/Desktop/template.txt'
356
+ # )
260
357
  def initialize( *args )
261
358
  if args.first.is_a? Hash
262
- h = args.first
359
+ h = args.first.clone
263
360
  verboten_keys = [ :body, :charset, :content_type, :subject ]
264
361
  raise InvalidStateError unless ( h.keys & verboten_keys ).empty?
265
362
  template_file = nil
@@ -274,6 +371,9 @@ module RhizMail
274
371
  if class_attributes.from_address and !h[:from_address]
275
372
  h[:from_address] = class_attributes.from_address
276
373
  end
374
+ unless h[:body_wrap].nil?
375
+ @body_wrap = h.delete :body_wrap
376
+ end
277
377
  super h
278
378
  else
279
379
  to_address, from_address, template_file = args
@@ -282,6 +382,16 @@ module RhizMail
282
382
  end
283
383
  generate_body @template
284
384
  end
385
+
386
+ def body
387
+ if @body_wrap or @body_wrap.nil?
388
+ format = Text::Format.new
389
+ format.columns = @body_wrap if @body_wrap
390
+ format.first_indent = 0
391
+ @body = format.paragraphs @body
392
+ end
393
+ @body
394
+ end
285
395
 
286
396
  def body_template( template ) # :nodoc:
287
397
  body = ''
@@ -296,7 +406,9 @@ module RhizMail
296
406
  body
297
407
  end
298
408
 
299
- def class_attributes; self.class.attributes; end
409
+ def class_attributes # :nodoc:
410
+ self.class.attributes
411
+ end
300
412
 
301
413
  def generate_body( template ) # :nodoc:
302
414
  @body = body_template( template )
@@ -310,16 +422,16 @@ module RhizMail
310
422
  substitute( token, proc )
311
423
  end
312
424
  }
313
- body
314
425
  end
315
426
 
316
- def read_template( template_file )
427
+ def read_template( template_file ) # :nodoc:
317
428
  @template = ''
318
429
  MockFS.file.open( template_file ) { |file| @template = file.gets nil }
319
430
  @template =~ /Subject: (.*)/
320
431
  @subject = $1
321
432
  end
322
433
 
434
+ # Substitute a token with a value in the email body.
323
435
  def substitute(token, value_or_proc )
324
436
  regexp = Regexp.new( "<%\s*#{ token }\s*%>", true )
325
437
  @body.gsub!( regexp ){ |match|
@@ -341,6 +453,8 @@ module RhizMail
341
453
  # return a hash of tokens to either values or Procs.
342
454
  def substitutions; {}; end
343
455
 
456
+ # Mailer will call this before sending this message; this will fail if any
457
+ # tokens are left unsubstituted.
344
458
  def verify_sendable
345
459
  super
346
460
  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.1
7
- date: 2005-08-14
6
+ version: 0.1.2
7
+ date: 2005-09-16
8
8
  summary: RhizMail is a test-friendly library for sending out customized emails.
9
9
  require_paths:
10
10
  - lib
@@ -51,6 +51,16 @@ dependencies:
51
51
  - !ruby/object:Gem::Dependency
52
52
  name: mockfs
53
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:
61
+ - !ruby/object:Gem::Dependency
62
+ name: text-format
63
+ version_requirement:
54
64
  version_requirements: !ruby/object:Gem::Version::Requirement
55
65
  requirements:
56
66
  -