NAME

Text::MagicTemplate - magic merger of runtime values with template

WARNING!

Starting with version 2.0, a few critical changes (that could break your old code based on versions < 2.0) have been introduced. You can maintain your old code whether adapting it to the new style (very easy job to do), or using the old 1.31 version. See Warning file in this distribution for details.

SYNOPSIS

the template

The template file 'my_template_file'... (this example uses plain text for clarity, but MagicTemplate works with any type of text file)

A scalar variable: {a_scalar}.
A reference to a scalar variable: {a_ref_to_scalar}.
A subroutine: {a_sub}
A reference to subroutine: {a_ref_to_sub}
A reference to reference: {a_ref_to_ref}
A hash: {a_hash}this block contains a {a_scalar} and a {a_sub}{/a_hash}

A loop:{an_array_of_hashes}
Iteration #{ID}: {guy} is a {job}{/an_array_of_hashes}

An included file:
{'my_included_file'}

... and another template file 'my_included_file' that will be included...

this is the included file 'my_included_file'
that contains a label: {a_scalar}
the code

... some variables and subroutines already defined somewhere in your code...

$a_scalar           = 'THIS IS A SCALAR VALUE';
$a_ref_to_scalar    = \$a_scalar;
@an_array_of_hashes = ( { ID => 1, guy => 'JOHN SMITH',  job => 'PROGRAMMER' },
                        { ID => 2, guy => 'TED BLACK',   job => 'WEBMASTER' },
                        { ID => 3, guy => 'DAVID BYRNE', job => 'MUSICIAN' }  );
%a_hash             = ( a_scalar => 'NEW SCALAR VALUE'
                        a_sub    => sub { 'NEW SUB RESULT' } );

sub a_sub         { 'THIS SUB RETURNS A SCALAR' }
sub a_ref_to_sub  { \&a_sub }
sub a_ref_to_ref  { $a_ref_to_scalar }

Just add these 3 magic lines...

use Text::MagicTemplate;
$mt = new Text::MagicTemplate;
$mt->print( 'my_template_file' );
the output

(in this example Lower case are from templates and Upper case are from code):

A scalar variable: THIS IS A SCALAR VALUE.
A reference to a scalar variable: THIS IS A SCALAR VALUE.
A subroutine: THIS SUB RETURNS A SCALAR
A reference to subroutine: THIS SUB RETURNS A SCALAR
A reference to reference: THIS IS A SCALAR VALUE
A hash: this block contains a NEW SCALAR VALUE and a NEW SUB RESULT

A loop:
Iteration #1: JOHN SMITH is a PROGRAMMER
Iteration #2: TED BLACK is a WEBMASTER
Iteration #3: DAVID BYRNE is a MUSICIAN

An included file:
this is the included file 'my_included_file'
that contains a label: THIS IS A SCALAR VALUE.

DESCRIPTION

Text::MagicTemplate is a "magic" interface between programming and design. It makes "magically" available all the runtime values - stored in your variables or returned by your subroutines - inside a static template file. There's usually no need to assign values to the object. Template outputs are linked to runtime values by their identifiers, which are added to the template in the form of simple labels or blocks of content.

a label: {identifier}
a block: {identifier} content of the block {/identifier}

From the designer point of view, this makes things very simple. The designer has just to decide what value and where to put it. Nothing else is required, no complicated new syntax to learn!

On the other side, the programmer has just to define variables and subroutines as usual and their values will appear in the right place within the output. The automatic interface allows the programmer to focus just on the code, saving him the hassle of interfacing code with output, and even complicated output - with complex switch branching and nested loops - can be easily organized by minding just a few simple concepts (see "How it works").

Structure

This is the general three of the Text::MagicTemplate system (just in case you get lost :-) )

Bundle
    Text
         MagicTemplate  a bundle to install everything in one step
