Commits

Michael Granger committed 646ed1f

Update documentation.

  • Participants
  • Parent commits 604c1a0

Comments (0)

Files changed (15)

File Examples.rdoc

+= Annotated Examples
+
+This is a list of template examples with annotations explaining what each group of lines is doing.
+
+== Simple Examples
+
+...
+
+
+== Advanced Examples
+
+Here's a somewhat more complex example. At our company Spime-Thorpe from above, say we're creating a system that will handle mass layoffs without the need for management to handle all those messy personal interactions. We'll need a mass-mailer for the employees that will be afforded the chance to explore their career opportunities, right? So we create a template called +overboard-mail.tmpl+:
+
+  <?config debugging_comments: true ?>
+  <?default grace_period to "7 days" ?>
+  
+  <?begin?>
+  <p>Dear <?call employee.fullname ?>,</p>
+  <?rescue DatabaseError ?>
+  <p>Dear Valued Ex-Employee,</p>
+  <?end begin?>
+  
+  <p>Congratulations! You have been selected by <?call failed_company.name ?>'s
+  elite management team as one of the many lucky individuals that will
+  enjoy the exciting challenge of pursuing other rewarding employment
+  opportunities!</p>
+  
+  <p><em>Kudos!</em></p>
+  
+  <p>You will find your accounts have been disabled, your desk has been helpfully 
+  cleared out for you, and all of your personal effects packaged up and
+  delivered to your address of record approximately 
+  <?timedelta tracking_info.delivery_date ?>:</p>
+  
+  <?for line in employee.address ?>
+  <?attr line ?><br />
+  <?end for ?>
+  
+  <p>Please visit your <a href="[?call config.overboard_url ?]/[?uriencode 
+  	failed_company.id ?]/[?uriencode employee.id ?]">customized Man OverBoard
+  	transition site</a> immediately:</p>
+  
+  <p>This will acknowledge that you have received this message, and automatically 
+  disable your email account.  Be sure and save this message!</p>
+  
+  <?if employee.severance_amount.nonzero? ?>
+  <p>Failure to acknowledge this message within <?attr grace_period ?> could result 
+  in delay of your final severance pay, in the amount of 
+  <?call "$%0.2f" % employee.severance_amount ?>.</p>
+  <?else?>
+  <p>Failure to acknowledge this message within <?attr grace_period ?> will 
+  result in automatic forfeiture of your numerous Man Overboard package benefits.</p>
+  <?end if?>
+  
+  <?comment Disabled at client request ?>
+  If you have any questions or concerns, please don't hesitate to contact your
+  friendly Spime-Thorpe <a href="mailto:salesteam2@spime-thorpe.com">representative</a>.
+  <?end comment ?>
+  
+  <p>Good Luck,<br />
+  Your friends at Spime-Thorpe, Inc!<br />
+  <a href="http://www.spime-thorpe.com/">http://www.spime-thorpe.com/</a></p>
+
+When wrapped with a layout template, here's what this renders as:
+
+  <?example { language: xml, caption: "The rendered output for one lucky individual" } ?>
+  <!DOCTYPE html>
+  <!--
+  
+  	Spime-Thorpe!
+  	$Id$
+  
+    -->
+  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+  	<title>Spime-Thorpe: Untitled</title>
+  
+  	<link rel="stylesheet" src="/css/base.css" type="text/css" />
+  	
+  	<script type="text/javascript" src="/js/jquery-latest.min.js"></script>
+  	
+  </head>
+  <body>
+  
+  	<header>
+  		<hgroup>
+  			<h1>Spime-Thorpe</h1>
+  			<h2></h2>
+  		</hgroup>
+  	</header>
+  
+  	<section id="content">
+  <p>Dear James Random,</p>
+  
+  <p>Congratulations! You have been selected by Widgets R Us's
+  elite management team as one of the many lucky individuals that will
+  enjoy the exciting challenge of pursuing other rewarding employment
+  opportunities!</p>
+  
+  <p><em>Kudos!</em></p>
+  
+  <p>You will find your accounts have been disabled, your desk has been helpfully 
+  cleared out for you, and all of your personal effects packaged up and
+  delivered to your address of record approximately 
+  3 days ago:</p>
+  
+  1213 NE. Winding Road<br />
+  Syracuse, NY  100213<br />
+  
+  <p>Please visit your <a href="http://failedcompany.spime-thorpe.com/overboard/a18661/1881">
+  customized Man OverBoard transition site</a> immediately:</p>
+  
+  <p>This will acknowledge that you have received this message, and automatically 
+  disable your email account.  Be sure and save this message!</p>
+  
+  <p>Failure to acknowledge this message within 11 days could result 
+  in delay of your final severance pay, in the amount of 
+  $12.81.</p>
+  
+  
+  <p>Good Luck,<br />
+  Your friends at Spime-Thorpe, Inc!<br />
+  <a href="http://www.spime-thorpe.com/">http://www.spime-thorpe.com/</a></p>
+  </section>
+  
+  	<footer>
+  				<section id="copyright">Copyright 2011, Spime-Thorpe</section>
+  	</footer>
+  
+  </body>
+  </html>
+
+This example can be found in the Inversion repository, in the +experiments+ directory.
+
 gem "tilt", "~>1.4", :group => [:development, :test]
 gem "sysexits", "~>1.0", :group => [:development, :test]
 gem "trollop", "~>1.16", :group => [:development, :test]
+gem "rdoc-generator-fivefish", "~>0", :group => [:development, :test]
 gem "hoe", "~>3.7", :group => [:development, :test]
 
 # vim: syntax=ruby

File GettingStarted.rdoc

+= Getting Started
+
+== Requirements
+
+- Ruby 1.9.2 or later
+
+== Installation
+
+$ gem install inversion
+
+== Basic Usage
+
+Inversion, like most other templating systems, works by giving you a way of defining the static
+parts of your output, and then letting you combine that at a later point with the dynamic parts:
+
+Create the template and use it to render an exciting message:
+
+	tmpl = Inversion::Template.new( "Hello, <?attr name ?>!" )
+	tmpl.name = "World"
+	puts tmpl.render
+
+The +<?attr name ?>+ tag defines the _name_ accessor on the template[rdoc-ref:Templates]
+object, the value of which is substituted for any occurrences of +name+ in the template:
+
+	Hello, World!
+
+This by itself isn't fantastically useful, but it does illustrate one of the ways in which Inversion
+is different: the program and the template share data through an API, instead of through a complex
+data structure, which establishes a clear delineation between what responsibility is the program's
+and which is the template's. The program doesn't have to know how the view uses the data it's given,
+and tests of the controller can substitute a Mock Object for the template to test the interaction
+between the two instead of having to match patterns in the eventual output like an integration test.
+
+You can also interact with the values set in the template:
+
+	Name: <?attr employee.full_name ?>
+
+This will call the #full_name method on whatever is set as the +employee+ attribute when 
+rendered, and the result will take the place of the tag.
+
+Inversion also comes with a collection of {other tags}[rdoc-ref:Tags] that
+provide flow control, exception-handling, etc.
+
+
+= Inversion User's Guide
+
+Inversion is a templating system for Ruby. It uses the Inversion of Control principle to decouple
+the contents and structure of the template from the code that uses it, making it easier to use,
+test-friendly, and clean.
+
+{Getting Started}[rdoc-ref:GettingStarted] goes over the requirements, installation, and basic usage of the library.
+
+The rdoc-ref:Templates topic has more details about how to load templates and control their behavior with options.
+
+When you're ready to start making your own templates, there's a quick-reference for the list of built-in rdoc-ref:Tags, too.
+
+Finally, there are a number of {Annotated Examples}[rdoc-ref:Examples] that illustrate how to solve common problems while templating.
+
+
+== Authors
+
+* Michael Granger
+* Mahlon E. Smith
+
+
+== License
+
+Copyright © 2011-2013, Michael Granger, Mahlon E. Smith
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this list of
+  conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, this list of
+  conditions and the following disclaimer in the documentation and/or other materials
+  provided with the distribution.
+* Neither the name of the authors nor contributors may be used to endorse or promote products
+  derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+

