Class: RhizMail::SimpleTemplateMessage
- Defined in:
- lib/rhizmail.rb
Overview
The SimpleTemplateMessage is designed to let a programmer define a simple set of tags for a certain sort of message and then substitute them in-code. It’s intended to be simple enough that non-programmers can edit it without too much confusion.
As an example, let’s say you’ve got an email to send out whenever somebody signs up to your website. The template could look like this:
$ cat /Users/francis/Desktop/template.txt
Subject: Thanks for joining Website.com!
Hi! Thanks for joining Website.com. For your reference, here's your signup
information:
Email: <% email %>
Password: <% password %>
Thanks!
The Subject and From information can be set explicitly in the template, with lines like:
Subject: Thanks for joining Website.com!
From: "Bill Smith" <[email protected]>
These header lines need to be at the top of the template, and they should be followed by a blank line dividing them from the template body. Header lines are optional; you can leave them and set them explicitly using SimpleTemplateMessage#subject=, SimpleTemplateMessage#from_name=, and SimpleTemplateMessage#from_address= instead.
Then you create an instance of SimpleTemplateMessage and use SimpleTemplateMessage#substitute to change the contents:
msg = RhizMail::SimpleTemplateMessage.new(
:to_address => '[email protected]',
:from_address => '[email protected]',
:template_file => '/Users/francis/Desktop/template.txt'
)
msg.substitute( 'email', '[email protected]' )
msg.substitute( 'password', 'p4ssw0rd' )
puts msg.body
Which prints this:
Hi! Thanks for joining Website.com. For your reference, here's your signup
information:
Email: [email protected]
Password: p4ssw0rd
Thanks!
This is the sort of email you’re likely to send out a lot, so you can encapsulate a lot of specific information when you define a subclass:
class IntroEmail < RhizMail::SimpleTemplateMessage
from_address '[email protected]'
template '/Users/francis/Desktop/template.txt'
substitute 'email', Proc.new { |msg| msg.user.email }
substitute 'password', Proc.new { |msg| msg.user.password }
attr_reader :user
def initialize( user )
@user = user
super( :to_address => user.email )
end
end
User = Struct.new( :email, :password )
u = User.new( '[email protected]', 'p4ssw0rd' )
email = IntroEmail.new u
This accomplishes the same thing.
Note that a Proc passed to SimpleTemplateMessage.substitute needs to take the message itself as a parameter; this is because the Proc belongs to the class, and doesn’t know which instance to use unless it’s specified. Don’t forget to use attr_reader
to give the Proc access to the message variables it needs.
A child of SimpleTemplateMessage should call super
at the end of its initialize
method; this will automatically take care of the defined substitutions.
If you call SimpleTemplateMessage#deliver without doing all the substitutions required by the template, it will raise an InvalidStateError. (Getting an exception is probably better than sending somebody an email with funny symbols in it.)
Defined Under Namespace
Classes: SubclassAttributes
Constant Summary collapse
- @@subclass_attributes =
Hash.new { |hash, key| hash[key] = SubclassAttributes.new( {} ) }
Instance Attribute Summary
Attributes inherited from Message
#charset, #content_type, #from_address, #from_name, #subject, #to_address, #to_name
Class Method Summary collapse
-
.attributes ⇒ Object
:nodoc:.
-
.from_address(from_address) ⇒ Object
Sets the
from_address
of every message of this class; use this to parameterize children of SimpleTemplateMessage. -
.substitute(token, proc) ⇒ Object
Adds a substitution to use on every message of this class.
-
.template(template_file) ⇒ Object
Sets the
template
for every message of this class; use this to parameterize children of SimpleTemplateMessage.
Instance Method Summary collapse
- #body ⇒ Object
-
#body_template(template) ⇒ Object
:nodoc:.
-
#class_attributes ⇒ Object
:nodoc:.
-
#generate_body(template) ⇒ Object
:nodoc:.
-
#initialize(*args) ⇒ SimpleTemplateMessage
constructor
Pass a hash to SimpleTemplateMessage.new to create it:.
-
#initialize_from_hash(h) ⇒ Object
:nodoc:.
-
#read_template(template_file) ⇒ Object
:nodoc:.
-
#substitute(token, value_or_proc) ⇒ Object
Substitute a token with a value in the email body.
-
#substitutions ⇒ Object
If you want to define a subclass of SimpleTemplateMessage, you can override
substitutions
to automatically define a set of substitutions to make when you create a new instance. -
#template_file_from_hash(h) ⇒ Object
:nodoc:.
-
#verify_sendable ⇒ Object
Mailer will call this before sending this message; this will fail if any tokens are left unsubstituted.
Methods inherited from Message
#deliver, #from_header, #headers, #to_header
Constructor Details
#initialize(*args) ⇒ SimpleTemplateMessage
Pass a hash to SimpleTemplateMessage.new to create it:
msg = SimpleTemplateMessage.new(
:to_address => '[email protected]',
:from_address => '[email protected]',
:template_file => '/Users/francis/Desktop/template.txt'
)
:template_file is a required key. Other valid keys are :from_address, :from_name, :to_address, :to_name.
Defaults for :template_file and :from_address can be set with SimpleTemplateMessage.template and SimpleTemplateMessage.from_address.
By default, SimpleTemplateMessage#body wraps to 72 columns. You can change this behavior by passing :body_wrap in through the hash; pass false
to turn off wrapping, any other number to set the number of columns to wrap to.
An older, deprecated style of creation is to take specific parameters:
msg = SimpleTemplateMessage.new(
'[email protected]', '[email protected]',
'/Users/francis/Desktop/template.txt'
)
421 422 423 424 425 426 427 428 429 430 431 432 |
# File 'lib/rhizmail.rb', line 421 def initialize( *args ) if args.first.is_a? Hash h = args.first.clone initialize_from_hash h super h else to_address, from_address, template_file = args read_template template_file super( @subject, to_address, from_address ) end generate_body @template end |
Class Method Details
.attributes ⇒ Object
:nodoc:
362 363 364 |
# File 'lib/rhizmail.rb', line 362 def self.attributes # :nodoc: @@subclass_attributes[self] end |
.from_address(from_address) ⇒ Object
Sets the from_address
of every message of this class; use this to parameterize children of SimpleTemplateMessage.
368 369 370 |
# File 'lib/rhizmail.rb', line 368 def self.from_address( from_address ) attributes.from_address = from_address end |
.substitute(token, proc) ⇒ Object
Adds a substitution to use on every message of this class. Use this to parameterize children of SimpleTemplateMessage, like so:
class IntroEmail < RhizMail::SimpleTemplateMessage
substitute 'email', Proc.new { |msg| msg.user.email }
substitute 'password', Proc.new { |msg| msg.user.password }
...
end
Note that a Proc passed to SimpleTemplateMessage.substitute needs to take the message itself as a parameter; this is because the Proc belongs to the class, and doesn’t know which instance to use unless it’s specified. Don’t forget to use attr_reader
to give the Proc access to the message variables it needs.
392 393 394 |
# File 'lib/rhizmail.rb', line 392 def self.substitute( token, proc ) attributes.substitutions[token] = proc end |
.template(template_file) ⇒ Object
Sets the template
for every message of this class; use this to parameterize children of SimpleTemplateMessage.
374 375 376 |
# File 'lib/rhizmail.rb', line 374 def self.template( template_file ) attributes.template = template_file end |
Instance Method Details
#body ⇒ Object
434 435 436 437 438 439 440 441 442 |
# File 'lib/rhizmail.rb', line 434 def body if @body_wrap or @body_wrap.nil? format = Text::Format.new format.columns = @body_wrap if @body_wrap format.first_indent = 0 @body = format.paragraphs @body end @body end |
#body_template(template) ⇒ Object
:nodoc:
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
# File 'lib/rhizmail.rb', line 444 def body_template( template ) # :nodoc: body = '' blank_line_found = false headers_included = true template.each { |line| if blank_line_found or !headers_included body += line elsif line =~ /: / blank_line_found = true if line =~ /^\n/ else headers_included = false body += line end } body end |
#class_attributes ⇒ Object
:nodoc:
461 462 463 |
# File 'lib/rhizmail.rb', line 461 def class_attributes # :nodoc: self.class.attributes end |
#generate_body(template) ⇒ Object
:nodoc:
465 466 467 468 469 470 471 472 473 474 475 476 477 |
# File 'lib/rhizmail.rb', line 465 def generate_body( template ) # :nodoc: @body = body_template( template ) tokens = @body.scan(/<%\s*(\S*)\s*%>/).collect { |matchArray| matchArray[0] } tokens.each { |token| if ( proc = substitutions[token] ) substitute( token, proc ) elsif ( proc = class_attributes.substitutions[token] ) substitute( token, proc ) end } end |
#initialize_from_hash(h) ⇒ Object
:nodoc:
479 480 481 482 483 484 485 486 487 488 489 490 491 |
# File 'lib/rhizmail.rb', line 479 def initialize_from_hash( h ) # :nodoc: verboten_keys = [ :body, :charset, :content_type, :subject ] raise InvalidStateError unless ( h.keys & verboten_keys ).empty? template_file = template_file_from_hash h raise InvalidStateError if template_file.nil? read_template template_file if class_attributes.from_address and !h[:from_address] h[:from_address] = class_attributes.from_address end unless h[:body_wrap].nil? @body_wrap = h.delete :body_wrap end end |
#read_template(template_file) ⇒ Object
:nodoc:
493 494 495 496 497 498 499 500 501 502 |
# File 'lib/rhizmail.rb', line 493 def read_template( template_file ) # :nodoc: @template = '' MockFS.file.open( template_file ) { |file| @template = file.gets nil } @template =~ /Subject: (.*)/ @subject = ( $1 or '' ) if @template =~ /^From: "(.*?)" \<(.*?)\>$/m self.from_name = $1 self.from_address = $2 end end |
#substitute(token, value_or_proc) ⇒ Object
Substitute a token with a value in the email body.
505 506 507 508 509 510 511 512 513 514 515 516 517 518 |
# File 'lib/rhizmail.rb', line 505 def substitute(token, value_or_proc ) regexp = Regexp.new( "<%\s*#{ token }\s*%>", true ) @body.gsub!( regexp ){ |match| if value_or_proc.is_a? Proc if value_or_proc.arity == 1 value_or_proc.call self else value_or_proc.call end else value_or_proc end } end |
#substitutions ⇒ Object
If you want to define a subclass of SimpleTemplateMessage, you can override substitutions
to automatically define a set of substitutions to make when you create a new instance. substitutions
should always return a hash of tokens to either values or Procs.
524 |
# File 'lib/rhizmail.rb', line 524 def substitutions; {}; end |
#template_file_from_hash(h) ⇒ Object
:nodoc:
526 527 528 529 530 531 532 |
# File 'lib/rhizmail.rb', line 526 def template_file_from_hash( h ) # :nodoc: if h[:template_file] h.delete :template_file else template_file = class_attributes.template end end |
#verify_sendable ⇒ Object
Mailer will call this before sending this message; this will fail if any tokens are left unsubstituted.
536 537 538 539 540 541 542 543 544 545 546 547 |
# File 'lib/rhizmail.rb', line 536 def verify_sendable super if @body =~ /<%/ || @body =~ /%>/ raise InvalidStateError, "substitution failed: #{ @body }", caller elsif @subject =~/<%/ || @subject =~ /%>/ raise( InvalidStateError, "substitution failed with subject: #{ @subject }", caller ) elsif @body == '' raise( InvalidStateError, "can't send email with blank body", caller ) end end |