Beefy Boxes and Bandwidth Generously Provided by pair Networks Cowboy Neal with Hat
XP is just a number

Highly Orthogonal Web Application Development with HTML_Tree

by princepawn (Parson)
on Aug 24, 2001 at 22:03 UTC ( #107656=perltutorial: print w/ replies, xml ) Need Help??

Highly Orthogonal Web Application Development with HTML_Tree

Update: the new CPAN distribution with similar and improved functionality is HTML::Seamstress
update: if you do a distribution search on then you will find HTML_Tree.

However, on you had better search for Apache::HTML::ClassParser which is part of the HTML_Tree distribution

HTML_Tree is supposedly yet another templating module. However, as far as I know, it is the only one in which the HTML to be templated is a completely syntactically valid HTML file, meaning no tags exist which lead to a syntacticaly invalid HTML file.

The way that HTML_Tree works its magic is by parsing the HTML file to be templated and searching for CLASS attributes within HTML tags. It presumes the CLASS attribute to be a pointer to a method in a module with the same name as the html file, sans .chtml, plus .pm. Four examples of this type of usage will be explained in detail. For now, a bit more on what HTML_Tree has to offer and how.

For me, the big win of HTML_Tree is that it allows me to build a complete site and then have a graphic artist come behind and work with pure HTML to beautify it.

HTML_Tree is supposedly Apache::Filter-aware, but I had to fix a small bug in how it handled such filter chains. Add the recreation of $r and you are set:

if ( lc( $r->dir_config( 'Filter' ) ) eq 'on' ) { $r = $r->filter_register; ## add this line $r->filter_input();
I tried emailing the author at his advertised CPAN email about this, but it bounced.

And another neat thing is that it uses it's own HTML parser which is written entirely in C++ and is several orders of magnitude faster HTML::Parser as a result.

The final thing that I like about this module is that I can write Perl classes that I can run from the shell (meaning, without a webserver). Thus my Perl code is runnable and testable in terms of its text generation for the webserver without having to debug via Apache error logs.

Now that the introductions are done, let's work through some examples using HTML_Tree. For your reference, for the next few days from Fri Aug 24 09:36:41 EDT 2001, the examples are at: this link.

Example One: dyanmic display of date and time

<html> <head> <h3>Date Time Page</h3> </head> <body> <h3>Date Time Page</h3> The current date and time is <span class="date_time">4:44pm on the dot</span> </body> </html>
package index; sub new { my $that = shift; my $class = ref( $that ) || $that; my $this = { class_map => { date_time => \&amp;amp;amp;amp;date_time, }, # other stuff you want here ... }; return bless $this, $class; } sub date_time { my( $this, $node, $class, $is_end_tag ) = @_; ($node->children())[0]->text( `date` ); }
1; </code>

Ok, note in the HTML there is a CLASS attribute whose value is date_time. When HTML_Tree sees this attribute, it uses the class_map of the object associated with this HTML file to find the method to call. In this method, we set the text aspect of the SPAN to the date. And we are done.

Conditional HTML

HTML <html> <head> <h3>Conditional HTML</h3> </head> <body> <h1>Conditional HTML</h1> This page will show a table when the calculated random numer is odd. Here is what we calculated : <B class="calc_random"> <span class="show_random">dummy text</span> </B> which has a modulus of <I CLASS="text::display">dummy text</I> <table class="if::display"> <tr><th>yo<th>ho<th>ho <tr><td>and<td>a<td>bottle <tr><td>of<td>rum,<td>son <tr><td>let's<td>have<td>fun! </table> </body> </html> Perl package index; srand (time ^ $$ ); sub new { my $that = shift; my $class = ref( $that ) || $that; my $this = { class_map => { calc_random => \&amp;amp;amp;amp;calc_random, show_random => \&amp;amp;amp;amp;show_random, show_tab => \&amp;amp;amp;amp;show_tab, }, # other stuff you want here ... }; return bless $this, $class; } sub calc_random { my( $this, $node, $class, $is_end_tag ) = @_; return 0 if $is_end_tag; $this->{random} = sprintf '%d', rand(1000); return 1; } sub show_random { my( $this, $node, $class, $is_end_tag ) = @_; return 0 if $is_end_tag; $this->{display} = ($this->{random} % 2); ($node->children())[0]->text( $this->{random}); return 1; } 1;

The calling of method subroutines via class_map should be old-hat to you by now. However, two shortcut methods from HTML_Tree were used in this case to simplify the coding. text::display is a shorthand for accessing the key display in the object and setting the text field to it's value. if::display access the display key and then includes the encapsulated HTML if that key returns true.

Dynamic Table Row Expansion

One of the neat things about HTML::Embperl is that you can load up an array with some data and have it loop across the data to create table rows. In HTML_Tree I will only show parts of the HTML here, so you can get an idea of how HTML_Tree allows you to do the same thing.

<tr class="next_row"> <td class=text::USER>billy_bob <td class=text::PID>123 <td class=text::CPU>456 <td class=text::MEM>789 <td class=text::TIME>101 <td class=text::COMMAND>666 </tr> sub next_row { my( $this, $node, $class, $is_end_tag ) = @_; return 1 if $is_end_tag; my $row = shift @{$this->{ps_table}}; return 0 if !$row; my @row = split /\s+/, $row; @{$this}{qw(USER PID CPU MEM TIME COMMAND)} = (@row[0..3],@row[9,10]); return 1; }
Basically the way this gets unrolled out into a bunch of table rows is handled by a few keys line. First     return 1 if $is_end_tag; What this is saying is: "When you, the HTML parser sees at tr end tag, reparse the tr tag, thus recalling the CLASS attribute. This way, I can get another crack at creating another table row". Next:
my $row = shift @{$this->{ps_table}}; return 0 if !$row;
Here we try to get another line from the ps command. If there are no more lines, then we return 0 and the HTML parser knows that this element can now be skipped and document processing can continue.

Otherwise, we simply load the fields of the ps command into parts of the hashref so that we can print them as individual td elements.

So now you should have a good feel for how table rows can be expanded dynamically. By dynamic, I mean that the HTML and webserver had no predefined notions of how many rows were going to come back. It was only by using next_row() in a callback fashion that it finally gave HTML_Tree the go-ahead to quit churning out rows.

Dynamic Table Rows and Data via Reformed Lists

Our final example in this set extends this idea one step further. In this case, not only are the number of rows dynamically determined but the number of elements within a row. And this is trivial with HTML_Tree we simply make the td callback return 0 when we have no more row elements and the tr callback do the same.

HTML <html> <head> <h3>The Reformed Data</h3> </head> <body> <h1>The Reformed Data</h1> <P>You requested to have your data formatted into <B CLASS=show_no_rows>300,00</B> columns: <P>And here is your wonderful data: <B CLASS=show_data>blah blah blah</B> <P>And now displayed as a table: <table class=reform_data> <tr class=table_row><td class=td>HOHOHO</td></tr> </table> </body> </html> Perl package index; sub new { my $that = shift; my $class = ref( $that ) || $that; my $this = { class_map => { show_no_rows => \&amp;amp;amp;amp;show_no_rows, show_data => \&amp;amp;amp;amp;show_data, reform_data => \&amp;amp;amp;amp;reform_data, table_row => \&amp;amp;amp;amp;table_row, td => \&amp;amp;amp;amp;td, }, # other stuff you want here ... }; return bless $this, $class; } sub show_no_rows { my( $this, $node, $class, $is_end_tag ) = @_; return 0 if $is_end_tag; $this->{rows} = $this->{ r }->param('no_rows'); ($node->children())[0]->text( $this->{rows} ); return 1; } sub show_data { my( $this, $node, $class, $is_end_tag ) = @_; return 0 if $is_end_tag; $this->{data} = [ split /\s+/, $this->{ r }->param('data') ]; ($node->children())[0]->text( $this->{r}->param('data' ) ); return 1; } sub reform_data { use Array::Reform; my( $this, $node, $class, $is_end_tag ) = @_; return 0 if $is_end_tag; warn "no_rows: $this->{rows}"; $this->{reform_data} = [ Array::Reform->reform($this->{rows}, $this->{data} ) ]; use Data::Dumper; warn Data::Dumper->Dump([$this->{reform_data}],['reform_data']); return 1; } sub table_row { my( $this, $node, $class, $is_end_tag ) = @_; warn 'table_row'; return 1 if $is_end_tag; my $rows= $this->{rows}; $this->{td} = shift @{$this->{reform_data}}; warn "THIS_TD: @{$this->{td}}"; return 0 if !$this->{td}; return 1; } sub td { my( $this, $node, $class, $is_end_tag ) = @_; warn 'td'; return 1 if $is_end_tag; my $rows= $this->{rows}; my $td = shift @{$this->{td}}; return 0 if !$td; ($node->children())[0]->text($td); return 1; } 1;

Comment on Highly Orthogonal Web Application Development with HTML_Tree
Select or Download Code
Re: Highly Orthogonal Web Application Development with HTML_Tree
by runrig (Abbot) on Aug 24, 2001 at 22:18 UTC
    I'm glad to see Paul finally put HTML::Tree on CPAN (HTML::Tree clashes with the namespace of another module, but while CPAN won't find the bundle name 'HTML_Tree' it will find the bundles with an 'HTML::Tree' module including HTML_Tree, BTW, a couple of the later bug fixes are mine). I used this once for a project and liked it, but couldn't sell anyone else in the shop on it.

    And while I'm sure its good for some things, it got more awkward to use as I had to make it fit an existing framework (e.g. you had to have valid HTML, but I was working with broken HTML which looked different when I fixed it and the PHB's didn't like the looks of the valid version). In retrospect, maybe that wasn't the best project to use it on :)

      HTML::Tree is by Sean M. Burke also. You are right, runrig, thanks I will notify the author.
Re: Highly Orthogonal Web Application Development with HTML_Tree
by princepawn (Parson) on Nov 13, 2001 at 15:16 UTC
    this package is no longer on CPAN.

    instead see it's new replacement, HTML::Seamstress

Re: Highly Orthogonal Web Application Development with HTML_Tree
by hiseldl (Priest) on Mar 23, 2003 at 04:50 UTC

    Excellent tutorial on HTML_Tree a.k.a. HTML::Seamstress.

    Just a quick note - the code under "Dynamic Table Rows and Data via Reformed Lists" has some misinterpreted chars in the 'new' sub e.g. "show_no_rows => \&amp;amp;amp;amp;show_no_rows,"


    What time is it? It's Camel Time!

Re: Highly Orthogonal Web Application Development with HTML_Tree
by Anonymous Monk on Dec 12, 2007 at 14:51 UTC

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perltutorial [id://107656]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (6)
As of 2014-04-20 03:38 GMT
Find Nodes?
    Voting Booth?

    April first is:

    Results (485 votes), past polls