Text
    MagicTemplate       the main module (start from here)
        Tutorial        the tutorial (very useful)
        Utilities       used internally (don't worry about it)
    MagicTemplateX      extensions namespace (documentation for power users)
        Core            core extensions collection
        HTML            HTML extensions collection
HTML
    MagicTemplate   wrapper for Text::MagicTemplate in HTML environment

Installation

Prerequisites
Perl version >= 5.005
CPAN

If you want to install Text::MagicTemplate plus all related extensions (Text::MagicTemplateX::HTML and prerequisites), all in one easy step:

perl -MCPAN -e 'install Bundle::Text::MagicTemplate'
Standard installation

From the directory where this file is located, type:

perl Makefile.PL
make
make test
make install

Note: this installs just the main distribution and does not install Text::MagicTemplateX::HTML and its prerequisites.

Manual installation

If your OS does not have any 'make' support, just copy the content of the /lib dir into perl installation site dir, maintaining the same hierarchy.

Policy

The main principle of Text::MagicTemplate is: keep the designing separated from the coding, giving all the power to the programmer and letting designer do only design. In other words: while the code includes ALL the active and dynamic directions to generate the output, the template is a mere passive and static file, containing just placeholder (zones) that the code will replace with real data.

This philosophy keep both jobs very tidy and simple to do, avoiding confusion and enforcing clearness, specially when programmer and designer are 2 different people. But another aspect of the philosophy of Text::MagicTemplate is flexibility, something that gives you the possibility to easily bypass the rules.

Even if I don't encourage breaking the main principle (keep the designing separated from the coding), sometimes you might find useful to put inside a template some degree of perl code, or may be you want just to interact DIRECTLY with the content of the template. See Using subroutines to rewrite links and Embed perl into a template for details.

Other important principles of Text::MagicTemplate are scalability and expandibility. The whole extension system is built on these principles, giving you the possibility of control the behaviour of this module by omitting, changing the orders and/or adding your own behaviours, without the need of subclassing the module. See new() method and eventually Text::MagicTemplateX.

Features

Since syntax and coding related to this module are very simple and mostly automatic, you should careful read this section to have the right idea about its features and power. This is a list - with no particular order - of the most useful features and advantages:

  • Simple, flexible and powerful to use

    In most cases, you will have just to use new() and print(template) methods, without having to pass any other value to the object: it will do the right job for you. However you can fine tune the behaviour as you need.

  • Extremely simple and configurable template syntax

    The template syntax is so simple and code-independent that even the less skilled webmaster will manage it without bothering you :-). By default Text::MagicTemplate recognizes labels in the form of simple identifiers surrounded by braces ({my_identifier}), but you can easily use different markers (see Redefine Markers).

  • Automatic or manual lookup of values

    By default, Text::MagicTemplate compares any label identifier defined in your template with any variable or subroutine identifier defined in the caller namespace. However, you can explicitly define the lookup otherwise, by passing a list of package namespaces, hash references and/or blessed objects to the -lookups constructor array.

  • Unlimited nested included templates

    Sometimes it can be useful to split a template into differents files. No nesting limit when including files into files. (see Include a file)

  • Branching

    You can easily create simple or complex if-elsif-else conditions to print just the blocks linked with the true conditions (see Setup an if-else condition and Setup a switch condition)

  • Unlimited nested loops

    When you need complex outputs you can build any immaginable nested loop, even mixed with control switches and included templates (see Build a loop and Build a nested loop)

  • Scalable and expandable extensions system

    You can load only the behaviour you need, to gain speed, or you can add as many behaviour you will use, to gain features. You can even write your own behaviour extension in just 2 or 3 lines of code, expanding its capability for your own purpose. (see new() method and eventually Text::MagicTemplateX)

  • Perl embedding

    Even if I don't encourage this approach, however you can very easily embed any quantity of perl code into any template. (See Embed perl into a template)

  • Block management

    When you need complex management of templates files, you have a couple of static methods to extract, mix and set blocks inside any template. (see get_block() and set_block() methods)

  • Placeholders and simulated areas

    Placeholders and simulated areas can help in designing the template for a more consistent preview of the final output. (see Setup placeholders and Setup simulated areas)

  • Simple to maintain

    Change your code and Text::MagicTemplate will change its behaviour accordingly. In most cases you will not have to reconfigure, either the object, or the template.

  • Very lightweight

    MagicTemplate.pm doesn't use any other module and its code is just about 90 lines (easier to write that this documentation :-) )

See also Text::MagicTemplate::Tutorial for more details.

How it works

