The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

HTML::Seamstress - HTML::Tree subclass for HTML templating via tree rewriting

SYNOPSIS

Text substitution via replace_content() API call.

In our first example, we want to perform simple text substitution on the HTML template document. The HTML file html/hello_world.htm has klass attributes which serve as compiler (kompiler?) hints to Seamstress:

 <html>
 <head>
   <title>Hello World</title>
 </head>
 <body>
 <h1>Hello World</h1>
   <p>Hello, my name is <span id="name">dummy_name</span>.
   <p>Today's date is <span id="date">dummy_date</span>.
 </body>
 </html>

Seamstress compiles HTML to html::hello_world

 shell> seamc html/hello_world.htm
 Seamstress v2.91 generating html::hello_world from html/hello_world.htm

Now you simply use the "compiled" version of HTML with API calls to HTML::TreeBuilder, HTML::Element, and HTML::Element::LIbrary

 use html::hello_world; 
 
 my $tree = html::hello_world->new; 
 $tree->look_down(id => name)->replace_content('terrence brannon');
 $tree->look_down(id => date)->replace_content('5/11/1969');
 print $tree->as_HTML;

If-then-else with the highlander API call

 <span id="age_dialog">
    <span id="under10">
       Hello, does your mother know you're 
       using her AOL account?
    </span>
    <span id="under18">
       Sorry, you're not old enough to enter 
       (and too dumb to lie about your age)
    </span>
    <span id="welcome">
       Welcome
    </span>
 </span>

Compile and use the module:

 use html::age_dialog;

 my $tree = html::dialog->new;

 $tree->highlander
    (age_dialog =>
     [
      under10 => sub { $_[0] < 10} , 
      under18 => sub { $_[0] < 18} ,
      welcome => sub { 1 }
     ],
     $age
    );

  print $tree->as_HTML;

  # will only output one of the 3 dialogues based on which closure 
  # fires first 

The following libraries are always available for more complicated manipulations:

PHILOSOPHY / MOTIVATION for HTML::Seamstress

The motivation for HTML::Seamstress was to provide a module which would allow for pure HTML to be dynamically "unrolled" without any foreign elements in the HTML itself.

This design goal can be met by the 3 most popular templating systems for Perl - HTML::Mason, HTML::Template and Template, but none of them enforce it by design.

A second design goal for Seamstress was to have only two technologies, object-oriented Perl and HTML. Neither HTML::Template or Template meet this design goal. HTML::Mason has components defined as a *mix* of Perl and HTML as it's fundamental item of reuse. Seamstress keeps the two technologies pure and separate.

Mixing Perl and HTML means that validation tools for both technologies is unavailable. Mixing mini-languages and HTML means that you are stuck with the disadvantages of mini-languages. The disadvantages of mini-languages are discussed here:

http://perlmonks.org/?node_id=428053

A striking example of their limitations is shown here: http://perlmonks.org/?node_id=493477

So the above 3 modules can enforce the stated design goals but there are ways around these goals. The only CPAN modules which enforce both design goals completely are XML::LibXML, PeTaL and this module. If you have time to generate high-quality XML and are comfortable with DOM then there is no problem with this module. If you prefer a Perlish and intuitive collection of methods, then the fact that Seamstress is built on top of HTML::TreeBuilder will appeal to you. Finally, we come to PeTaL. A nice module, but I don't enjoy learning mini-languages and mastery of PeTal entails learning TAL and MetaTAL.

DESCRIPTION

This section will cover two things. What Seamstress offers and the theory of HTML as trees.

HTML as Trees

From reading HTML::Tree::Scanning, we know that HTML has a tree structure. HTML::Seamstress is a subclass of HTML::TreeBuilder and HTML::Element::Library which makes it easier to perform common HTML templating operations as tree rewriting.

Text Substitution == Node rewriting

The "SYNOPSIS" gave an example of text substitution. From a tree-writing perspective, text substitution involves an in-place change to the content of a node of an HTML tree.

You will find the "Tree Rewriting Methods" in HTML::Element::Library of great use when doing this sort of templating.

Conditional Processing (aka if/unless) == Node Deletion

In tree rewriting terms, an if directive is used to decide whether a particular node of the HTML tree is preserved or deleted.

The most useful function for this is the highlander method, also a part of HTML::Element::Library.

Looping (e.g. Table Unrolling) == Child Replication

Table unrolling, pulldown creation, li unrolling, and dl unrolling are all examples of a tree operation in which you take a child of a node and clone it and then alter it in some way (replace the content, alter some of its attributes), and then stick it under its parent.

Functions for use with the common HTML elements --- <table>, <ol>, <ul>, <dl>, <select> are documented in HTML::Element::Lbrary and are prefaced with the words "Tree Building Methods".

What Seamstress offers

All of the tree-rewriting functionality discussed above exists outside of this distribution in libraries

The module HTML::Seamstress is a base class of HTML::Element and HTML::Element::Library which makes it easy to access the API calls of those modules in a convenient fashion.

