NAME

HTML::Seamstress - dynamic HTML generation via pure HTML and pure Perl.

SYNOPSIS

  # HTML
  <html>

  <table supply="$s->_aref($s->load_data)">

    <tr>  <th>name<th>age<th>weight</th> </tr>

    <tr iterator="$s->{supply}->Next">

        <td worker="$s->_text($s->{iterator}->{name})">    </td>
        <td worker="$s->_text($s->{iterator}->{age})">     </td>
        <td worker="$s->_text($s->{iterator}->{weight})">  </td>

   </tr>

  </table>

 </html>

  # Perl
  use HTML::Seamstress;
  HTML::Seamstress->weave(html => 'simple.html', with => 'simple.pm');

  # simple.pm
 package simple;

 use base qw(HTML::Seamstress::Iterator 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

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 for HTML elements.

In model-view-controller terms, the HTML is the view. Seamstress is the controller, making calls to 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 more importantly, unit-testable outside of Perl.

Seamstress knows what Perl methods to call and when by the looking up specific attributes within a tag that are special to it. The attributes are supply, iterator, and worker tags.

  • A worker attribute 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 the HTML::Element to some text. The others are listed in the manpage for HTML::Stitchery.

  • A supply attribute is called for side-effect. It creates a store of data for use by iterator and worker tags. Note that this creation may be actual or via a Perl tie. HTML::Seamstress automatically stores the results of supply attribute evaluation in the page object under $s-{supply}> for later reference.

  • An iterator attribute is used to pull records from a supply store previously created. HTML::Seamstress automatically stores the results of iterator 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 in HTML::Seamstress.

  • CGI::Seamstress is a derived class of HTML::Seamstress, HTML::Stitchery and CGI.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 of HTML::Seamstress, HTML::Stitchery, CGI.pm, and Apache::Request. And it isn't written yet either. Sigh.

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 ITERATOR="$s->{supply}->next_flavor">
    <OPTION WORKER="$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, 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;

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 the HTML::Stitchery constructor if you want and if you have an HTML file that you don't want HTML::Seamstress dhtml in, then you don't have to have a dummy .pm file, simply omit the using 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 of 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)

2 POD Errors

The following errors were encountered while parsing the POD:

Around line 537:

You forgot a '=back' before '=head1'

Around line 714:

You forgot a '=back' before '=head1'