(Please, read the "SYNTAX GLOSSARY" section for definitions.)

short explanation

  1. The object parse the template and search for any labeled zone

  2. When a zone is found, the object looks into your code and search for any variable or sub with the same identifier (name)

  3. When a match is found the object replace the label or the block with the value returned by the variable or sub found into your code (dereferencing and/or executing code as needed)

medium explanation

The MagicTemplate ouput generation has 3 basic aspects internally handled by the module: template parsing, code lookup and behaviour:

1 The object parse the template to find zones

Each zone defines the area that will be replaced with the result of the behaviour, and defines 3 parameter used by the object: an identifier and otionals attributes and content.

According to the default syntax, this is a minimum zone with no attributes and no content (a label):

{my_identifier}

This is a more complex and complete zone with attributes and content (a block delimited by a label and an end label):

{my_identifier attribute1 attribute2 attributeX} content of block {/identifier}

where 'my_identifier' is the IDENTIFIER, 'attribute1 attribute2 attributeX' are the optional ATTRIBUTES and ' content of block ' is the optional CONTENT.

See the -markers constructor array for details.

2 When a zone identifier is found, the object looks into your code to find a match

To do this, it uses the elements contained in the -lookups constructor array, that can be package namespaces, blessed objects, or references to hash.

If the element is a package namespace or a blessed objects, it compares the zone identifier with each variable or subroutine identifier defined in the package; if the element is a hash reference, it compares the zone identifier with each key defined in the hash. If no -lookups constructor array is passed to the new() method, the package namespace of the caller will be used by default.

See the -lookups constructor array for details.

3 When a match is found, the object choose and execute the behaviour

To do this, it uses the elements contained in the -behaviours constructor array. Each element of this array is a reference to sub or a behaviour extension name (resulting in one or more reference to sub), that receives several parameters and conditionally returns a result. The object check in turn each element of the array, for a true value: when the first true value is returned by a behaviour, the object replaces the template zone with the returned value and start again the process with the next parsed zone.

If no -behaviours constructor array is passed, then the default behaviours will be applied: SCALAR, REF, CODE, ARRAY and HASH. These behaviours are triggered by the found value type:

  • A SCALAR value type will replace the zone with the scalar value.

  • A REFERENCE value will be dereferenced, and the value returned will be checked again to apply an appropriate behaviour

  • A CODE value type will be executed, and the value returned will be checked again to apply an appropriate behaviour

  • An ARRAY value type will generate a loop, merging each value in the array with the zone content and replacing the zone with the sequence of the outputs.

  • A HASH value type will set that HASH as a temporary lookup for the zone. Text::MagicTemplate first uses that hash to look up the identifiers contained in the block; then, if unsuccessful, it will search into the other elements of the -lookups constructor array.

  • Finally, if no previous behaviour returned any value, the zone will be deleted.

See the -behaviours constructor array, and Text::MagicTemplateX::Core for details.

long explanation

This document plus Text::MagicTemplate::Tutorial plus Text::MagicTemplateX::Core plus Text::MagicTemplateX.

METHODS

new ( [constructor_arrays] )

The new() method accepts one optional reference to a hash that can contain the following optionals constructor arrays: -markers, -lookups, -behaviours.

If you don't pass any parameter to the constructor method, the constructor defaults are usually smart enough to do the right job for you, but if you need complete control over the output generation, you can fine tune them by controlling them explicitly.

Note: all the constructor arrays should be array references, but if you have to pass just one element, you can pass it as a plain element as well:

$mt = new Text::MagicTemplate { -lookups => [\%my_hash],
                                -markers => ['HTML'] };

# same thing less noisy
$mt = new Text::MagicTemplate { -lookups => \%my_hash,
                                -markers => 'HTML' };
-markers

Use this constructor array to define the 3 label markers - START_MARKER, END_MARKER_ID, END_MARKER - you want to use in your template. The -markers constructor array can contain the name of 1 markers extension, or a reference to an array containing the 3 explicit markers.

If you want to use the default markers, just call the new() method without any -markers constructor array:

