Text::Boilerplate


NAME

Text::Boilerplate - format a script's output without programming


SYNOPSIS

    use Text::Boilerplate;

    # Create a Boilerplate from a text string
    $boiler = new Text::Boilerplate $text;

    # Create a Boilerplate by loading a file
    $boiler = Text::Boilerplate->load($filename);

    # Create a Boilerplate by loading from a filehandle
    $boiler = Boilerplate->load(\*FILEHANDLE);

    # Fill in all tags in a Boilerplate
    print STDOUT $boiler->fill({ 
        'Tag Name'  => $value, 
        'Tag Name2' => $value,
        'Tag Name3' => [
            { Subtag1 => $subval, Subtag2 => $subval2 },
            { Subtag1 => $subval3, Subtag2 => $subval4 }
        ],
    });

    # Get the Perl code to produce the Boilerplate
    $boiler->code();

    # Get information on the tags in the Boilerplate
    $boiler->tag_info();


DESCRIPTION

Boilerplates let you separate what a script does from what its output looks like, using a simple mark-up language which is easy for non-programmers to learn. Using Boilerplates can make creating and maintaining dynamic web pages and e-mail messages much easier.


Using the Boilerplate Mark-Up System

The Boilerplate mark-up system is designed to allow the look and feel for script-generated text (i.e. dynamic HTML pages, form letters, etc.) to be designed by someone who doesn't know how to program.

Say, for example, that you need to develop a CGI script for a local bookstore that lists their top three best-selling books, plus their customer of the week. If, like me, you have no real aptitude for HTML page design, you'd probably have someone else design the actual page, which, let's say, should look like this:

    <HTML>
    
    <HEAD>
        <TITLE>Boring Bookshop</TITLE>
    </HEAD>
    
    <BODY>
        <H1>Boring Books</H1>

        <P>
            <B>Customer Of The Week:</B> Bob Smeed
        </P>

        <P>
            <B>Top Three Best-Selling Books</B>

            1. <I>Book Title #1</I> by Author<BR>
            2. <I>Book Title #2</I> by Author #2<BR>
            3. <I>Book Title #3</I> by Author #3<BR>
        </P>
    </BODY>

    </HTML>
    

Well, you could just hard-code the HTML into your script with print statements. Sooner or later, though, Boring Books is going to want changes made to the HTML. If this were an ordinary web page, they could simply ask a page designer to make whatever fixes they want (e.g. putting their logo on top, putting the top-selling books into an HTML table, changing the background, etc.) without bothering you, the programmer. In this case, though, the HTML is buried inside a script, and even if the graphics person is willing to make changes to your code, there's a strong possibility that they might introduce errors into your script whenever they edit it.

That's where the Boilerplate mark-up system comes in. Instead of sticking your HTML in the script, you could create a separate text file, consisting of:

    
    <HTML>
    
    <HEAD>
        <TITLE>Boring Bookshop</TITLE>
    </HEAD>
    
    <BODY>
        <H1>Boring Books</H1>

        <P>
            <B>Customer Of The Week:</B> [* "Customer" *]
        </P>

        <P>
            <B>Top Three Best-Selling Books</B>
            [* REPEAT NAME="Top Books" *]
                [* "Rank" *]. <I>[* "Title" *]</I> 
                by [* "Author" *]<BR>
            [* /REPEAT *]
        </P>
    </BODY>

    </HTML>

Then, in your script, add a few lines to load this file from disk and fill it in with book titles and so forth. Assuming you named the above file ``boringbooks.html'', the code would look something like this:

    use Text::Boilerplate;

    # Do whatever is necessary to figure out the top-selling
    # books and customer of the week, print the HTML header,
    # etc.

    # Load the Boilerplate from its file
    $boiler = Text::Boilerplate->load('./boringbooks.html');

    # Print out the Boilerplate, filling in books
    # and customer
    print STDOUT $boiler->fill({
        'Customer'  => $customer,
        'Top Books' => \@books_info,      # @books_info:
                                          # A list of hashes a la
   });                                    # { 'Rank' => $rank, 
                                          #   'Title' => $title,
                                          #   'Author => $author }


Efficiency

The Boilerplate system is designed to fill in values extremely quickly, at the expense of some additional time taken during initialization. Therefore, to get the most out of it, try to use as few 'load's or 'new's as possible, and as many 'fill's as you want. In CGI scripts, a good way of doing this is to use FastCGI, like so:

    use CGI::Fast;
    use Text::Boilerplate;

    # Load the boilerplate for our dynamic page
    $boiler = Boilerplate->new(<<EOF);
    <HTML>
    <HEAD><TITLE>Greetings</TITLE></HEAD>
    <BODY>Hello, [* VALUE NAME="User Name" *]</BODY>
    </HTML>
    EOF

    # Start accepting web hits
    while ($form = new CGI::Fast) {
        
        # Print 'em out a personalized page, assuming that
        # they entered 'Name' on a previous form
        print $form->header();
        print $boiler->fill({'User Name' => $form->param('Name')});
    }