File Manifest.txt

 ChangeLog
+Examples.rdoc
+GettingStarted.rdoc
+Guide.rdoc
 History.rdoc
 Manifest.txt
 README.rdoc
 Rakefile
+Tags.rdoc
 bin/inversion
 lib/inversion.rb
 lib/inversion/exceptions.rb
 This will call the +#full_name+ method on whatever is set as the +employee+
 attribute when rendered, and the result will take the place of the tag.
 
-Inversion also comes with a collection of other tags that provide flow
-control, exception-handling, etc.
+Inversion also comes with {a collection of other tags}[rdoc-ref:Tags] that
+provide flow control, exception-handling, etc.
 
 Here's a slightly more complex example: Say we have a layout template that
 contains all the boilerplate, navigation, etc. for the site, and then an
-`<?attr body ?>` somewhere in the content area for the content specific to
+<code><?attr body ?></code> somewhere in the content area for the content specific to
 each view:
 
     layout = Inversion::Template.load( 'templates/layout.tmpl' )
     </section>
 
 Loading this template results in a Ruby object whose API contains one method:
-`#articles`. To render the view, we just call that accessor with instances of
-an `Article` domain class we defined elsewhere, and then drop the 'alist'
++#articles+. To render the view, we just call that accessor with instances of
+an +Article+ domain class we defined elsewhere, and then drop the +alist+
 template into the layout and render them:
 
     alist = Inversion::Template.load( 'templates/alist.tmpl' )
     layout.body = alist
     puts layout.render
 
-The `for` tag in the alist will iterate over the enumerable Articles and
-generate an `<li>` for each one. The resulting template object will be set as
+The +for+ tag in the alist will iterate over the enumerable Articles and
+generate an +<li>+ for each one. The resulting template object will be set as
 the body of the layout template, and stringified when the enclosing template
 is rendered. Templates can be nested this way as deeply as you like.
 