$mt = new Text::MagicTemplate; # default markers
$mt = new Text::MagicTemplate { -markers => 'DEFAULT' }; # same but explicit extension name
$mt = new Text::MagicTemplate { -markers => [ '{', '/', '}' ] }; # same but 3 explicit default markers

$mt = new Text::MagicTemplate { -markers => 'HTML' }; # HTML markers extension name
$mt = new Text::MagicTemplate { -markers => [qw(<!-- / -->)] }; # same but 3 explicit HTML markers
$mt = new Text::MagicTemplate { -markers => [qw(__ END_ __)] }; # custom explicit markers

Since each element of the -markers array is parsed as a regular expression as: qr/element/, you can extend the markers beyond a static string marker. This markers:

$mt = new Text::MagicTemplate { -markers => [ '\d{3}', '\W', '\d{3}' ] }; # 3 weird explicit markers

will match this blocks labeled 'identifier':

235identifier690 content of block 563-identifier054
123identifier321 content of block 000#identifier865

See "Redefine Markers" in Text::MagicTemplate::Tutorial to have more details.

-lookups

Use this constructor array to explicitly define where to look up the values in your code. This array can contain package names, blessed objects and hash references. If no -lookups construction array is passed, the package namespace of the caller will be used by default.

With packages names the lookup is done with all the IDENTIFIERS (variables and subroutines) defined in the package namespace.

With blessed objects the lookup is done with all the IDENTIFIERS (variables and methods) defined in the class namespace. Note: Use this type of location when you want to call an object method from a template: the method will receive the blessed object as the first parameter and it will work as expected.

With hash references the lookup is done with the KEYS existing in the hash.

If you want to make available all the identifiers of your current package, just call the constructor without any -lookups parameter:

# default lookup in the caller package
$mt = new Text::MagicTemplate ;

# same thing but explicit
$mt = new Text::MagicTemplate { -lookups => __PACKAGE__ };

If you want to keep unavailable some variable or subroutine from the template, you can pass just the reference of some hash containing just the identifiers used in the template. This is the best method to use the module IF you allow untrustworthy people to edit the template AND if you have any potentially dangerous subroutine in your code. (see Allow untrustworthy people to edit the template).

# lookup in %my_hash only
$mt = new Text::MagicTemplate { -lookups => \%my_hash } ;

You can also define an arbitrary list of packages and/or references to hashes as the lookup: the precedence of the lookup will be inherited from the order of the items passed, and the first found mach will return the value.

# lookup in several location
$mt = new Text::MagicTemplate { -lookups => [\%my_hash, 'main', \%my_other_hash] } ;

In this example, the lookup will be done in %my_hash first - if unsuccessful - it will be done in the 'main' package and - if unsuccessful - it will be done in %my_other_hash.

If you use Text::MagicTemplate inside another module, you can pass the blessed object as the location:

use Text::MagicTemplate;
package Local::foo;
sub new
{
    my $s = bless {data=>'THE OBJECT DATA'}, shift;
    $s->{mt} = new Text::MagicTemplate {-lookups => $s};
    $s;
}

sub method_triggered_by_lookup
{
	my $s = shift; # correct object passed
	...
	$s->{data};
}

so that if some zone identifier will trigger 'method_triggered_by_lookup', it will receive the blessed object as the first parameter and it will work as expected.

-behaviours

Use this constructor array to explicitly define or modify the behaviour of the object. This constructor array can contain sub references and/or behaviour extension names (resulting in one or more sub references: see Text::MagicTemplateX for details). The sub referenced in the array, must be conditional behaviours (or just 'behaviours' for short), that simply means: subs that will be executed only if a condition is true. This is the typical structure of a behaviour:

$behaviour = sub { if(condition){do_something} }

The object will use the behaviours in this array to construct a switch case condition similar to this:

$result = &$behaviour1 || &$behaviour2 || &$behaviour3 ....

If you don't pass any -behaviours constructor array, the default behaviours will be used:

$mt = new Text::MagicTemplate;
# means
$mt = new Text::MagicTemplate { -behaviours => 'DEFAULT' };
# that expicitly means
$mt = new Text::MagicTemplate { -behaviours =>  [qw(SCALAR REF CODE ARRAY HASH)] };