METHODS

Text::Boilerplate encapsulates a number of different modules allowing you to create, learn about, and fill in boilerplate text.


Static Methods

new()
Arguments: $text

Creates a new Text::Boilerplate from a scalar. For example:

    $form_ltr = new Text::Boilerplate("Dear [* 'Name' *],");

This would create a new Text::Boilerplate with a VALUE fill-in tag named 'Name'.

load()
Arguments: $filename

Loads in a Text::Boilerplate from a text file or filehandle. For example:

    $boiler = Text::Boilerplate->load('page1.boil');

Load() croaks if it can't open the file. I don't know if this is a bug or a feature.

If you already have a filehandle open containing boilerplate text, you can pass load() a filehandle reference, like this:

   $boiler = Text::Boilerplate->load(\*DATA);

This could be helpful if you want to store Text::Boilerplate data at the end of your script. (Although this *would* defeat the purpose a bit...)

fill()
Fills in all of the tags in a Boilerplate with the data provided to it, formatted appropriately, and returns the text of the filled-in Boilerplate. Example:

    $boiler = Text::Boilerplate->new(<<EOF);
    In Xanadu did [* VALUE NAME="Person" *]
    A stately [* VALUE NAME="Item" *] decree...
    EOF

    $filled_boiler = $boiler->fill({
        'Person' => 'Kubla Khan',
        'Item'   => 'Pleasure-Dome',
    });

$filled_boiler is now:

    In Xanadu did Kubla Khan
    A stately Pleasure-Dome decree...

See BOILERPLATE TAGS for a full discussion of Boilerplate tag syntax.

code()
Behind the scenes, a Boilerplate is just a Perl subroutine compiled from the text of the Boilerplate document. If for some strange reason you want to see the Perl code for this subroutine, use the code() method. Keep in mind that this code is not especially human-readable.

tag_info()
Returns a list of hashes containing the name, type, and attributes of all of the tags in the Boilerplate, like so:

    ( { 'Name'     => $name, 
        'Type'     => $type, 
        'Contents' => $contents }, ...)

For nested tags (such as REPEAT), the 'Contents' key will contain a reference to a list of all of the tags contained between the tag (i.e. [* REPEAT NAME="fish" *]) and its terminator (i.e. [* /REPEAT *]). Other tags will hold a reference to an empty list in the 'Contents' value. =back

BOILERPLATE TAGS

All of this is useless without tags to fill in. Boilerplate tags are always sandwiched between '[* ' and ' *]'. They generally consist of a tag type, followed by a list of attributes, e.g.:

    [* TAG NAME="name of tag" ATTR2="Another" ATTR3 *]

Tag types and attribute names are case-insensitive; tag values are not. Therefore, THIS=``that'' means the same thing as this=``that'', but not the same as THIS=``THAT''.

There are three ways to assign a value to an attribute: using a double quote, using a single quote, and using no quotes. These are all the same:

    [* TAG NAME="thing" *]
    [* TAG NAME='thing' *]
    [* TAG NAME=thing *]

You can't use spaces in an attribute value without putting quotes around the value.

General Tags

Tags of general utility.

VALUE
Attributes: NAME

Description: The VALUE tag is the simplest and most commonly-used of all Boilerplate tags. It just fills in whatever data provided by the script. Since it is so frequently used, there's a shortcut for it: just use the NAME attribute's value in quotes, and the Boilerplate module will interpret it as though it were a VALUE tag.

(N.B.: I have since come to dislike using VALUE as a tag name, since it's so frequently an attribute. Be prepared for changes.)

Examples:

    [* VALUE NAME="User Name" *] has [* VALUE NAME="time" *]
    hours left on the system.

    The quick [* "Color" *] [* "Animal" *] jumped over the
    lazy [* "Other Animal" *]

REPEAT.../REPEAT
Attributes: NAME

Description: The REPEAT tag fills in as many copies of whatever is between it and its closing tag as are required by the data supplied by the script.

If a SEPARATOR block appears within the REPEAT block, the contents of SEPARATOR are filled in between every repetition of the REPEAT block. Note that SEPARATOR cannot access data given to the REPEAT block by the script.

Examples:

    <B>The users on this system are:</B>
    <OL>
    [* REPEAT NAME="User List" *]
        <LI>[* "Username" *]
    [* /REPEAT *]
    </OL>

    <B>Your menu item choices:</B>
    [* REPEAT NAME="Menu List" *]
    <P>
        [* "Menu Item" *]
    </P>
    [* SEPARATOR *]
    <HR>
    [* /SEPARATOR *]
    [* /REPEAT *]

SEPARATOR.../SEPARATOR
Separates the repeating items in a REPEAT block. See REPEAT for details. SEPARATOR's tags are filled in from the same set of values as the block surrounding the repeat. (Don't worry about this overmuch.)

