rhizmail 0.1.1 → 0.1.2

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 +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
  -