Terrence Brannon
2004-12-31 19:24:49 UTC
Recently I began to doubt that the compiler hints were a worthwhile
thing to use when creating DOM-like accessor classes to HTML documents.
This post shows the current usage of Seamstress with compiler
hints. And then gives a plethora of reasons why such a design was, on
the whole, a waste of time and should be dropped immediately :).
Overview
HTML::Seamstress is a Perl module which supports HTML templating via
tree-manipulations. It is based on HTML::Tree. The latest version of
this module was inspired by the amazingly concise code that one can
write with Class::DBI after setting up an object-oriented hierarchy.
Here are two samples of tree-based templating as the module is developed
now:
Text substitution
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 <klass=content span id="name">dummy_name</span>.
<p>Today's date is <klass=content 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 object-oriented
accessors.
use html::hello_world;
my $tree = html::hello_world->new;
$tree->name('terrence brannon')->date('5/11/1969')->as_HTML;
If-then-else with the highlander kompiler hint
The "highlander" kompiler hint is used to mark a subtree of HTML in
which only one child should survive:
<span klass="highlander" 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::highlander;
my $tree = html::highlander->new;
$tree->age_dialog
(
[
under10 => sub { $_[0] < 10} ,
under18 => sub { $_[0] < 18} ,
welcome => sub { 1 }
],
$age
)->as_HTML;
# will only output one of the 3 dialogues based on which closure
# fires first
The dilemmas
Use of the klass tag as a kompiler hint
The biggest dilemma I have is whether to alter the HTML by use of the
"klass" attribute as a kompiler hint. The original reason for writing
Seamstress was to provide HTML templating via pure Perl and pure HTML.
The original connection between the two was the "id" tag, a standard
HTML attribute which must be unique for every element within an HTML4.01
document. The Java framework which inspired the development of
Seamstress, XMLC, uses only standard "class" and "id" tags to generate
Java accessors to HTML documents.
If I wanted to eliminate the "klass" attribute, then I would have to
provide command line arguments to the Seamstress compiler to generate
certain types of methods:
$> ./seamc -klass="name content" -klass="date content" hello_world.html
But that would get tedious when dealing with a ton of files.
So, even though the HTML would be slightly modified with the use of the
"klass" tag, I think I would prefer that over having to supply kompiler
hints at the shell.
Use of any magic whatsoever
Currently, Seamstress supplies accessors which are "intelligent" in two
ways. One, they acts as getters or setters based on whether or not they
are supplied arguments:
$tree->name ; # returns $tree->look_down(id => 'name');
$tree->name(12); # will call a setter method based on compiler hint
Two, they act as specialized setters based on compiler hints. Since
there are numerous ways to "set" a node in a tree (you can set its
contents, you can set its children's contents, you can set an attribute,
you can delete all but one of the children, etc.), the tree operation
that is called is based on the compiler hint.
However, it is not clear that all the extra work to make the inline code
succint is worthwhile. The "magical" version of the hello_world.pm
program is:
$tree->name('terrence brannon')->date('5/11/1969')->as_HTML;
The "plain Jane" version is:
$tree->get_name->replace_content('terrence brannon')
->get_date->replace_conent('5/11/69);
I like the explictness of the plain version.
The plain version would simply have the Seamstress compiler create
get_$id accessors for any HTML element in the document with an id tag. This
plain version is very attractive for a number of reasons:
* the HTML file has zero shock value to the HTML designer
* no kompiler hints need to be written
* no worry about needing to expand the kompiler hints into a
mini-langauge
Over time, mini-languages tend to need more and more. If all
shortcuts and idioms are handled by library methods, then the full
power of Perl can be brought to bear on any situation.
* Similar to the way XMLC works for Java
XMLC does not use any extra tags when creating DOM accessor classes
to HTML files. The widespread success and usage of XMLC implies that
none are necessary.
If I wanted 100% compatibility with XMLC I would be using Terrence
Mather's XML::DOM. However, HTML::ElementTable is an excellent
module for imperative tree-building in Perl and I want to be
able to integrate its results into my HTML templates.
Conclusion
It is very depressing to have to rip out my compiler's guts. I spent a
good amount of time building the compiler and code-generator and
creating tests for it. Now, the compiler is going to be much simpler and
all of the idiomatic processing will exist in standalone tree-processing
libraries such as HTML::Element::Library (to be uploaded) or
HTML::ElementTable.
However, I think this is a change for the better. In fact, it is nice to
know that all tree processing actions will be handled like this:
$tree->$id_name->$library_method(@method_args);
instead of
$tree->$id_name(@args_to_magic_method)
thing to use when creating DOM-like accessor classes to HTML documents.
This post shows the current usage of Seamstress with compiler
hints. And then gives a plethora of reasons why such a design was, on
the whole, a waste of time and should be dropped immediately :).
Overview
HTML::Seamstress is a Perl module which supports HTML templating via
tree-manipulations. It is based on HTML::Tree. The latest version of
this module was inspired by the amazingly concise code that one can
write with Class::DBI after setting up an object-oriented hierarchy.
Here are two samples of tree-based templating as the module is developed
now:
Text substitution
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 <klass=content span id="name">dummy_name</span>.
<p>Today's date is <klass=content 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 object-oriented
accessors.
use html::hello_world;
my $tree = html::hello_world->new;
$tree->name('terrence brannon')->date('5/11/1969')->as_HTML;
If-then-else with the highlander kompiler hint
The "highlander" kompiler hint is used to mark a subtree of HTML in
which only one child should survive:
<span klass="highlander" 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::highlander;
my $tree = html::highlander->new;
$tree->age_dialog
(
[
under10 => sub { $_[0] < 10} ,
under18 => sub { $_[0] < 18} ,
welcome => sub { 1 }
],
$age
)->as_HTML;
# will only output one of the 3 dialogues based on which closure
# fires first
The dilemmas
Use of the klass tag as a kompiler hint
The biggest dilemma I have is whether to alter the HTML by use of the
"klass" attribute as a kompiler hint. The original reason for writing
Seamstress was to provide HTML templating via pure Perl and pure HTML.
The original connection between the two was the "id" tag, a standard
HTML attribute which must be unique for every element within an HTML4.01
document. The Java framework which inspired the development of
Seamstress, XMLC, uses only standard "class" and "id" tags to generate
Java accessors to HTML documents.
If I wanted to eliminate the "klass" attribute, then I would have to
provide command line arguments to the Seamstress compiler to generate
certain types of methods:
$> ./seamc -klass="name content" -klass="date content" hello_world.html
But that would get tedious when dealing with a ton of files.
So, even though the HTML would be slightly modified with the use of the
"klass" tag, I think I would prefer that over having to supply kompiler
hints at the shell.
Use of any magic whatsoever
Currently, Seamstress supplies accessors which are "intelligent" in two
ways. One, they acts as getters or setters based on whether or not they
are supplied arguments:
$tree->name ; # returns $tree->look_down(id => 'name');
$tree->name(12); # will call a setter method based on compiler hint
Two, they act as specialized setters based on compiler hints. Since
there are numerous ways to "set" a node in a tree (you can set its
contents, you can set its children's contents, you can set an attribute,
you can delete all but one of the children, etc.), the tree operation
that is called is based on the compiler hint.
However, it is not clear that all the extra work to make the inline code
succint is worthwhile. The "magical" version of the hello_world.pm
program is:
$tree->name('terrence brannon')->date('5/11/1969')->as_HTML;
The "plain Jane" version is:
$tree->get_name->replace_content('terrence brannon')
->get_date->replace_conent('5/11/69);
I like the explictness of the plain version.
The plain version would simply have the Seamstress compiler create
get_$id accessors for any HTML element in the document with an id tag. This
plain version is very attractive for a number of reasons:
* the HTML file has zero shock value to the HTML designer
* no kompiler hints need to be written
* no worry about needing to expand the kompiler hints into a
mini-langauge
Over time, mini-languages tend to need more and more. If all
shortcuts and idioms are handled by library methods, then the full
power of Perl can be brought to bear on any situation.
* Similar to the way XMLC works for Java
XMLC does not use any extra tags when creating DOM accessor classes
to HTML files. The widespread success and usage of XMLC implies that
none are necessary.
If I wanted 100% compatibility with XMLC I would be using Terrence
Mather's XML::DOM. However, HTML::ElementTable is an excellent
module for imperative tree-building in Perl and I want to be
able to integrate its results into my HTML templates.
Conclusion
It is very depressing to have to rip out my compiler's guts. I spent a
good amount of time building the compiler and code-generator and
creating tests for it. Now, the compiler is going to be much simpler and
all of the idiomatic processing will exist in standalone tree-processing
libraries such as HTML::Element::Library (to be uploaded) or
HTML::ElementTable.
However, I think this is a change for the better. In fact, it is nice to
know that all tree processing actions will be handled like this:
$tree->$id_name->$library_method(@method_args);
instead of
$tree->$id_name(@args_to_magic_method)
--
Carter's Compass: I know I'm on the right track when,
by deleting something, I'm adding functionality.
Carter's Compass: I know I'm on the right track when,
by deleting something, I'm adding functionality.