ENV
Attributes: NAME

Returns the environment variable (or equivalent) for which it's named. Probably not terribly useful on machines which don't support such things. A frequent use of ENV is to create a form that calls its creating script, like so:

    <FORM METHOD="post" ACTION="[* ENV NAME='SCRIPT_NAME' *]">

Since, in the CGI protocol, SCRIPT_NAME always contains the relative URL of the current script, the proper URL to call the script would be filled into the ACTION tag.

The ENV tag opens up potential security holes if you keep sensitive data hanging around in environment variables.

IF.../IF
Attributes: NAME

If the value for which IF is named is true, it fills that in. If that value is false, the section remains blank. If an ELSE block appears within the IF block, the ELSE block is filled in. For example:

    [* IF NAME="Items Found" *]
        [* "Items Found" *] items were found.
        [* ELSE *]
            No items were found.
        [* /ELSE *]
    [* /IF *]

ELSE.../ELSE
Inside an IF.../IF block, defines what to print if the IF value is false. See IF.

Web Tags

While you're welcome to use the following tags for any purpose, they're probably most useful in generating HTML pages on-the-fly.

INPUT
Attributes: VALUE any attribute

The INPUT tag is used for generating a form input HTML tag with a dynamic VALUE. For example:

    <FORM METHOD="post" ACTION="/cgi-bin/other_script.pl">

    <P>
        User Name: [* INPUT NAME="User Name" *]
    </P>

    <INPUT TYPE="submit">

    </FORM>

If the script decided that 'User Name' should be 'Christabel', for example, the filled-in boilerplate would be:

    <FORM METHOD="post" ACTION="/cgi-bin/other_script.pl">

    <P>
        User Name: <INPUT NAME="User Name" VALUE="Christabel">
    </P>

    <INPUT TYPE="submit">

    </FORM>
    
If you wish for the input field to have a default value, you can
specify it using the VALUE attribute, like so:

    [* INPUT NAME="User Name" VALUE="Person From Porlock" *]

That way, if the script doesn't have a value for 'User Name', it will default to 'Person From Porlock'.

Any other attributes given to INPUT will be copied verbatim into the filled-in tag, e.g.:

    [* INPUT NAME="User Name" SIZE="20" MAXSIZE="25" *]

will become something like:

    <INPUT NAME="User Name" VALUE="Christabel" SIZE="20" MAXSIZE="25">

Note that I only recommend using INPUT for simple fill-in fields, like text and number input types. See RADIO and CHECKBOX for more complex form inputs.

RADIO
Attributes: NAME VALUE

Used to generate HTML <INPUT TYPE=``radio''> tags. The HTML tag is marked CHECKED if the value supplied by the script for this tag is equal to the tag's VALUE attribute.

CHECKBOX
Attributes: NAME VALUE

Used to generate HTML <INPUT TYPE=``checkbox''> tags. The HTML tag is marked CHECKED if one of the values supplied by the script for this tag is equal to the tag's VALUE attribute.

Programmer's note: takes an array reference as argument.

SELECT.../SELECT
ATTRIBUTES: NAME any attribute

The SELECT tag is used to generate option lists with script-controllable selections. The options are specified in the document with HTML <OPTION> tags, just as in ordinary HTML. Values in the SELECT tag are passed on into the generated HTML. For example:

    [* SELECT NAME="Poet" SIZE="4" *]
        <OPTION>Coleridge
        <OPTION>Browning
        <OPTION>Wordsworth
    [* /SELECT *]

If the script chose a value of ``Browning'' for ``Poet'', the script would return:

    <SELECT NAME="Poet" SIZE="4">
        <OPTION VALUE="Coleridge">Coleridge
        <OPTION VALUE="Browning" SELECTED>Browning
        <OPTION VALUE="Wordsworth">Wordsworth
    </SELECT>

Programmer's note: this tag accepts an array ref, permitting multiple selections that way.

MENU
Attributes: NAME any

Used to create an HTML <SELECT> menu on-the-fly for a list of options. While the Boilerplate SELECT tag chooses from a list of options already present in the Boilerplate, MENU generates the appropriate tag from data supplied by the script.

Programmer's note: this tag accepts a list of hashes, a la:

    [ { 'Option' => $opt, 'Value' => $val, 'Selected' => $sel } ... ]


SEE ALSO

perl, Text, and Text.


COPYRIGHT

Copyright (c) 1997 Steve Nelson. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


AUTHOR

senelson@negentropy.com (Steve Nelson)