Where 'DEFAULT', 'SCALAR', 'REF', 'CODE', 'ARRAY', 'HASH' are behaviour extension names that the object will use to load the named extension file.

You can add, omit or change the order of the element in the array, fine tuning the behaviour of the object.

$my_behaviour = sub{ if(my_condition){do_my_behaviour} }

$mt = new Text::MagicTemplate { -behaviours => [ 'DEFAULT', $my_behaviour ] };
# that explicitly means
$mt = new Text::MagicTemplate { -behaviours =>  [ 'SCALAR', 'REF', 'CODE', 'ARRAY', 'HASH', $my_behaviour] };

# or you can add, omit and change the order of the behaviours
$mt = new Text::MagicTemplate { -behaviours =>  [ 'SCALAR', 'REF', $my_behaviour, 'ARRAY', 'HASH'] };

See Text::MagicTemplateX::Core for details and examples.

output ( template [, identifier] )

This method merges the runtime values with the template and returns a reference to the output. It accepts one template parameter that can be a reference to a SCALAR content, a path to a template file or a filehandle. If any identifier is passed, it returns a reference to the output of just that block.

# template is a path
$output = $mt->output( '/temp/template_file.html' ) ;

# template is a reference
$output = $mt->output( \$tpl_content ) ;

#template is a filehandler
$output = $mt->output( *FILEHANDLER );

# this limits the output to just 'my_block_identifier'
$my_block_output = $mt->output( \$tpl_content, 'my_block_identifier');

This method merges the runtime values with the template and prints the output. It accepts one template parameter that can be a reference to a SCALAR content, a path to a template file or a filehandle. If any identifier is passed, it prints the output of just that block.

# template is a path
$mt->print( '/temp/template_file.html' );

# template is a reference
$mt->print( \$tpl_content ) ;

#template is a filehandler
$mt->print( *FILEHANDLER );

# this limits the output to just 'my_block_identifier'
$mt->print( \$tpl_content, 'my_block_identifier' );

get_block ( template [, identifier] )

This method returns a reference to the template content or to a block inside the template, without merging values. It accepts one template parameter that can be a reference to a SCALAR content, a path to a template file or a filehandle. If any identifier is passed, it returns just that block.

# this returns a ref to the whole template content
$tpl_content = $mt->get_block ( '/temp/template_file.html' );

# this return a ref to the 'my_block_identifier' block
$tpl_block = $mt->get_block ( '/temp/template_file.html', 'my_block_identifier' );

# same thing passing a reference
$tpl_block = $mt->get_block ( $tpl_content, 'my_block_identifier' );

set_block ( template, identifier, new_content )

This method sets the content of the block (or blocks) identifier inside a template - without merging values - and returns a reference to the changed template. It accepts one template parameter that can be a reference to a SCALAR content, a path to a template file or a filehandle. New_content can be a reference to the content or the content itself.

# this return a ref to the 'my_block' block
$new_content = $mt->get_block ( '/temp/template_file_2.html', 'my_block' );

# this returns a ref to the changed template content,
$changed_content = $mt->set_block ( '/temp/template_file.html', 'my_old_block', $new_content );

STATIC METHODS

set_ID_output ()

Calling this method will redefine the behaviour of the module, so your program will generate a pretty formatted output of only the identifiers present in the template. Thus the programmer can pass a description of each label and block within a template to a designer:

Text::MagicTemplate->set_ID_output(1);

See also Prepare the identifiers description list.

SYNTAX GLOSSARY

attributes

The attributes are alphanumeric words (\w+) separated by a space that are optionally used to pass special parameter to the behaviour

behaviour

The behaviour generated by Text::MagicTemplate (i.e. with the 'DEFAULT' behaviours, an UNDEF value type triggers the deletion of the zone, an ARRAY value type generates a loop).

block

A block is a template zone delimited by (and including) a label and an end label:

+-------+-------------------+------------+
| LABEL |      CONTENT      | END_LABEL  |
+-------+-------------------+------------+

Example: {my_identifier} content of the block {/my_identifier}

where '{my_identifier}' is the LABEL, ' content of the block ' is the CONTENT and '{/my_identifier}' is the END_LABEL.

end label

An end label is a string in the form of:

+--------------+---------------+------------+------------+
| START_MARKER | END_MARKER_ID | IDENTIFIER | END_MARKER |
+--------------+---------------+------------+------------+

Example of end label : {/my_identifier}

where '{' is the START_MARKER, '/' is the END_MARKER_ID, 'my_identifier' is the IDENTIFIER, and '}' is the END_MARKER.

identifier

A label identifier is a alphanumeric name (\w+) that represents (and usually matches) a variable or a subroutine identifier of your code.

illegal blocks

Each block in the template can contain arbitrary quantities of nested labels and/or blocks, but it cannot contain itself (a block with its same identifier), or cannot be cross-nested.

Legal block: {block1}...{block2}...{/block2}...{/block1}

Illegal auto-nested block: {block1}...{block1}...{/block1}...{/block1}

Illegal cross-nested block: {block1}...{block2}...{/block1}...{/block2}

If the template contains any illegal block, unpredictable behaviours may occur.

include label

An include label is a label used to include a template file. The identifier must be surrounded by single or double quotes and should be a valid path.

Example: {'/templates/temp_file.html'}

label

A label is a string in the form of:

+--------------+------------+------------+------------+
| START_MARKER | IDENTIFIER | ATTRIBUTES | END_MARKER |
+--------------+------------+------------+------------+

Example: {my_identifier attribute1 attribute2}

where '{' is the START_MARKER, 'my_identifier' is the IDENTIFIER, 'attribute1 attribute2' are the ATTRIBUTES and '}' is the END_MARKER.

lookup

The action to match label identifier with code identifier (variable, subroutine and method identifiers and hash keys).

markers

The markers that defines a labels and blocks. These are the default values of the markers that define the label:

START_MARKER:   {
END_MARKER_ID:  /
END_MARKER:     }

You can redefine them by using the -markers constructor array. (see "Redefine Markers" and -markers).

nested block

A nested block is a block contained in another block:

+----------------------+
|   CONTAINER_BLOCK    |
|  +----------------+  |
|  |  NESTED_BLOCK  |  |
|  +----------------+  |
+----------------------+

Example: {my_container_identifier} {my_nested_identifier} content of the block {/my_nested_identifier} {/my_container_identifier}

where all the above is the CONTAINER_BLOCK and '{my_nested_identifier} content of the block {/my_nested_identifier}' is the NESTED_BLOCK.

output

The output is the result of the merger of runtimes data with a template

template

A template is a text content or a text file (i.e. plain, HTML, XML, etc.) containing some label or block.

value type

The type of the value found by a lookup (i.e. UNDEF, SCALAR, HASH, ARRAY, ...), that is usually used in the behaviour condition to trigger the behaviour.

zone

A zone is an area in the template that must have an identifier, may have some attributes and may have a content. A zone without any content is also called label, while a zone with content is also called block.

SEE ALSO

Text::MagicTemplate::Tutorial, Text::MagicTemplateX, Text::MagicTemplate::Core, Text::MagicTemplateX::HTML.

SUPPORT and FEEDBACK

I would like to have just a line of feedback from everybody who tries or actually uses this software. Feel free to write me any comment, suggestion or request.

AUTHOR

Domizio Demichelis, <dd@4pro.net>.

COPYRIGHT

Copyright (c)2002 Domizio Demichelis. All Rights Reserved. This is free software; it may be used freely and redistributed for free providing this copyright header remains part of the software. You may not charge for the redistribution of this software. Selling this code without Domizio Demichelis' written permission is expressly forbidden.

This software may not be modified without first notifying the author (this is to enable me to track modifications). In all cases the copyright header should remain fully intact in all modifications.

This code is provided on an "As Is'' basis, without warranty, expressed or implied. The author disclaims all warranties with regard to this software, including all implied warranties of merchantability and fitness, in no event shall the author, be liable for any special, indirect or consequential damages or any damages whatsoever including but not limited to loss of use, data or profits. By using this software you agree to indemnify the author from any liability that might arise from it is use. Should this code prove defective, you assume the cost of any and all necessary repairs, servicing, correction and any other costs arising directly or indrectly from it is use.

The copyright notice must remain fully intact at all times. Use of this software or its output, constitutes acceptance of these terms.