-For detailed tag documentation and examples, see the {online
-manual}[http://deveiate.org/code/Inversion-manual/] or browse {the API
-docs}[http://deveiate.org/code/inversion].
+For detailed tag documentation and examples, start with the Inversion::Template
+class in the API documentation.
 
 
 == References
 #!/usr/bin/env rake
 
+require 'rdoc/task'
+
 begin
 	require 'hoe'
 rescue LoadError
 	self.dependency 'tilt',          '~> 1.4',  :development
 	self.dependency 'sysexits',      '~> 1.0',  :development
 	self.dependency 'trollop',       '~> 1.16', :development
+	self.dependency 'rdoc-generator-fivefish', '~> 0', :development
 
 	self.require_ruby_version( '>=1.9.2' )
 	self.hg_sign_tags = true if self.respond_to?( :hg_sign_tags= )
 	Rake::Task[:spec].invoke
 end
 
+
+Rake::Task[ 'docs' ].clear
+RDoc::Task.new( 'docs' ) do |rdoc|
+	rdoc.main = "README.rdoc"
+	rdoc.rdoc_files.include( "*.rdoc", "ChangeLog", "lib/**/*.rb" )
+	rdoc.generator = :fivefish
+	rdoc.rdoc_dir = 'doc'
+end
+
+
+= Built-In Tags
+
+Inversion's tags support the Pluggability[http://rubygems.org/gems/pluggability] API, allowing for the easy addition of {custom tags}[@Custom+Tags], but it comes with a number of built-in ones too.
+
+
+== Tag Syntax
+
+Tags can be in either of two formats:
+
+- XML Pre-processing instruction style: <code><?tagname tagdata ?></code>
+- or the same thing, but with square brackets instead: <code>[?tagname tagdata ?]</code>
+
+The second form is especially useful if you're generating HTML and want to put an Inversion tag inside the attribute of an HTML tag, but still want the template to be well-formed:
+
+
+  <a href="[?call article.permalink ?]">Permalink</a>
+
+You can mix tag forms in a single document.
+
+
+== Tags
+
+=== comment
+
+The +comment+ tag can be used to temporarily stop a section of a document from being rendered:
+
+  <?comment?>
+  This stuff won't be rendered.
+  <?end comment ?>
+  But this stuff will.
+
+Note that the tags inside the comment will still be parsed, so the template's interface won't be affected by the comment:
+
+  <?comment?>
+  <?attr content ?>
+  <?end comment ?>
+
+If you have +debugging_comments+ enabled in the template options[rdoc-ref:Templates@Template+Options], sections which are commented out will be rendered as a comment describing what was omitted, surrounded by the comment characters set in +comment_start+ and +comment_end+ options, respectively.
+
+  <!-- Commented out 1 nodes on line 1 -->
+
+You can also embed a description of why the section is commented in the tag body, which will be used as a label when rendering the comment. For example:
+
+  <?comment Disabled until there's content ?>
+  <?attr content ?>
+  <?end comment ?>
+
+will be rendered as:
+
+  <!-- Commented out 1 nodes on line 1: Disabled until there's content -->
+
+
+=== config
+
+The +config+ tag can be used to override template options[Templates@Template+Options]
+on a per-template basis.  It allows for convenient, inline settings from
+within a template rather than from the code in which the template is
+loaded.
+
+For example, if you want to enable debugging comments on a single template:
+
+  <?config debugging_comments: true ?>
+
+Multiple template options can be set simultaneously by using a YAML hash:
+
+  <?config
+      on_render_error: propagate
+      debugging_comments: true
+      comment_start: /*
+      comment_end: */
+  ?>
+
+Note that this also allows you to set multiple options on a single line, if you wrap them in braces:
+
+  <?config { comment_start: "/*", comment_end: "*/" } ?>
+
+
+== Placeholder Tags
+
+Placeholder tags represent the main functionality of Inversion; they create a placeholder in the output text which can be filled in via a method on the template object with the same name.
+
+
+=== attr
+
+The +attr+ tag is the primary placeholder tag for injecting dynamic values into your templates.  The most basic form is analogous to +attr_accessor+; it defines a method on the template object that, when set, replaces all occurrences of the tag in the template with the value:
+
+  Title: <?attr title ?>
+
+Calling the template object's +#title=+ method will inject the stringified value into that part of the output when it's rendered, e.g.,
+
+  template.title = "How to Breed Kangaroos for Milk and Meat"
+  template.render
+  # => "Title: How to Breed Kangaroos for Milk and Meat"
+
+The rendered values of an +attr+ tag can also be the result of calling methods on the attr value:
+
+  ISBN: <?attr book.isbn ?>
+
+Attributes can be sprintf formatted using Ruby's String#% method:
+
+  Book price: <?attr "%0.2f" % book.price ?>
+
+Attributes can also contain other template objects, which allows templates to be nested within each other easily.
+
+  layout  = Inversion::Template.load( 'layout.tmpl' )
+  content = Inversion::Template.load( 'content.tmpl' )
+  content.caption = "Your kids will love their new Kangaroo family!"
+  layout.body = content
+  layout.render
+
+
+=== call
+
++call+ is just an alias for +attr+.  Use whichever strikes your fancy.
+
+
+=== escape
+
++escape+ works just like +attr+, but it escapes the content inserted into the template, using the configured escaping behavior.  The supported escaping behaviors are defined in a mixin called Inversion::Escaping. The behavior to use can be set using the {:escape_format}[Templates@Template+Options] option on the template or in a +config+ tag; it defaults to HTML escaping.
+
+  <p>Company name: <?escape company.name ?></p>
+
+If the company was +"AT&T"+, the output would look like:
+
+  <p>Company name: AT&amp;T</p>
+
+
+=== uriencode
+
+The +urlencode+ tag is another +attr+-like tag, but this one does URI encoding:
+
+  <nav>Edit <a href="/profile?name=[?uriencode person.name ?]">your profile</a></nav>
+
+
+== Special Placeholders
+
+=== timedelta
+
+If you need to automatically generate a human-readable description of the interval between two times, you can use the +timedelta+ tag:
+
+  <article class="blogentry">
+    <header>
+      <p>Posted: <?timedelta entry.date_posted ?>.</p>
+    </header>
+    ...
+  </article>
+
+The tag supports any object which responds to the +#to_time+ method, so standard +Time+, +Date+, and +DateTime+ objects all work.
+
+Dates are compared against the current time, and render to approximate descriptions of the interval, e.g.,
+
+* 4 days ago
+* about an hour from now
+* 6 weeks ago
+* less than a minute from now
+
+
+== Import and Publish/Subscribe
+
+Both of these tags operate on nested templates: one copies objects from an outer template into an inner one (+import+), and the other publishes sections of content from one template to any other subscribed template.
+
+
+=== import
+
+Occasionally, you'll want to compose output from several different templates by nesting them, but you don't want to have to set common objects on all of them from code. The +import+ tag lets you copy the values from a container template into one intended to be nested within it:
+
+  <!-- layout.tmpl -->
+  Logged in as: <?attr request.authenticated_user ?>
+  <?attr body ?>
+  
+  <!-- body.tmpl -->
+  <?import request ?>
+  <p>You can check your balance using <a href="[?call request.path_info ?]/accounts">the
+     accounts tool</a>.</p>
+
+When the content template is nested in the container, you only need to set the +request+ attribute on the container to set it in both places:
+
+  layout = Inversion::Template.load( 'layout.tmpl' )
+  body = Inversion::Template.load( 'body.tmpl' )
+  
+  layout.body = body
+  layout.request = request
+  
+  puts layout.render
+
+Without the use of +import+, you'd need to similarly set the request attribute on the body template.
+
+The imported attribute's value is determined at render time, so you can also use it to import values from an iteration. 
+
+  <!-- Container template (table.tmpl)" -->
+  <table>
+    <thead>...</thead>
+    <tbody>
+      <?for user in assigned_users ?>
+      <?attr row ?>
+      <?end for ?>
+    </tbody>
+  </table>
+  <?end?>
+  
+  <!-- Content template (row.tmpl)" -->
+  <?import user ?>
+      <tr>
+        <th>Username:</th><td><?escape user.username ?></td>
+        <th>UID:</th><td><?escape user.uid ?></td>
+        <th>GID:</th><td><?escape user.gid ?></td>
+      </tr>
+  <?end?>
+
+and the code:
+
+  usertable = Inversion::Template.load( 'table.tmpl' )
+  userrow = Inversion::Template.load( 'row.tmpl' )
+  
+  usertable.row = userrow
+  usertable.assigned_users = User.assigned.all
+  
+  puts usertable.render
+
+When the +row.tmpl+ is rendered each time, its imported +user+ is set to whatever the +user+ in the container is, in this case the next object in +assigned_users+.
+
+You can import values into deeply-nested templates, provided each container imports it as well.
+
+
+=== publish/subscribe
+
+Often you'll want to set up a generic layout template to establish a global look-and-feel, and then modify it based on the content of an inner template. 
+
+==== Look and feel template (layout.tmpl)
+  <!DOCTYPE HTML>
+  <html lang="en">
+  <head>
+    <title><?subscribe title || Untitled ?></title>
+    <link rel="stylesheet" href="/css/base.css" type="text/css" media="screen" 
+      title="Base Stylesheet" charset="utf-8" />
+      <?subscribe stylesheets ?>
+  
+    <script defer="defer" src="/js/jquery-1.4.2.min.js" 
+        type="text/javascript" charset="utf-8"></script>
+      <?subscribe scripts ?>
+  </head>
+  
+  <body><?attr body ?></body>
+  </html>
+
+
+==== A content template (content.tmpl)
+  <?publish title ?>I make stuff up<?end publish?>
+  
+  <?publish stylesheets ?>
+    <link rel="stylesheet" href="/css/content.css" type="text/css" media="screen" 
+      title="Content Style Overrides" charset="utf-8" />
+  <?end publish?>
+  
+  <?publish scripts ?>
+    <script defer="defer" src="/js/content.js" type="text/javascript" charset="utf-8"></script>
+  <?end publish?>
+  
+  <div>Hi, there.</div>
+
+==== Template setup
+
+  layout  = Inversion::Template.load( 'layout.tmpl' )
+  content = Inversion::Template.load( 'content.tmpl' )
+  
+  layout.body = content
+  
+  puts layout.render
+
++subscribe+ renders to an empty string if there is no matching +publish+, or to the value of a default if supplied (as in the HTML title example above.)
+In this fashion, you can dynamically switch out different content pages, with each having the ability to optionally override various HTML elements. 
+
+=== include
+
+The +include+ tag allows inclusion of other template files from within a template.  This supports separation of a template into several reusable components.  The included template becomes a part of the including template, along with any defaults, attributes and configuration.
+
+==== Include setup
+
+  email = Inversion::Template.load( 'email.tmpl' )
+  
+  email.greeting = "Kudos"
+  email.company  = Company[ :spime_thorpe ]
+  email.user     = User[ :jrandom ]
+  
+  puts main.render
+
+==== Including template (email.tmpl)
+
+  Subject: Great news, everybody!
+  From: <?attr company.email ?>
+  To: <?attr user.email ?>
+  
+  <?attr greeting ?>, <?attr user.first_name ?>!
+  
+  We are excited to inform you that you have been selected to participate
+  in a challenging and exciting career displacement opportunity!
+  
+  Please attend the mandatory Man Overboard (tm) session we have scheduled
+  for you at 8:45AM on Thursday in the Sunshine Room. Light refreshments 
+  and computer-aided aptitude testing will be provided.
+  
+  <?include signature.tmpl ?>
+
+==== Included template (signature.tmpl)
+
+  Sincerely, 
+  Your Friends at <?attr company.name ?>!
+
+==== The rendered output
+
+  Subject: Great news, everybody!
+  From: "Spime-Thorpe, Inc." <salesteam2@spime-thorpe.com>
+  To: "James Random" <jrandom@compusa.com>
+  
+  Kudos, James!
+  
+  We are excited to inform you that you have been selected to participate
+  in a challenging and exciting career displacement opportunity!
+  
+  Please attend the mandatory Man Overboard (tm) session we have scheduled
+  for you at 8:45AM on Thursday in the Sunshine Room. Light refreshments 
+  and computer-aided aptitude testing will be provided.
+  
+  Sincerely, 
+  Your Friends at Spime Thorpe!
+
+
+== Flow Control
+
+The following tags are used to alter the flow of rendering from within templates.
+
+
+=== for
+
+The +for+ tag iterates over the objects in a collection, rendering its
+template section once for each iteration. Its attribute can be set to anything
+that responds to @#each@. The iteration variable(s) are scoped to the block,
+and temporarily override any template attributes of the same name.
+
+==== 'For' tag setup
+
+  overhead_list = Inversion::Template.load( 'employee_list.tmpl' )
+  overhead_list.users = User.
+    filter { start_date < 6.months.ago }.
+    filter { department = 'Information Technology' }
+  
+  puts overhead_list.render
+
+The +for+ tag's iteration works just like Ruby's +for+; if the enumerated
+value has more than one value, you can give a list of iteration variables to
+be assigned to.
+
+==== Employee list using 'for'
+
+  <table>
+    <thead>...</thead>
+    <tbody>
+      <?for user, i in users.each_with_index ?>
+          <tr class="[?if i.even? ?]even[?else?]odd[?end if?]-row">
+              <td><?attr user.first_name ?></td>
+              <td><?attr user.last_name ?></td>
+              <td><?attr user.title ?></td>
+              <td><?attr user.start_date ?></td>
+              <td><?attr user.salary ?></td>
+          </tr>
+      <?end for ?>
+    </tbody>
+  </table>
+
+The example above uses a Ruby enumerator for the +#each_with_index+ method to set the class of the row to +'even-row'+ or +'odd-row'+.
+
+This works with the keys and values of Hashes, too:
+
+==== Display hash of notes keyed by author using 'for'
+
+  <?for user, content in user.notes ?>
+  <section class="note">
+    <header>
+      Note by <?call user.username ?>
+    </header>
+    <p><?escape content ?></p>
+  </section>
+  
+  <?end for ?>
+
+Note that you can also use Ruby's "external iterator" syntax to iterate, too:
+
+==== Iterate over each byte of a string with an index using 'for'
+
+  <section class="hexdump">
+  <?for byte, index in frame.header.each_byte.with_index ?>
+    <?if index.modulo(8).zero? ?>
+      <?if index.nonzero? ?>
+    </span><br />
+      <?end if ?>
+    <span class="row"><?attr "0x%08x" % index ?>:
+    <?end if ?>
+    &nbsp;<code><?attr "0x%02x" % byte ?></code>
+  <?end for ?>
+  </section>
+
+
+
+=== if/elsif/else
+
+The +if+ tag can be used to conditionally render a section of the template based on the value of an attribute or the value of a method called on it.
+
+==== Conditional block
+
+  <?if user.has_stock_options? ?>
+  You will have 21 days to exercise your stock options.
+  <?else ?>
+  You have a week to optionally take home a handful of supplies from the
+  office cabinet.
+  <?end if ?>
+
+
+=== unless
+
+Unless is like the +if+ tag, but with inverted logic. Note that an +unless+ can have an +else+ tag, but cannot have any +elsif+ tags within it.
+
+
+=== yield
+
+The +yield+ tag is used to defer rendering of some part of the template to the code that is calling render[rdoc-ref:Inversion::Template#render] on it. If a block is passed to +#render+, then the +yield+ tag will call it with the Inversion::RenderState object that is currently in effect, and will render the return value in its place.
+
+==== Using 'yield' to defer an expensive database lookup (report.tmpl)
+
+  <?if extra_details_enabled ?>
+  <?yield ?>
+  <?end if ?>
+
+  report = Inversion::Template.load( 'report.tmpl' )
+  report.extra_details_enabled = true if $DEBUG
+  puts report.render do
+    report_table = Inversion::Template.load( 'table.tmpl' )
+    report_table.rows = an_expensive_database_query()
+    report_table
+  end
+
+This will insert the +report_table+ template in place of the yield, but only if $DEBUG is true.
+
+
+== Troubleshooting/Introspection
+
+=== pp
+
+The +pp+ tag uses the +PP+ library to output an escaped representation of its argument.
+
+==== Creating an object to inspect
+
+  content = Inversion::Template.load( 'content.tmpl' )
+  content.file = File.stat( '/tmp/example.txt' )
+  
+  puts content.render
+
+==== Inspecting an object from within a template (content.tmpl)
+
+  <div class="debugging">
+      The file's stat attributes:
+      <?pp file ?>
+  </div>
+
+The output is escaped according to the current setting of the {:escape_format}[rdoc-ref:Templates@Template+Options] option.
+
+==== The rendered result
+
+  <div class="debugging">
+      The file's stat attributes:
+          #&lt;File::Stat
+   dev=0xe000004,
+   ino=3064556,
+   mode=0100644 (file rw-r--r--),
+   nlink=1,
+   uid=501 (mahlon),
+   gid=0 (wheel),
+   rdev=0x0 (0, 0),
+   size=0,
+   blksize=4096,
+   blocks=0,
+   atime=2011-08-12 08:43:15 -0700 (1313163795),
+   mtime=2011-08-12 08:43:15 -0700 (1313163795),
+   ctime=2011-08-12 08:43:15 -0700 (1313163795)&gt;</div>
+  </div>
+

File lib/inversion/template.rb

 end
 
 
-# The main template class. Instances of this class are created by parsing template
-# source and combining the resulting node tree with a set of attributes that
-# can be used to populate it when rendered.
+# The main template class.
+#
+# Inversion templates are the primary objects you'll be interacting with. Templates
+# can be created from a string:
+#
+#   Inversion::Template.new( template_source )
+#
+# or from a file:
+#
+#   Inversion::Template.load( 'path/to/template.tmpl' )
+#
+#
+# == Template Options
+#
+# Inversion supports the {Configurability}[http://rubygems.org/gems/configurability]
+# API, and registers itself with the +templates+ key. This means you can either add
+# a +templates+ section to your Configurability config, or call
+# ::configure yourself with a config Hash (or something that quacks like one).
+#
+# To set options on a per-template basis, you can pass an options hash to either
+# Inversion::Template::load or Inversion::Template::new, or set them from within the template
+# itself using the {config tag}[rdoc-ref:Tags@config].
+#
+# The available options are:
+#
+# [:ignore_unknown_tags]
+#   Setting to false causes unknown tags used in templates to raise an
+#   Inversion::ParseError. Defaults to +true+.
+#
+# [:on_render_error]
+#   Dictates the behavior of exceptions during rendering. Defaults to +:comment+.
+#
+#   [:ignore]
+#     Exceptions are silently ignored.
+#   [:comment]
+#     Exceptions are rendered inline as comments.
+#   [:propagate]
+#     Exceptions bubble up to the caller of Inversion::Template#render.
+#
+#
+# [:debugging_comments]
+#   Insert various Inversion parse and render statements while rendering. Defaults to +false+.
+#
+# [:comment_start]
+#   When rendering debugging comments, the comment is started with these characters.
+#   Defaults to <code>"<!--"</code>.
+#
+# [:comment_end]
+#   When rendering debugging comments, the comment is finished with these characters.
+#   Defaults to <code>"-->"</code>.
+#
+# [:template_paths]
+#   An array of filesystem paths to search for templates within, when loaded or
+#   included with a relative path.  The current working directory is always the
+#   last checked member of this. Defaults to <code>[]</code>.
+#
+# [:escape_format]
+#   The escaping used by tags such as +escape+ and +pp+. Default: +:html+.
+#
+# [:strip_tag_lines]
+#   If a tag's presence introduces a blank line into the output, this option
+#   removes it. Defaults to +true+.
+#
+# [:stat_delay]
+#   Templates know when they've been altered on disk, and can dynamically
+#   reload themselves in long running applications.  Setting this option creates
+#   a purposeful delay between reloads for busy servers. Defaults to +0+
+#   (disabled).
+#
+#
 class Inversion::Template
 	extend Loggability
 	include Inversion::DataUtilities
 	}.freeze
 
 
-	### Global config
-	@config = DEFAULT_CONFIG.dup
+	##
+	# Global config
 	class << self; attr_accessor :config; end
+	self.config = DEFAULT_CONFIG.dup
 
+	##
 	# Global template search path
-	@template_paths = []
 	class << self; attr_accessor :template_paths; end
+	self.template_paths = []
 
 
 	### Configure the templating system.

File lib/inversion/template/begintag.rb

 #   <?begin ?><?call employees.length ?><?end?>
 #
 #   <?begin ?>
-#		<?for employee in employees.all ?>
-#			<?attr employee.name ?> --> <?attr employee.title ?>
-#		<?end for?>
+#       <?for employee in employees.all ?>
+#           <?attr employee.name ?> --> <?attr employee.title ?>
+#       <?end for?>
 #   <?rescue DatabaseError => err ?>
-#		Oh no!! I can't talk to the database for some reason.  The
-#		error was as follows:
-#		<pre>
-#			<?attr err.message ?>
-#		</pre>
+#       Oh no!! I can't talk to the database for some reason.  The
+#       error was as follows:
+#       <pre>
+#           <?attr err.message ?>
+#       </pre>
 #   <?end?>
 #
 class Inversion::Template::BeginTag < Inversion::Template::Tag

File manual/src/examples.page

----
-# vim: set et nosta sw=4 ts=4 ft=textile :
-title: Annotated Examples
-layout: default
-index: 3
-filters:
-  - erb
-  - links
-  - examples
-  - editorial
-  - api
-  - textile
----
-
-h2. <%= page.config['title'] %>
-
-<div id="auto-toc"></div>
-
-This is a list of template examples with annotations explaining what each group of lines is doing.
-
-h3(#simple-examples). Simple Examples
-
-...
-
-
-h3(#advanced-examples). Advanced Examples
-
-Here's a somewhat more complex example. At our company Spime-Thorpe from above, say we're creating a system that will handle mass layoffs without the need for management to handle all those messy personal interactions. We'll need a mass-mailer for the employees that will be afforded the chance to explore their career opportunities, right? So we create a template called @overboard-mail.tmpl@:
-
-<?example { language: xml, caption: "overboard-mail.tmpl" } ?>
-<?config debugging_comments: true ?>
-<?default grace_period to "7 days" ?>
-
-<?begin?>
-<p>Dear <?call employee.fullname ?>,</p>
-<?rescue DatabaseError ?>
-<p>Dear Valued Ex-Employee,</p>
-<?end begin?>
-
-<p>Congratulations! You have been selected by <?call failed_company.name ?>'s
-elite management team as one of the many lucky individuals that will
-enjoy the exciting challenge of pursuing other rewarding employment
-opportunities!</p>
-
-<p><em>Kudos!</em></p>
-
-<p>You will find your accounts have been disabled, your desk has been helpfully 
-cleared out for you, and all of your personal effects packaged up and
-delivered to your address of record approximately 
-<?timedelta tracking_info.delivery_date ?>:</p>
-
-<?for line in employee.address ?>
-<?attr line ?><br />
-<?end for ?>
-
-<p>Please visit your <a href="[?call config.overboard_url ?]/[?uriencode 
-	failed_company.id ?]/[?uriencode employee.id ?]">customized Man OverBoard
-	transition site</a> immediately:</p>
-
-<p>This will acknowledge that you have received this message, and automatically 
-disable your email account.  Be sure and save this message!</p>
-
-<?if employee.severance_amount.nonzero? ?>
-<p>Failure to acknowledge this message within <?attr grace_period ?> could result 
-in delay of your final severance pay, in the amount of 
-<?call "$%0.2f" % employee.severance_amount ?>.</p>
-<?else?>
-<p>Failure to acknowledge this message within <?attr grace_period ?> will 
-result in automatic forfeiture of your numerous Man Overboard package benefits.</p>
-<?end if?>
-
-<?comment Disabled at client request ?>
-If you have any questions or concerns, please don't hesitate to contact your
-friendly Spime-Thorpe <a href="mailto:salesteam2@spime-thorpe.com">representative</a>.
-<?end comment ?>
-
-<p>Good Luck,<br />
-Your friends at Spime-Thorpe, Inc!<br />
-<a href="http://www.spime-thorpe.com/">http://www.spime-thorpe.com/</a></p>
-
-<?end ?>
-
-When wrapped with a layout template, here's what this renders as:
-
-<?example { language: xml, caption: "The rendered output for one lucky individual" } ?>
-<!DOCTYPE html>
-<!--
-
-	Spime-Thorpe!
-	$Id$
-
-  -->
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
-<head>
-	<title>Spime-Thorpe: Untitled</title>
-
-	<link rel="stylesheet" src="/css/base.css" type="text/css" />
-	
-	<script type="text/javascript" src="/js/jquery-latest.min.js"></script>
-	
-</head>
-<body>
-
-	<header>
-		<hgroup>
-			<h1>Spime-Thorpe</h1>
-			<h2></h2>
-		</hgroup>
-	</header>
-
-	<section id="content">
-<p>Dear James Random,</p>
-
-<p>Congratulations! You have been selected by Widgets R Us's
-elite management team as one of the many lucky individuals that will
-enjoy the exciting challenge of pursuing other rewarding employment
-opportunities!</p>
-
-<p><em>Kudos!</em></p>
-
-<p>You will find your accounts have been disabled, your desk has been helpfully 
-cleared out for you, and all of your personal effects packaged up and
-delivered to your address of record approximately 
-3 days ago:</p>
-
-1213 NE. Winding Road<br />
-Syracuse, NY  100213<br />
-
-<p>Please visit your <a href="http://failedcompany.spime-thorpe.com/overboard/a18661/1881">
-customized Man OverBoard transition site</a> immediately:</p>
-
-<p>This will acknowledge that you have received this message, and automatically 
-disable your email account.  Be sure and save this message!</p>
-
-<p>Failure to acknowledge this message within 11 days could result 
-in delay of your final severance pay, in the amount of 
-$12.81.</p>
-
-
-<p>Good Luck,<br />
-Your friends at Spime-Thorpe, Inc!<br />
-<a href="http://www.spime-thorpe.com/">http://www.spime-thorpe.com/</a></p>
-</section>
-
-	<footer>
-				<section id="copyright">Copyright 2011, Spime-Thorpe</section>
-	</footer>
-
-</body>
-</html>
-<?end ?>
-
-This example can be found in the Inversion repository, in the @experiments@ directory.
-

File manual/src/gettingstarted.page

----
-# vim: set et nosta sw=4 ts=4 ft=textile :
-title: Getting Started
-layout: default
-index: 1
-filters:
-  - erb
-  - links
-  - examples
-  - textile
----
-
-h2. <%= page.config['title'] %>
-
-<div id="auto-toc"></div>
-
-h3(#requirements). Requirements
-
-- Ruby 1.9.2 or later
-
-h3(#installation). Installation
-
-$ gem install inversion
-
-h3(#usage). Basic Usage
-
-Inversion, like most other templating systems, works by giving you a way of defining the static
-parts of your output, and then letting you combine that at a later point with the dynamic parts:
-
-Create the template and use it to render an exciting message:
-
-<?example { syntax: ruby, caption: "Creating a template" } ?>
-tmpl = Inversion::Template.new( "Hello, <?attr name ?>!" )
-tmpl.name = "World"
-puts tmpl.render
-<?end?>
-
-The <tt><?attr name ?></tt> tag defines the _name_ accessor on the <?link "template":Templates ?>
-object, the value of which is substituted for any occurrences of @name@ in the template:
-
-<?example { syntax: text } ?>
-Hello, World!
-<?end ?>
-
-This by itself isn't fantastically useful, but it does illustrate one of the ways in which Inversion
-is different: the program and the template share data through an API, instead of through a complex
-data structure, which establishes a clear delineation between what responsibility is the program's
-and which is the template's. The program doesn't have to know how the view uses the data it's given,
-and tests of the controller can substitute a Mock Object for the template to test the interaction
-between the two instead of having to match patterns in the eventual output like an integration test.
-
-You can also interact with the values set in the template:
-
-<?example { syntax: text } ?>
-Name: <?attr employee.full_name ?>
-<?end ?>
-
-This will call the @#full_name@ method on whatever is set as the @employee@ attribute when 
-rendered, and the result will take the place of the tag.
-
-Inversion also comes with a collection of <?link "other tags":Tags ?> that
-provide flow control, exception-handling, etc.
-
-

File manual/src/index.page

----
-# vim: set et nosta sw=4 ts=4 ft=textile :
-title: Inversion User's Guide
-layout: default
-index: 1
-filters:
-  - erb
-  - links
-  - examples
-  - textile
----
-
-h2. <%= page.config['title'] %>
-
-<div id="auto-toc"></div>
-
-Inversion is a templating system for Ruby. It uses the Inversion of Control principle to decouple
-the contents and structure of the template from the code that uses it, making it easier to use,
-test-friendly, and clean.
-
-# <?link Getting Started ?>
-## <?link "Requirements":Getting Started #requirements ?>
-## <?link "Installation":Getting Started #installation ?>
-## <?link "Basic Usage":Getting Started #usage ?>
-# <?link Templates ?>
-## <?link "Options":Templates#options ?>
-# <?link Tags ?>
-## <?link "Tag Syntax":Tags#tagsyntax ?>
-## <?link "Comment":Tags#comment ?>
-## <?link "Config":Tags#config ?>
-## <?link "Placeholders":Tags#placeholders ?>
-### <?link "Attr":Tags#attr ?>
-### <?link "Call":Tags#call ?>
-### <?link "Escape":Tags#escape ?>
-### <?link "Urlencode":Tags#urlencode ?>
-## <?link "Special Placeholders":Tags#special-placeholders ?>
-### <?link "TimeDelta":Tags#timedelta ?>
-## <?link "Import/Export":Tags#import-export ?>
-### <?link "Import":Tags#import ?>
-### <?link "Export":Tags#export ?>
-### <?link "Include":Tags#include ?>
-### <?link "Render":Tags#render ?>
-## <?link "Flow Control":Tags#flowcontrol ?>
-### <?link "For":Tags#for ?>
-### <?link "If/Elsif/Else":Tags#if ?>
-### <?link "Unless":Tags#unless ?>
-### <?link "Yield":Tags#yield ?>
-## <?link "Troubleshooting/Introspection":Tags#troubleshooting ?>
-### <?link "PP":Tags#pp ?>
-### <?link "PrettyPrint":Tags#prettyprint ?>
-
-# <?link Annotated Examples ?>
-## <?link "Simple Examples":Annotated Examples#simple-examples ?>
-## <?link "Advanced Examples":Annotated Examples#advanced-examples ?>
-
-
-h3. Authors
-
-* Michael Granger
-* Mahlon E. Smith
-
-
-h3. License
-
-Copyright © 2011, Michael Granger, Mahlon E. Smith
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are
-permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this list of
-  conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright notice, this list of
-  conditions and the following disclaimer in the documentation and/or other materials
-  provided with the distribution.
-* Neither the name of the authors nor contributors may be used to endorse or promote products
-  derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-<div id="cc-license">
-<a rel="license" href="http://creativecommons.org/licenses/by/3.0/">
-	!http://i.creativecommons.org/l/by/3.0/88x31.png(Creative Commons License)!
-</a><br/>
-The content of this manual, including images, video, and any example source code is 
-licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative 
-	Commons Attribution 3.0 License</a>.
-</div>
-

File manual/src/tags.page

----
-# vim: set et nosta sw=4 ts=4 ft=textile :
-title: Tags
-layout: default
-index: 1
-filters:
-  - erb
-  - links
-  - examples
-  - textile
----
-
-h2. <%= page.config['title'] %>
-
-<div id="auto-toc"></div>
-
-h3(#tagsyntax). Tag Syntax
-
-Tags can be in either of two formats:
-
-* XML Pre-processing instruction style: @<?tagname tagdata ?>@
-* or the same thing, but with square brackets instead: @[?tagname tagdata ?]@
-
-The second form is especially useful if you're generating HTML and want to put an Inversion tag inside the attribute of an HTML tag, but still want the template to be well-formed:
-
-<?example { syntax: html, caption: "Using square-bracket tags inside an HTML tag" } ?>
-<a href="[?call article.permalink ?]">Permalink</a>
-<?end ?>
-
-You can mix tag forms in a single document.
-
-h3(#comment). Comment
-
-The @comment@ tag can be used to temporarily stop a section of a document from being rendered:
-
-<?example { syntax: html, caption: "Using square-bracket tags inside an HTML tag" } ?>
-<?comment?>
-This stuff won't be rendered.
-<?end comment ?>
-But this stuff will.
-<?end?>
-
-Note that the tags inside the comment will still be parsed, so the template's interface won't be affected by the comment:
-
-<?example { syntax: html, caption: "Commenting out an attr tag" } ?>
-<?comment?>
-<?attr content ?>
-<?end comment ?>
-<?end?>
-
-If you have @debugging_comments@ enabled in the template <?link "options":Templates#options ?>, sections which are commented out will be rendered as a comment describing what was omitted, surrounded by the comment characters set in @comment_start@ and @comment_end@ <?link "options":Templates#options ?>, respectively.
-
-<?example { syntax: html, caption: "Rendered comment section" } ?>
-<!-- Commented out 1 nodes on line 1 -->
-<?end?>
-
-You can also embed a description of why the section is commented in the tag body, which will be used as a label when rendering the comment:
-
-<?example { syntax: html, caption: "Commenting with a label" } ?>
-<?comment Disabled until there's content ?>
-<?attr content ?>
-<?end comment ?>
-<?end?>
-
-<?example { syntax: html, caption: "Rendered comment with a label" } ?>
-<!-- Commented out 1 nodes on line 1: Disabled until there's content -->
-<?end?>
-
-h3(#config). Config
-
-The @config@ tag can be used to override template <?link "options":Templates#options ?>
-on a per-template basis.  It allows for convenient, inline settings from
-within a template rather than from the code in which the template is
-loaded.
-
-For example, if you want to enable debugging comments on a single template:
-
-<?example { syntax: html, caption: "Enable debugging comments via the config tag" } ?>
-<?config debugging_comments: true ?>
-<?end?>
-
-Multiple template options can be set simultaneously by using a YAML hash:
-
-<?example { syntax: html, caption: "Changing multiple template options" } ?>
-<?config
-    on_render_error: propagate
-    debugging_comments: true
-    comment_start: /*
-    comment_end: */
-?>
-<?end?>
-
-Note that this also allows you to set multiple options on a single line, when wrapped in braces:
-
-<?example { syntax: html, caption: "Changing multiple template options on one line" } ?>
-<?config { comment_start: "/*", comment_end: "*/" } ?>
-<?end?>
-
-
-h3(#placeholders). Placeholders
-
-h4(#attr). Attr
-
-The @attr@ tag is the primary placeholder tag for injecting dynamic values into your templates.  The most basic form is analogous to @attr_accessor@; it defines a method on the template object that, when set, replaces all occurrences of the tag in the template with the value:
-
-<?example { syntax: html, caption: "A basic template attribute" } ?>
-Title: <?attr title ?>
-<?end?>
-
-Calling the template object's @#title=@ method will inject the stringified value into that part of the output when it's rendered:
-
-<?example { syntax: ruby, caption: "Setting the title attribute" } ?>
-template.title = "How to Breed Kangaroos for Milk and Meat"
-template.render
-# => "Title: How to Breed Kangaroos for Milk and Meat"
-<?end?>
-
-The rendered values of an @attr@ tag can also be the result of calling methods on the attr value:
-
-<?example { syntax: html, caption: "An attr tag that calls a method" } ?>
-ISBN: <?attr book.isbn ?>
-<?end?>
-
-Attributes can be sprintf formatted:
-
-<?example { syntax: html, caption: "An attr tag with formatting" } ?>
-Book price: <?attr "%0.2f" % book.price ?>
-<?end?>
-
-Attributes can also contain other template objects, which allows templates to be nested within each other easily.
-
-<?example { syntax: ruby, caption: "Nesting templates" } ?>
-layout  = Inversion::Template.load( 'layout.tmpl' )
-content = Inversion::Template.load( 'content.tmpl' )
-content.caption = "Your kids will love their new Kangaroo family!"
-layout.body = content
-layout.render
-<?end?>
-
-
-h4(#call). Call
-
-@call@ is just an alias for @attr@.  Use whichever strikes your fancy.
-
-h4(#escape). Escape
-
-@escape@ works just like @attr@, but it escapes the content inserted into the template, using the configured escaping behavior.  The supported escaping behaviors are defined in a mixin called @Inversion::Escaping@. The behavior to use can be set using the <?link ":escape_format":Templates#options ?> option on the template, or in a @config@ tag; it defaults to using HTML escaping.
-
-<?example { syntax: html, caption: "Using the escape tag" } ?>
-<p>Company name: <?escape company.name ?></p>
-<?end?>
-
-If the company was AT&T, the output would look like:
-
-<?example { syntax: html, caption: "Rendered escape tag" } ?>
-<p>Company name: AT&amp;T</p>
-<?end?>
-
-
-h4(#urlencode). Uriencode
-
-The @urlencode@ tag is another @attr@-like tag, but this one does URI encoding:
-
-<?example { syntax: html, caption: "Using the uriencode tag" } ?>
-<nav>Edit <a href="/profile?name=[?uriencode person.name ?]">your profile</a></nav>
-<?end?>
-
-
-h3(#special-placeholders). Special Placeholders
-
-h4(#timedelta). TimeDelta
-
-If you need to automatically generate a human-readable description of the interval between two times, you can use the @timedelta@ tag:
-
-<?example { syntax: html, caption: "Using the timedelta tag" } ?>
-<article class="blogentry">
-	<header>
-		<p>Posted: <?timedelta entry.date_posted ?>.</p>
-	</header>
-	...
-</article>
-<?end?>
-
-The tag supports any object which support the @#to_time@ method, so @Time@, @Date@, and @DateTime@ and anything else that quacks similarly.
-
-Dates are compared against the current time, and render to approximate descriptions of the interval:
-
-* 4 days ago
-* about an hour from now
-* 6 weeks ago
-* less than a minute from now
-
-
-h3(#import-pubsub). Import and Publish/Subscribe
-
-Both of these tags operate on nested templates: one copies objects from an outer template into an inner one (@import@), and the other publishes sections of content from one template to any other subscribed template.
-
-h4(#import). Import
-
-Occasionally, you'll want to compose output from several different templates by nesting them, but you don't want to have to set common objects on all of them from code. The @import@ tag lets you copy the values from a container template into one intended to be nested within it:
-
-<?example { syntax: html, caption: "Container template (layout.tmpl)" } ?>
-Logged in as: <?attr request.authenticated_user ?>
-<?attr body ?>
-<?end?>
-
-<?example { syntax: html, caption: "Content template (body.tmpl)" } ?>
-<?import request ?>
-<p>You can check your balance using <a href="[?call request.path_info ?]/accounts">the accounts tool</a>.</p>
-<?end?>
-
-When the content template is nested in the container, you only need to set the @request@ attribute on the container to set it in both places:
-
-<?example { syntax: ruby, caption: "" } ?>
-layout = Inversion::Template.load( 'layout.tmpl' )
-body = Inversion::Template.load( 'body.tmpl' )
-
-layout.body = body
-layout.request = request
-
-puts layout.render
-<?end?>
-
-Without the use of @import@, you'd need to similarly set the request attribute on the body template.
-
-The imported attribute's value is determined at render time, so you can also use it to import values from an iteration. 
-
-<?example { syntax: html, caption: "Container template (table.tmpl)" } ?>
-<table>
-	<thead>...</thead>
-	<tbody>
-		<?for user in assigned_users ?>
-		<?attr row ?>
-		<?end for ?>
-	</tbody>
-</table>
-<?end?>
-
-<?example { syntax: html, caption: "Content template (row.tmpl)" } ?>
-<?import user ?>
-		<tr>
-			<th>Username:</th><td><?escape user.username ?></td>
-			<th>UID:</th><td><?escape user.uid ?></td>
-			<th>GID:</th><td><?escape user.gid ?></td>
-		</tr>
-<?end?>
-
-<?example { syntax: ruby, caption: "Template setup" } ?>
-usertable = Inversion::Template.load( 'table.tmpl' )
-userrow = Inversion::Template.load( 'row.tmpl' )
-
-usertable.row = userrow
-usertable.assigned_users = User.assigned.all
-
-puts usertable.render
-<?end?>
-
-When the @row.tmpl@ is rendered each time, its imported @user@ is set to whatever the @user@ in the container is, in this case the next object in @assigned_users@.
-
-You can import values into deeply-nested templates, provided each container imports it as well.
-
-
-h4(#publish-subscribe). Publish/Subscribe
-
-Often you'll want to set up a generic layout template to establish a global look-and-feel, and then modify it based on the content of an inner template. 
-
-<?example { syntax: html, caption: "Look and feel template (layout.tmpl)" } ?>
-<!DOCTYPE HTML>
-<html lang="en">
-<head>
-	<title><?subscribe title || Untitled ?></title>
-	<link rel="stylesheet" href="/css/base.css" type="text/css" media="screen" 
-		title="Base Stylesheet" charset="utf-8" />
-    <?subscribe stylesheets ?>
-
-	<script defer="defer" src="/js/jquery-1.4.2.min.js" type="text/javascript" charset="utf-8"></script>
-    <?subscribe scripts ?>
-</head>
-
-<body><?attr body ?></body>
-</html>
-<?end?>
-
-
-<?example { syntax: html, caption: "A content template (content.tmpl)" } ?>
-<?publish title ?>I make stuff up<?end publish?>
-
-<?publish stylesheets ?>
-	<link rel="stylesheet" href="/css/content.css" type="text/css" media="screen" 
-		title="Content Style Overrides" charset="utf-8" />
-<?end publish?>
-
-<?publish scripts ?>
-	<script defer="defer" src="/js/content.js" type="text/javascript" charset="utf-8"></script>
-<?end publish?>
-
-<div>Hi, there.</div>
-<?end?>
-
-<?example { syntax: ruby, caption: "Template setup" } ?>
-layout  = Inversion::Template.load( 'layout.tmpl' )
-content = Inversion::Template.load( 'content.tmpl' )
-
-layout.body = content
-
-puts layout.render
-<?end?>
-
-@subscribe@ renders to an empty string if there is no matching @publish@, or to the value of a default if supplied (as in the HTML title example above.)
-In this fashion, you can dynamically switch out different content pages, with each having the ability to optionally override various HTML elements. 
-
-h4(#include). Include
-
-The @include@ tag allows inclusion of other template files from within a template.  This supports separation of a template into several reusable components.  The included template becomes a part of the including template, along with any defaults, attributes and configuration.
-
-<?example { syntax: ruby, caption: "Include setup" } ?>
-email = Inversion::Template.load( 'email.tmpl' )
-
-email.greeting = "Kudos"
-email.company  = Company[ :spime_thorpe ]
-email.user     = User[ :jrandom ]
-
-puts main.render
-<?end?>
-
-<?example { syntax: html, caption: "Including template (email.tmpl)" } ?>
-Subject: Great news, everybody!
-From: <?attr company.email ?>
-To: <?attr user.email ?>
-
-<?attr greeting ?>, <?attr user.first_name ?>!
-
-We are excited to inform you that you have been selected to participate
-in a challenging and exciting career displacement opportunity!
-
-Please attend the mandatory Man Overboard (tm) session we have scheduled
-for you at 8:45AM on Thursday in the Sunshine Room. Light refreshments 
-and computer-aided aptitude testing will be provided.
-
-<?include signature.tmpl ?>
-<?end?>
-
-<?example { syntax: html, caption: "Included template (signature.tmpl)" } ?>
-
-Sincerely, 
-Your Friends at <?attr company.name ?>!
-<?end?>
-
-<?example { syntax: text, caption: "The rendered output" } ?>
-Subject: Great news, everybody!
-From: "Spime-Thorpe, Inc." <salesteam2@spime-thorpe.com>
-To: "James Random" <jrandom@compusa.com>
-
-Kudos, James!
-
-We are excited to inform you that you have been selected to participate
-in a challenging and exciting career displacement opportunity!
-
-Please attend the mandatory Man Overboard (tm) session we have scheduled
-for you at 8:45AM on Thursday in the Sunshine Room. Light refreshments 
-and computer-aided aptitude testing will be provided.
-
-Sincerely, 
-Your Friends at Spime Thorpe!
-<?end?>
-
-h4(#render). Render
-
-#TODO
-
-h3(#flowcontrol). Flow Control
-
-The following tags are used to alter the flow of rendering from within templates.
-
-h4(#for). For
-
-The @for@ tag iterates over the objects in a collection, rendering its
-template section once for each iteration. Its attribute can be set to anything
-that responds to @#each@. The iteration variable(s) are scoped to the block,
-and temporarily override any template attributes of the same name.
-
-<?example { syntax: ruby, caption: "'For' tag setup" } ?>
-overhead_list = Inversion::Template.load( 'employee_list.tmpl' )
-overhead_list.users = User.
-	filter( :start_date < 6.months.ago ).
-	filter( :department => 'Information Technology' ).all
-
-puts overhead_list.render
-<?end?>
-
-The @for@ tag's iteration works just like Ruby's @for@; if the enumerated
-value has more than one value, you can give a list of iteration variables to
-be assigned to.
-
-<?example { syntax: html, caption: "Employee list using 'For'" } ?>
-<table>
-	<thead>...</thead>
-	<tbody>
-		<?for user, i in users.each_with_index ?>
-        <tr class="[?if i.even? ?]even[?else?]odd[?end if?]-row">
-            <td><?attr user.first_name ?></td>
-            <td><?attr user.last_name ?></td>
-            <td><?attr user.title ?></td>
-            <td><?attr user.start_date ?></td>
-            <td><?attr user.salary ?></td>
-        </tr>
-		<?end for ?>
-	</tbody>
-</table>
-<?end?>
-
-The example above uses a Ruby enumerator for the @#each_with_index@ method to set the class of the row to @'even-row'@ or @'odd-row'@.
-
-This works with the keys and values of Hashes, too:
-
-<?example { syntax: html, caption: "Display hash of notes keyed by author using 'For'" } ?>
-<?for user, content in user.notes ?>
-<section class="note">
-	<header>
-		Note by <?call user.username ?>
-	</header>
-	<p><?escape content ?></p>
-</section>
-
-<?end for ?>
-<?end?>
-
-Note that you can also use Ruby's "external iterator" syntax to iterate, too:
-
-<?example { syntax: html, caption: "Iterate over each byte of a string with an index using 'For'" } ?>
-<section class="hexdump">
-<?for byte, index in frame.header.each_byte.with_index ?>
-	<?if index.modulo(8).zero? ?>
-		<?if index.nonzero? ?>
-	</span><br />
-		<?end if ?>
-	<span class="row"><?attr "0x%08x" % index ?>:
-	<?end if ?>
-	&nbsp;<code><?attr "0x%02x" % byte ?></code>
-<?end for ?>
-</section>
-<?end?>
-
-
-
-h4(#if). If/Elsif/Else
-
-The @if@ tag can be used to conditionally render a section of the template based on the value of an attribute or the value of a method called on it.
-
-<?example { syntax: html, caption: "Conditional block" } ?>
-<?if user.has_stock_options? ?>
-You will have 21 days to exercise your stock options.
-<?else ?>
-You have a week to optionally take home a handful of supplies from the
-office cabinet.
-<?end if ?>
-<?end?>
-
-
-h4(#unless). Unless
-
-Unless is like the @if@ tag, but with inverted logic. Note that an @unless@ can have an @else@ tag, but cannot have any @elsif@ tags within it.
-
-
-h4(#yield). Yield
-
-The @yield@ tag is used to defer rendering of some part of the template to the code that is calling <?api Inversion::Template#render:"#render" ?> on it. If a block is passed to @#render@, then the @yield@ tag will call it with the <?api Inversion::RenderState ?> object that is currently in effect, and will render the return value in its place.
-
-<?example { syntax: html, caption: "Using 'yield' to defer an expensive database lookup (report.tmpl)" } ?>
-<?if extra_details_enabled ?>
-<?yield ?>
-<?end if ?>
-
-<?example { syntax: ruby, caption: "" } ?>
-report = Inversion::Template.load( 'report.tmpl' )
-report.extra_details_enabled = true if $DEBUG
-puts report.render do
-	report_table = Inversion::Template.load( 'table.tmpl' )
-	report_table.rows = an_expensive_database_query()
-	report_table
-end
-<?end?>
-
-This will insert the @report_table@ template in place of the yield, but only if $DEBUG is true.
-
-
-h3(#troubleshooting). Troubleshooting/Introspection
-
-h4(#pp). PP
-
-The @pp@ tag uses the @PP@ library to output an escaped representation of its argument.
-
-<?example { syntax: ruby, caption: "Creating an object to inspect" } ?>
-content = Inversion::Template.load( 'content.tmpl' )
-content.file = File.stat( '/tmp/example.txt' )
-
-puts content.render
-<?end?>
-
-<?example { syntax: html, caption: "Inspecting an object from within a template" } ?>
-<div class="debugging">
-    The file's stat attributes:
-    <?pp file ?>
-</div>
-<?end?>
-
-The output is escaped according to the current setting of the <?link ":escape_format":Templates#options ?> option.
-
-<?example { syntax: html, caption: "The rendered result" } ?>
-<div class="debugging">
-    The file's stat attributes:
-        #&lt;File::Stat
- dev=0xe000004,
- ino=3064556,
- mode=0100644 (file rw-r--r--),
- nlink=1,
- uid=501 (mahlon),
- gid=0 (wheel),
- rdev=0x0 (0, 0),
- size=0,
- blksize=4096,
- blocks=0,
- atime=2011-08-12 08:43:15 -0700 (1313163795),
- mtime=2011-08-12 08:43:15 -0700 (1313163795),
- ctime=2011-08-12 08:43:15 -0700 (1313163795)&gt;</div>
-</div>
-<?end?>
-
-
-h4(#prettyprint). PrettyPrint
-
-#TODO

File manual/src/templates.page

----
-# vim: set et nosta sw=4 ts=4 ft=textile :
-title: Templates
-layout: default
-index: 1
-filters:
-  - erb
-  - links
-  - api
-  - examples
-  - textile
----
-
-h2. <%= page.config['title'] %>
-
-<div id="auto-toc"></div>
-
-Inversion <?api "templates":Inversion::Template ?> are the primary objects you'll be interacting with. Templates can be created from a string via <?api "Template#new":Inversion::Template.new ?>, or from a file with <?api "Template#load":Inversion::Template.load ?>.
-
-h3(#options). Template Options
-
-You can set Inversion's configuration options in a number of ways. 
-
-To set global options, Inversion supports the "Configurability":https://bitbucket.org/ged/configurability API, and registers itself with the @templates@ key. This means you can either add a 'templates' section to your Configurability config, or call <?api Inversion::Template.configure ?> yourself with an object that can be passed to @Hash#merge@.
-
-To set options on a per-template basis, you can pass an options hash to either @.load@ or @.new@, or set them from within the template itself using the <?link "configure tag":Tags#configure-tag ?>.
-
-The available options are:
-
-<notextile>
-<dl>
-	<dt>:ignore_unknown_tags</dt>
-	<dd>Setting to false causes unknown tags used in templates to raise an <code>Inversion::ParseError</code>.</dd>
-	<dd class="default">Default: true</dd>
-   
-	<dt>:on_render_error</dt>
-	<dd>
-		Dictates the behavior of exceptions during rendering. 
-		<dl>
-		  	<dt>:ignore</dt>
-			<dd>Exceptions are silently ignored.</dd>
-		  	<dt>:comment</dt>
-			<dd>Exceptions are rendered inline as comments.</dd>
-		  	<dt>:propagate</dt>
-			<dd>Exceptions bubble up to the caller of #render.</dd>
-		</dl>
-	</dd>
-    <dd class="default">Default: <code>:comment</code></dd>
-
-    <dt>:debugging_comments</dt>
-    <dd>Insert various Inversion parse and render statements while rendering.</dd>
-    <dd class="default">Default: <code>false</code></dd>
-
-    <dt>:comment_start</dt>
-    <dd>When rendering debugging comments, the comment is started with these characters.</dd>
-    <dd class="default">Default: <code>"&lt;!--"</code></dd>
-
-    <dt>:comment_end</dt>
-    <dd>When rendering debugging comments, the comment is finished with these characters.</dd>
-    <dd class="default">Default: <code>"--&gt;"</code></dd>
-
-    <dt>:template_paths</dt>
-    <dd>An array of filesystem paths to search for templates within, when loaded or included with a relative path.  The current working directory is always the last checked member of this.</dd>
-    <dd class="default">Default: <code>[]</code></dd>
-
-    <dt>:escape_format</dt>
-    <dd>The escaping <?api "strategy":Inversion::Escaping ?> used by tags such as <code>escape</code> and <code>pp</code>.</dd>
-    <dd class="default">Default: <code>:html</code></dd>
-
-    <dt>:strip_tag_lines</dt>
-    <dd>If a tag's presence introduces a blank line into the output, this option removes it.</dd>
-    <dd class="default">Default: <code>true</code></dd>
-
-    <dt>:stat_delay</dt>
-    <dd>Templates know when they've been altered on disk, and can dynamically reload themselves in long running applications.  Setting this option creates a purposeful delay between reloads for busy servers.</dd>
-    <dd class="default">Default: <code>0</code></dd>
-</dl>
-</notextile>