The HTML::Seamstress distribution also comes with a small script, seamc, which creates .pm files for .htm(l)? files, which can then be used by your Perl code. The .pm files have only a constructor, new, which returns a tree of the HTML file blessed into the HTML::Seamstress class, which due to its subclassing, can be operated on by most of the modules listed above. Matthew Sisk's modules have not been subclassed or required by Seamstress because I have not had the personal need to use them, but doing so should be straightforward. Tests and patches welcome.

seamc requires a file called seamc.cfg to exist in the directory above the root for your HTML. Thus if your HTML is in a directory called html, the file seamc.cfg would be a sibling of the directory html. Here's an exmaple from a website I am doing now:

  /var/www/terry/gimblerus.com:
  total 56
  drwxr-xr-x  11 terry terry 4096 Feb  3 02:19 .
  drwxr-xr-x   5 terry terry 4096 Feb  4 00:46 ..
  -rw-r--r--   1 terry terry  318 Jan 30 02:59 favicon.ico
  drwxr-xr-x   2 terry terry 4096 Feb  3 23:57 html
  drwxr-xr-x   2 terry terry 4096 Jan 29 23:12 img
  -rw-r--r--   1 terry terry  544 Feb  2 16:35 index.html
  -rw-r--r--   1 terry terry    1 Jan 29 16:41 seamc.cfg
  drwxr-xr-x   3 terry terry 4096 Jan 29 23:13 sql

  /var/www/terry/gimblerus.com/html:
  total 148
  drwxr-xr-x   2 terry terry 4096 Feb  3 23:57 .
  drwxr-xr-x  11 terry terry 4096 Feb  3 02:19 ..
  -rw-r--r--   1 terry terry 1188 Feb  3 23:57 addclan.html
  -rw-r--r--   1 terry terry  396 Feb  2 01:06 addclan.pm  # <- Seamstress!
  -rw-r--r--   1 terry terry  323 Feb  3 17:44 footer.html
  -rw-r--r--   1 terry terry  394 Feb  1 23:50 footer.pm   # <- Seamstress!

To use seamc, you simply type

  seamc -verbose $html_file 

from any directory. seamc will ascend the directory tree until it finds the seamc.cfg file and then create a Perl module whose name will allow it to be used as long as the html/ directory is in your Perl include path, @INC.

For the example above, footer.pm will have package name html::footer and would be used like so:

 use lib '/var/www/terry/gimblerus.com';
 use html::footer;

 my $tree = html::footer->new;
 
 # ... etc

A simple structure-modifying method, expand_replace()

Let's say you have this HTML:

     <div id="sidebar">

        <div class="sideBlock" id=mpi>mc::picBar::index</div>

        <div class="sideBlock" id=mnm>mc::navBox::makeLinks</div>

        <div class="sideBlock" id=mg>mc::gutenBox</div>

      </div>

In this case, the content of each sideBlock is the name of a Perl Seamstress-style class. As you know, When the constructor for such a class is called an HTML::Element, $E, will be returned for it's parsed content.

In this case, we want the content of the div element to go from the being the class name to being the HTML::Element that that class constructs, in other words:

and so to inline all 3 tags you would do the following;

 $tree->look_down(id => $_)->expand_replace for qw(mpi mnm mg);

SEE ALSO

HTML Templating as Tree Rewriting: Part I: "If Statements"

http://perlmonks.org/index.pl?node_id=302606

HTATR II: HTML table generation via DWIM tree rewriting

http://perlmonks.org/index.pl?node_id=303188

Los Angeles Perl Mongers Talk on HTML::Seamstress

http://www.metaperl.com

XMLC, A similar framework for Java

http://xmlc.enhydra.org

Similar Frameworks for Perl

Two other frameworks come to mind. Both are stricter with regard to the correctness of the HTML and both use a different means for node lookup and rewrite.

For me, Seamstress was a natural extension of my love for HTML::TreeBuilder, but if I had an XML job to do, I really think I would reach for Petal. It is quite sweet.

  • Petal

    Based on Zope's TAL, this is a very nice and complete framework that is the basis of MkDoc, a XML application server. It offers a mini-language for XML rewriting, Seamstress does not. The philosophy of the Seamstress is the orthogonal integration of Perl and HTML not a mini-language and HTML.

  • XML::LibXML

    By the XML guru Matt Sergeant, who is also the author of AxKit, another XML application server. This offers XPath for finding nodes

  • XML::DOM

    If I wanted to ape XMLC entirely, I would have used TJ Mather's XML::DOM. Because XMLC is based around DOM API calls. However, TreeBuilder is very handy and has a lot of nice libraries around it such HTML::PrettyPrinter. The biggest win of XML::DOM is it's easy integration with XML::Generator

    From the docs, it looks like XML::GDOME is the successor to this module.

AUTHOR

Terrence Brannon, <tbone@cpan.org<gt>

COPYRIGHT AND LICENSE

Copyright 2002-2005 by Terrence Brannon.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.