NAME
HTML::Seamstress - dynamic HTML generation via pure HTML and pure Perl.
SYNOPSIS
# HTML
<html>
# supply element automatically binds $s->{supply}
<table class=supply id="$s->_aref($s->load_data)">
<tr> <th>name<th>age<th>weight</th> </tr>
# iterator element automatically binds $s->{iterator}
<tr class=iterator id="$s->{supply}->shift">
<td class=worker id="$s->_text($s->{iterator}->{name})"> </td>
<td class=worker id="$s->_text($s->{iterator}->{age})"> </td>
<td class=worker id="$s->_text($s->{iterator}->{weight})"> </td>
</tr>
</table>
</html>
# Perl call to generate HTML
use HTML::Seamstress;
HTML::Seamstress->weave(html => 'simple.html', using => 'Simple::Class');
# Perl call to generate a Perl program, which when run, generates HTML
use HTML::Seamstress;
HTML::Seamstress->compile(html => 'simple.html', using => 'Simple::Class');
# Simple/Class.pm
package simple;
use base qw(HTML::Stitchery);
my @name = qw(bob bill brian babette bobo bix);
my @age = qw(99 12 44 52 12 43);
my @weight = qw(99 52 80 124 120 230);
sub new {
my $this = shift;
bless {}, ref($this) || $this;
}
sub load_data {
my @data;
for (0 .. 5) {
push @data, {
age => $age[rand $#age] + int rand 20,
name => shift @name,
weight => $weight[rand $#weight] + int rand 40
}
}
return \@data;
}
1;
DESCRIPTION
Disclaimer One - THIS IS ALPHA SOFTWARE. USE AT YOUR OWN RISK
Disclaimer Two - This package is (too?) similar to HTML::Template
On to the description
HTML::Seamstress
allows webpages to be built by serving as the bridge between experts in their respective pure technologies: HTML experts do their thing, object-oriented Perl experts do their thing and HTML::Seamstress
serves to weave the two together, traversing the pure HTML and making use of the output of Perl objects at various points in the traversal.
Its distinctive feature, unlike existing techniques, is that it uses pure, standard HTML files: no print-statement-laden CGI scripts, no embedded statements from some programming langauge, and no pseudo-HTML elements. Code is cleanly separated into a separate file. What links the two together are semantic attributes (CLASS and ID) for HTML elements.
In model-view-controller terms, the HTML is the view. Seamstress is the controller, making calls to view-agnostic Perl methods to retrieve model data for inclusion in the view.
In HTML::Seamstress
the model classes are completely view and controller independant, and are thus use-able outside of HTML and most importantly, unit-testable outside of Perl.
Seamstress knows what Perl methods to call and when by the looking up CLASS attributes within a tag that are special to it. The attributes are supply
, iterator
, and worker
tags. The id
tag is used for Perl code.
A
worker
class is used when actual "work" is going to be done on the HTML file. This work is usually simple such as_text
, which sets the content aspect of theHTML::Element
to some text. The others are listed in the manpage forHTML::Stitchery
.A
supply
class is called for side-effect. It creates a store of data for use byiterator
andworker
tags. Note that this creation may be actual or via a Perltie
.HTML::Seamstress
automatically stores the results ofsupply
attribute evaluation in the page object under$s-
{supply}> for later reference.An
iterator
class is used to pull records from asupply
store previously created.HTML::Seamstress
automatically stores the results ofiterator
attribute evaluation in the page object under$s-
{iterator}> for later reference.
Companion modules
HTML::Seamstress
has one simple job as described above. A number of other modules, work to make this a complete suite for other HTML-based tasks:
HTML::Stitchery
provides a number of useful "stitches" to be automatically woven into HTML files. These stitches are nothing more than object methods. For practical examples of many common dynamic HTML generation tasks such as dynamic table generation, language localization, database connectivity, conditional HTML, and so forth see its documentation. But in doing so, note well that all of the above tasks are implemented as Perl object methods and incur the minal class attribute adulteration of the HTML. In fact, the actual attribute to be used can be configured by setting$worker_attr
,$supply_attr
,$iterator_attr
inHTML::Seamstress
.CGI::Seamstress
is a derived class ofHTML::Seamstress
,HTML::Stitchery
andCGI.pm
. All capabilities of each of these individual technologies are offered to the programmer while placing no burden on the HTML designer. It, ahem, isn't written yet. But it does sound good and orthogonal, doesn't it? :)Apache::Seamstress
is a derived class ofHTML::Seamstress
,HTML::Stitchery
,CGI.pm
, andApache::Request
. And it isn't written yet either. Sigh. I don't think it should be actually. A better way to seamless support both CGI and mod_perl is viaApache::Registry
Sample usage of HTML::Seamstress
The t/
directory of _HTML::Stitchery_ (not HTML::Seamstress) contains a large number of examples. Further examples are in the CGI::Seamstress and Apache::Seamstress directories.
This, and other small snippets of documentation are taken from Paul J. Lucas' HTML Tree distribution (http://homepage.mac.com/pauljlucas/software/html_tree), which CHTML::Seamstress
was based on but changed due to experience.
The HTML File
The file for a web page is in pure HTML. (It can also contain JavaScript code, but that's irrelevant for the purpose of this discussion.) At every location in the HTML file where something is to happen dynamically, an HTML element must contain a CLASS
attribute (and perhaps some "dummy" content). (The dummy content allows the web page designer to create a mock-up page.)
For example, suppose the options in a menu are to be retrieved from a relational database, say the flavors available on an ice cream shop's web site. The HTML would look like this:
<SELECT NAME="Flavors" SUPPLY="$s->query_flavors">
<SPAN CLASS=ITERATOR id="$s->{supply}->next_flavor">
<OPTION CLASS=WORKER id="$s->_text($s->{iterator}->{flavor}" VALUE="0">
Tooty Fruity
</OPTION>
</SPAN>
</SELECT>
query_flavors
, next_flavor
, and the worker will be used to generate HTML dynamically. The values of the attributes
attributes can be any Perl code as long as they agree with those in the code file (specified later).The text "Tooty Fruity" is dummy content.
The query_flavors
SUPPLY
will be used to perform the query from the database; next_flavor
will be used to fetch every tuple returned from the query and to substitute the name and ID number of the flavor.
The Code File
The associated code file in Perl in specified via the weave
configuration option of HTML::Seamstress
.
The implementation of the query_flavors()
and next_flavor()
methods shall be presented in stages.
The query_flavors()
method begins by getting its arguments as described above:
sub query_flavors {
my $this = shift;
A copy of the database and statement handles is stored in the object's hash so the next_flavor()
method can access them later:
$this->{ dbh } = DBI->connect( 'DBI:mysql:ice_cream:localhost' );
$this->{ sth } = $this->{ dbh }->prepare( '
SELECT flavor_id, flavor_name
FROM flavors
ORDER BY flavor_name
' );
$this->{ sth }->execute();
Finally, the method returns true to tell HTML::Seamstress
to proceed with parsing the SELECT
element's child elements if any rows were returned from the query.
return $this->{sth}->rows;
}
The next_flavor()
method begins identically to query_flavors()
:
sub next_flavor {
my $this = shift;
The next portion of code fetches a tuple from the database. If there are no more tuples, the method returns false. This tells HTML::Seamstress
not to emit the HTML for the OPTION
element and also tells it to stop looping:
my( $flavor_id, $flavor_name ) = $this->{ sth }->fetchrow();
unless ( $flavor_id ) {
$this->{ sth }->finish();
$this->{ dbh }->disconnect();
return 0;
}
The code also disconnects from the database. (However, if Apache::DBI
was specified, and we are running mod_perl, then the disconnect()
becomes a no-op and the connection remains persistent.)
Finally, the method returns true to tell HTML::Seamstress
to emit the HTML for the OPTION
element now containing the dynamically generated content: return $flavor_name;
}
1;
Things you may be wondering
Where is the if-then-else tag?
There is no if-then-else tag. That functionality occurs implicitly. When a portion of the HTML document is bracketed by a tag whose class
is supply
and that tag's id
when evaluated returns a Perl false value, then that tag, and the entire HTML subtree under it are not placed in the output document.
This is what an if statement does
Also note, some cases of if-then-else are better handled by doing if-then-else in your CGI program and then issuing a redirect to one of several appropriate pages instead of spaghetti'ing the hell out of one document with a maelstrom of if-then-elses.
Where is the loop tag?
Again there isn't one. The combination of supply
and iterator
serves as a basic general purpose loop. In fact, if you look at the compiled code, you will see something like this: { last unless $page->{iterator}->() ...
redo;
}
which is the most general Perl loop available.
Where is the include file tag?
There isn't one. I insist of using HTML reuse via HTML design programs. Seamstress believes that a quality HTML designer can do quality things with a quality program and library element re-use is one of those things.
Closely related software products
HTML::Template
I consider this package to be to be HTML::Template's little brother. HTML::Template is more mature than this and also has excellent and flexible caching technology built in. Both modules believe that very little logic should exist in HTML files.
So why continue with it if the packages are similar in philosophy and HTML::Template is well designed and debugged? For me, the answer is manyfold:
HTML::Template is already showing signs that it's restrictive variable-only approach is somewhat weak
Note the recent creation of HTML::Template::Expr. And note that you are moving into "3rd technology" (ie. something other than pure Perl and pure HTML) real with this and you must learn it's new rules and exceptions.
In fact, HTML::Template itself requires you to learn a number of pseudo-HTML operators and then learn how to convert pure model code to a hashref for use by them.
With Seamstress, if you know Perl and simply add CLASS and ID tags in the HTML tags where you want dynamic expansion you are finished.
Because the templating instructions are within the HTML tags themselves the page will always be cleaner than an HTML::Template page
The comparative example below will show this.
HTML::Seamstress can compile its templated documents into pure Perl
This is useful for fast execution in CGI or mod_perl.
Let's write a sample piece of code in both HTML::Template and HTML::Seamstress.
<HTML>
<HEAD><TITLE>Test Template</TITLE>
<BODY>
My Home Directory is <TMPL_VAR NAME=HOME>
<P>
My Path is set to <TMPL_VAR NAME=PATH>
</BODY>
</HTML>
<HTML>
<HEAD><TITLE>Test Template</TITLE>
<BODY>
My Home Directory is <SPAN CLASS=worker id="$s->_text($ENV{HOME}")> </SPAN>
<P>
My Path is set to <SPAN CLASS=worker id="$s->_text($ENV{PATH}")> </SPAN>
</BODY>
</HTML>
Hmm, the HTML::Template code is cleaner. Let's try something harder. I have to win this argument. :-)
<TMPL_LOOP NAME="THIS_LOOP">
Word: <TMPL_VAR NAME="WORD"><BR>
Number: <TMPL_VAR NAME="NUMBER"><P>
</TMPL_LOOP>
<span class=supply id="$s->{this_loop}">
<span class=iterator id="$s->{supply}->Next"> # next via Set::Array method
Word: <br class=worker id="$s->_text($s->{iterator}{word})"></br>
Numb: <br class=worker id="$s->_text($s->{iterator}{number})"></br>
</span>
</spna>
How this software differs from Paul J. Lucas' HTML_Tree
In concept, Seamstress and Lucas' HTML_Tree (call it ltree) are the same, with Seamstress being developed after usage of ltree. However, there are some technical differences between the packages:
HTML::Seamstress
parses the HTML using SBURKE's HTML::Tree distribution. SBURKE's HTML parser is written in C and Paul Lucas' HTML parser is written in C++, so they are both fast.in ltree, a .pm file must be associated with your HTML file and you must write your own constructor.
Both of these steps are optional with
HTML::Seamstress
. You can always inherit theHTML::Stitchery
constructor if you want and if you have an HTML file that you don't wantHTML::Seamstress
dhtml in, then you don't have to have a dummy .pm file, simply omit theusing
argument to HTML::Seamstress'weave
method... actually at the moment I require a file a well, but this restriction and automatic common page objects will be made available in a later version.method lookups in ltree are done through two mechanisms: a hash called
%function_map
:% function_map = ( href_id => \&sub_href_id , .... ) ;
and the
class_map
attribute of your required constructor in your required .pm file.HTML::Seamstress
simply relies on Perl object-oriented single dispatch. But it could do multiple-dispatch as well.each object method had to do its own argument splitting in ltree:
# ltree example - note API is slightly different too sub sub_href_id { my ($this, $node, $class_arg, $is_end_tag) = @_; my ($method, @arg) = split '::', $class_arg;
Because
HTML::Seamstress
uses pure Perl in the attributes as opposed to a pseudo-HTML-like language, it does not have to do argument splitting but lets Perl handle that.ltree does not support traversal of infinite levels of reference nesting.
Instead it has a simple text() method which returns the key in the hash of its arguments. Because
HTML::Seamstress
uses Perl, arbitrary degrees of nesting of hashes, arrays, as well as method overloads could be supported.
AUTHOR
T. M. Brannon <tbone@cpan.org>
SEE ALSO
Paul J. Lucas' HTML Tree distribution
(http://homepage.mac.com/pauljlucas/software/html_tree)
XMLC
This is a Java framework with the exact same HTML pages. It creates DOM object files and works on HTML, XHTML and XML
http://xmlc.enhydra.org/
HTML Tidy
The demo programs tidy up their HTML output via this program:
http://www.w3.org/People/Raggett/tidy/
2 POD Errors
The following errors were encountered while parsing the POD:
- Around line 531:
You forgot a '=back' before '=head1'
- Around line 820:
You forgot a '=back' before '=head1'