NAME
Text::MetaText - Perl extension implementing meta-language for
processing "template" text files.
SYNOPSIS
use Text::MetaText;
my $mt = new Text::MetaText;
print $mt->process($filename, \%vardefs);
SUMMARY OF METATEXT DIRECTIVES
%% DEFINE variable=value %% # define variable(s)
%% SUBST variable %% # insert variable value
%% variable %% # short form of above
%% BLOCK blockname %% # define a block 'blockname'
block text...
%% ENDBLOCK %%
%% INCLUDE blockname %% # include 'blockname' block text
%% INCLUDE filename %% # include external file 'filename'
%% INCLUDE file_or_block # a more complete example...
variable=value # additional variable definition(s)
if=condition # conditional inclusion
format=format_string # printf-like format string with '%s'
filter=fltname(params) # post-process filter
%%
%% TIME # current system time, as per time(2)
format=format_string # display format, as per strftime(3C)
%%
DESCRIPTION
MetaText is a text processing and markup meta-language which can
be used for processing "template" files. This module is a Perl 5
extension implementing a MetaText object class which processes
text files, interpreting and acting on the embedded MetaText
directives within.
Like a glorified pre-processor, MetaText can; include files,
define and substitute variable values, execute conditional
actions based on variables, call other perl functions or object
methods and capture the resulting output back into the document,
and more. It can format the resulting output of any of these
operations in a number of ways. The objects, and inherently, the
format and symantics of the MetaText langauge itself, are highly
configurable.
MetaText was originally designed to aid in the creation of html
documents in a large web site. It remains well suited for this
and similar tasks, being able to create web pages (dynamically
or statically) that are consistent with each other, yet easily
customisable:
* standard headers, footers and other elements can be defined in
separate files and then inserted into web documents:
%% INCLUDE header %%
* variables can be defined externally or from within a document,
then can be substituted back into the text. This is useful
for including your %% name %% or %% email %% address or any
other variable, and for encoding URL's or file paths that
can then be changed en masse. e.g.
* conditional actions can be made based on variable definitions,
allowing easily and instantly customisable web pages. e.g
%% INCLUDE higraphics/header if="higfx && userid != abw" %%
* blocks of text can be internally defined simplifying the
creation of repetitive elements. e.g.
%% BLOCK table_row %%
%% userid %% | %% name %% |
%% ENDBLOCK %%
%% INCLUDE table_row userid=lwall name="Larry Wall" %%
%% INCLUDE table_row userid=tomc name="Tom Christiansen" %%
%% INCLUDE table_row userid=merlyn name="Randal L. Schwartz" %%
* in addition, the metapage utility is a script which can
automatically traverse document trees, processing updated
files to assist in web document management and other similar
tasks.
PREREQUISITES
MetaText requires Perl 5.004 or later. The Date::Format module
should also be installed. This is available from CPAN (in the
"TimeDate" distribution) as described in the following section.
OBTAINING AND INSTALLING THE METATEXT MODULE
The MetaText module is available from CPAN. As the 'perlmod' man
page explains:
CPAN stands for the Comprehensive Perl Archive Network.
This is a globally replicated collection of all known Perl
materials, including hundreds of unbunded modules.
[...]
For an up-to-date listing of CPAN sites, see
http://www.perl.com/perl/ or ftp://ftp.perl.com/perl/ .
Within the CPAN archive, MetaText is in the "Text::" group which
forms part of the the category:
*) String Processing, Language Text Processing,
Parsing and Searching
The module is available in the following directories:
/modules/by-module/Text/Text-MetaText-.tar.gz
/authors/id/ABW/Text-MetaText-.tar.gz
For the latest information on MetaText or to download the latest
pre-release/beta version of the module, consult the definitive
reference, the MetaText Home Page:
http://www.kfs.org/~abw/perl/metatext/
MetaText is distributed as a single gzipped tar archive file:
Text-MetaText-.tar.gz
Note that "" represents the current MetaText Revision
number, of the form "0.14". See the REVISION manpage below to
determine the current version number for Text::MetaText.
Unpack the archive to create a MetaText installation directory:
gunzip Text-MetaText-.tar.gz
tar xvf Text-MetaText-.tar
'cd' into that directory, make, test and install the MetaText
module:
cd Text-MetaText-
perl Makefile.PL
make
make test
make install
The 't' sub-directory contains a number of small sample files
which are processed by the test script (called by 'make test').
See the README file in that directory for more information. A
logfile (test.log) is generated to report any errors that occur
during this process. Please note that the test suite is
incomplete and very much in an 'alpha' state. Any further
contributions here are welcome.
The 'make install' will install the module on your system. You
may need root access to perform this task. If you install the
module in a local directory (for example, by executing "perl
Makefile.PL LIB=~/lib" in the above - see `perldoc MakeMaker'
for full details), you will need to ensure that the PERL5LIB
environment variable is set to include the location, or add a
line to your scripts explicitly naming the library location:
use lib '/local/path/to/lib';
The metapage utility is a script designed to automate MetaText
processing of files. It can traverse directory trees, identify
modified files (by comparing the time stamp of the equivalent
file in both "source" and "destination" directories), process
them and direct the resulting output to the appropriate file
location in the destination tree. One can think of metapage as
the MetaText equivalent of the Unix make(1S) utility.
The installation process detailed above should install metapage
in your system's perl 'installbin' directory (try `perl '-
V:installbin'' to check this location). See the metapage
documentation (`perldoc metapage') for more information on
configuring and using metapage.
USING THE METATEXT MODULE
To import and use the MetaText module the following line should
appear in your Perl script:
use Text::MetaText;
MetaText is implemented using object-oriented methods. A new
MetaText object is created and initialised using the
Text::MetaText->new() method. This returns a reference to a new
MetaText object.
my $mt = Text::MetaText->new;
A number of configuration options can be specified when creating
a MetaText object. A reference to a hash array of options and
their associated values should be passed as a parameter to the
new() method.
$my $mt = Text::MetaText->new( { 'opt1' => 'val1', 'opt2' => 'val2' } );
The configurations options available are described in full
below. All keywords are treated case-insensitively (i.e. "LIB",
"lib" and "Lib" are all considered equal).
LIB The INCLUDE directive causes the external file specified
("INCLUDE ") to be imported into the current document.
The LIB option specifies one or more directories in which
the file can be found. Multiple directories should be
separated by a colon or comma. The current directory is also
searched by default.
my $mt = Text::MetaText->new( { LIB => "/tmp:/usr/metatext/lib" } );
CASE
The default behaviour for MetaText is to treat variable
names and identifiers case insensitively. Thus, the
following are treated identically:
%% INCLUDE foo %%
%% INCLUDE FOO %%
Setting the CASE option to any non-zero value causes the
document to be processed case sensitively.
my $mt = Text::MetaText->new( { CASE => 1 } ); # case sensitive
Note that the configuration options described in this
section are always treated case insensitively regardless of
the CASE setting.
MAGIC
MetaText directives are identifed in the document being
processed as text blocks surrounded by special "magic"
identifers. The default identifiers are a double percent
string, "%%", for both opening and closing identifiers.
Thus, a typical directive looks like:
%% INCLUDE some/file %%
and may be embedded within other text:
normal text, blah, blah %% INCLUDE some/file %% more normal text
The MAGIC option allows new identifiers to be defined. A
single value assigned to MAGIC defines a token to be used
for both opening and closing identifiers:
my $mt = Text::MetaText->new( { MAGIC => '++' } );
++ INCLUDE file ++
A reference to an array providing two values (elements 0 and
1) indicates separate tokens to be used for opening and
closing identifiers:
my $mt = Text::MetaText->new( { MAGIC => [ '' ] } );
CHOMP
When MetaText processes a file it identifies directives and
replaces them with the result of whatever magical process
the directive represents (e.g. file contents for an INCLUDE,
variable value for a SUBST, etc). Anything outside the
directive, including newline characters, are left intact.
Where a directive is defined that has no corresponding
output (DEFINE, for example, which silently sets a variable
value), the trailing newline characters can leave large
tracts of blank lines in the output documents.
For example:
line 1
%% DEFINE f="foo" %%
%% DEFINE b="bar" %%
line 2
Produces the following output:
line 1
line 2
This happens because the newline characters at the end of
the second and third lines are left intact in the output
text.
Setting CHOMP to any true value will remove any newline
characters that appear immediately after a MetaText
directive. Any characters coming between the directive and
the newline, including whitespace, will override this
behaviour and cause the intervening characters and newline
to be output intact.
With CHOMP set, the following example demonstrates the
behaviour:
line 1
%% DEFINE f="foo" %%
%% DEFINE b="bar" %%
line 2
Produces the following output (Note that "" is
intended to represent a single space character, not the
string "" itself, although the effect would be
identical):
line 1
line 2
FILTER
There may be times when you may want to INCLUDE a file or
element in a document but want to filter the contents in
some way. You may wish to escape (i.e. prefix with a
backslash '\') certain characters such as quotes, search for
certain text and replace with an alternative phrase, or
perform some other post-processing task. The FILTER option
allows you to define one or more code blocks that can be
called as filter functions from an INCLUDE directive. Each
code block is given a unique name to identify it and may
have calling parameters (parenthesised and separated by
commas) that can be specified as part of the directive. e.g.
%% INCLUDE foo filter="slurp(prm1, prm2, ...)" %%
Two default filters are pre-defined: escape() and sr().
escape() takes as a parameter a perl-like regular expression
pattern that indicates characters that should be 'escaped'
(i.e. prefixed by a backslash '\') in the text. For example,
to escape any of the character class `["'\]' you would
specify the filter as:
%% INCLUDE foo filter="escape([\"'\\])" %%
The second filter, sr(), takes two arguments, a search
string and a replace string. A simple substitution is made
on the included text. e.g.
%% INCLUDE foo filter="sr(spam, \"processed meat\")" %%
Note that quotes and other special metacharacters should be
escaped within the filter string as shown in the two
examples above.
Additional filters can be specified by passing a reference
to a hash array that contains the name of the filter and the
code itself in each key/value pair. Your filter function
should be designed to accept the name of the function as the
first parameter, followed by a line of text to be processed.
Any additional parameters specified in the INCLUDE directive
follow. The filter function is called for each line of an
INCLUDE block and should return the modified text.
Example:
my $mt = Text::MetaText->new( {
FILTER => {
'xyzzy' => sub {
my ($filtername, $text, @params) = @_;
$text = # do something here...
$text; # return modified text
}
}
} );
%% INCLUDE file1 filter="xyzzy(...)" %%
A new FILTER definition will replace any existing filter
with the same name.
EXECUTE
The SUBST directive performs a simple substitution for the
value of the named variable. In the example shown below, the
entire directive, including the surrounding 'magic' tokens
'%%', is replaced with the value of the variable 'foo':
%% SUBST foo %% (or more succinctly, %% foo %%)
If the named variable has not been defined, MetaText can
interpret the variable as the name of an object method in
the current class or as a function in the main package.
If the EXECUTE flag is set to any true value, the MetaText
processor will interpret the variable as an object method
and attempt to apply it to its own object instance (i.e.
$self->$method(...)). If the method is not defined, the
processor fails quietly (but see ROGUE below to see what can
happen next). This allows classes to be derived from
MetaText that implement methods that can be called (when
EXECUTE == 1) as follows:
%% method1 ... %% # calls $self->method1(...);
%% method2 ... %% # calls $self->method2(...);
The text returned from the method is used as a replacement
value for the directive.
The following pseudo-code example demonstrates this:
package MyMetaText;
@ISA = qw( Text::MetaText );
sub foo { "This is method 'foo'" } # simple return string
sub bar { "This is method 'bar'" } # " " "
package main;
my $mt = MyMetaText->new( { EXECUTE => 1 } );
print $mt->process("myfile");
which, for the file 'myfile':
%% foo %%
%% bar %%
generates the following output:
This is method 'foo'
This is method 'bar'
If the EXECUTE flag is set to a value > 1 and the variable
name does not correspond to a class method, the processor
tries to interpret the variable as a function in the main
package. Like the example above, the processor fails
silently if the function is not defined (but see ROGUE
below).
The following pseudo-code extract demonstrates this:
my $mt = Text::MetaText->new( { EXECUTE => 2 } );
print $mt->processs("myfile");
sub foo { "This is function 'foo'" } # simple return string
sub bar { "This is fucntion 'bar'" } # " " "
which, for the file 'myfile':
%% foo %%
%% bar %%
generates the following output:
This is function 'foo'
This is function 'bar'
Any additional parameters specified in the directive are
passed to the class method or function as a hash array
reference. The original parameter string is also passed.
Note that the first parameter passed to class methods is the
MetaText (or derivative) object reference itself.
Example:
%% foo name="Seuss" title="Dr" %%
causes the equivalent of (when EXECUTE is any true value):
$self->foo( # implicit $self ref
{ 'name' => 'Seuss', 'title' => 'Dr' }, # hash ref of params
'name="Seuss" title="Dr"' ); # parameter string
and/or (when EXECUTE > 1):
&main::foo(
{ 'name' => 'Seuss', 'title' => 'Dr' }, # hash ref of params
'name="Seuss" title="Dr"' ); # parameter string
ROGUE
This configuration item determines how MetaText behaves when
it encounters a directive it does not recognise. The ROGUE
option may contain one or more of the ROGUE keywords
separated by any non-word character. The keywords and their
associated meanings are:
warn Issue a warning (via the ERROR function, if
specified) when the directive is encountered.
delete Delete any unrecognised directives.
The default behaviour is to silently leave any unrecognised
directive in the processed text.
Example:
my $mt = Text::MetaText->new( { ROGUE => "delete,warn" } );
DELIMITER
The DELIMITER item specifies the character or character
sequence that is used to delimit lists of data. This is
used, for example, by the "in" operator which can be used in
evaluation conditions. e.g.
%% INCLUDE hardenuf if="uid in abw,wrigley" %%
In this case, the condition evaluates true if the uid
variable contains the value "abw" or "wrigley". The default
delimiter character is a comma.
The example:
my $mt = Text::MetaText->new( { DELIMITER => ":" } );
would thus correctly process:
%% INCLUDE hardenuf if="uid in abw:wrigley" %%
ERROR
The ERROR configuration item allows an alternative error
reporting function to be specified for error handling. The
function should expect a printf() like calling convention.
Example:
my $mt = Text::MetaText->new( {
ERROR => sub {
my ($format, @params) = @_;
printf(STDERR "ERROR: $format", @params);
}
} );
DEBUG
The DEBUG item allows an alternative debug function to be
provided. The function should expect a printf() like calling
convention, as per the ERROR option described above. The
default DEBUG function sends debug messages to STDERR,
prefixed by a debug string: 'D> '.
DEBUGLEVEL
The DEBUGLEVEL item specifies which, if any, of the debug
messages are displayed during the operation of the MetaText
object. Like the ROGUE option described above, the
DEBUGLEVEL value should be constructed from one or more of
the following keywords:
none no debugging information (default)
info general processing information
config MetaText object configuration items
preproc pre-processing phase
process processing phase
postproc post-processing phase
data additional data parameters in debug messages
content content of pre-processed INCLUDE blocks
function list functions calls as executed
evaluate trace conditional evaluations
test used for any temporary test code
all all of the above (excluding "none", obviously)
Example:
my $mt = Text::MetaText->new( {
DEBUGLEVEL => "preproc,process,data"
} );
MAXDEPTH
It is possible for MetaText to become stuck in an endless
loop if a circular dependancy exists between one or more
files. For example:
foo:
%% INCLUDE bar %%
bar:
%% INCLUDE foo %%
To detect and avoid such conditions, MetaText allows files
to be nested up to MAXDEPTH times. By default, this value is
32. If you are processing a file which has nested INCLUDE
directives to a depth greater than 32 and MetaText returns
with a "Maximum recursion exceeded" warning, set this
confiuration item to a higher value. e.g.
my $mt = Text::MetaText->new( { MAXDEPTH => 42 } );
PROCESSING TEXT
The MetaText process() method is called to process a file,
interpreting any MetaText directives embedded within it. The
first parameter should be the name of the file which should
reside in the current working directory or in one of the
directories specified in the LIB configuration option. A
filename starting with a slash '/' is considered to be an
absolute path. The optional second parameter may be a reference
to a hash array containing a number of variable/value
definitions that should be pre-defined when processing the file.
print $mt->process("somefile", { name => "Fred" });
If "somefile" contains:
Hello %% name %%
then the output generated would be:
Hello Fred
Pre-defining variables in this way is equivalent to using the
DEFINE directive (described below) at the start of the INCLUDE
file
%% DEFINE name="Fred" %%
Hello %% name %%
The process() function will continue until it reaches the end of
the file or a line containing the pattern "__END__" by itself
("END" enclosed by double underscores, no other characters or
whitespace on the line). Note that the pre-processor (a private
method which is called by process(), so feel free to forget all
about it) *does* scan past any __END__ marker. In practice, that
means you can define blocks *after*, but use them *before*, the
__END__ marker. e.g.
Martin, %% INCLUDE taunt %%
__END__ << processor stops here and ignores
everything following
%% BLOCK taunt %% << but the pre-processor has correctly
you Camper! continued and parsed this block so that
%% ENDBLOCK %% it can be included in the main body
produces the output:
Martin, you Camper!
The process() function returns a string containing the processed
file or block output.
my $output = $mt->process("myfile");
print $output;
METATEXT DIRECTIVES
A MetaText directive is a block of text in a file that is
enclosed by the MAGIC identifiers (by default '%%'). A directive
may span multiple lines and may include blank lines within in.
Whitespace within a directive is generally ignored except where
quoted as part of a specific value.
%% DEFINE
name = Yorick
age = 30
comment = "A fellow of infinite jest"
%%
The first word of the directive indicates the directive type.
Directives may be specified in upper, lower or mixed case,
irrespective of the CASE sensitivity flag (which affects only
variable names). The general convention is to specify the
directive type in UPPER CASE to aid clarity.
The MetaText directives are:
DEFINE
Define the values for one or more variables
SUBST
Substitute the value of a named variable
INCLUDE
Process and include the contents of the named file or block
BLOCK
Define a named block which can be subsequently INCLUDE'd
ENDBLOCK
Marks the end of a BLOCK definition
To improve clarity and reduce excessive, unnecessary and
altogether undesirable verbosity, a directive block that doesn't
start with a recognised MetaText directive is assumed to be a
'SUBST' variable substitution. Thus,
%% SUBST foo %%
can be written more succinctly as
%% foo %%
When MetaText processes directives, it is effectively performing
a "search and replace". The MetaText directive block is replaced
with whatever text is appropriate for the directive specified.
Generally speaking, MetaText does not alter any text content or
formatting outside of directive blocks. The only exception to
this rule is when CHOMP is turned on (see the section on "USING
THE METATEXT MODULE") and newlines immediately following a
directive are subsequently deleted.
DEFINE
The DEFINE directive allows simple variables to be assigned
values. Multiple variables may be defined in a single DEFINE
directive.
%% DEFINE
name = Caliban
quote = "that, when I waked, I cried to dream again."
%%
Variables defined within a file or passed to the process()
function as a hash array remain defined until the file or block
is processed in entirety. Variable values will be inherited by
any nested files or blocks INCLUDE'd into the file. Re-
definitions of existing variables will persist within the file
or block, masking any existing values, until the end of the file
or block when the previous values will be restored.
The following example illustrates this:
foo:
Hello %% name %% # name assumes any predefined value
%% DEFINE name=tom %%
Hello %% name %% # name = 'tom'
%% INCLUDE bar name='dick' %% # name = 'dick' for "INCLUDE bar"
Hello %% name %% # name = 'tom'
bar:
Hello %% name %% # name = 'dick'
%% DEFINE name='harry' %% # name = 'harry'
Hello %% name %%
Processing the file 'foo' as follows:
print $mt->process('foo', { 'name' => 'nobody' });
produces the following output (with explanatory comments added
for clarity):
Hello nobody # value from process() hash
Hello tom # from foo
Hello dick # from bar
Hello harry # re-defined in bar
Hello tom # restored to previous value in foo
SUBST
A SUBST directive performs a simple variable substitution. If
the variable is defined, its value will be inserted in place of
the directive.
Example:
%% DEFINE name=Jake %%
Hello %% SUBST name %%
generates the following output:
Hello Jake
The SUBST keyword can be omitted for brevity. Thus "%% name %%"
is processed identically to "%% SUBST name %%".
If the variable is undefined, the MetaText processor will,
according to the value of the EXECUTE configuration value, try
to execute a class method or a function in the main package with
the same name as the SUBST variable. If EXECUTE is set to any
true value, the processor will try to make a corresponding
method call for the current object (that is, the current
instantiation of the MetaText or derived class). If no such
method exists and EXECUTE is set to any value greater than 1,
the processor will then try to execute a function in the main
package with the same name as the SUBST variable In either case,
the text returned from the method or function is included into
the current block in place of the SUBST directive (non-text
values are automatically coerced to text strings). If neither a
variable, method or function exists, the SUBST directive will
either be deleted or left intact (and additionally, a warning
may be issued), depending on the value of the ROGUE
configuration item.
See EXTENDING METATEXT below for more information on deriving
MetaText classes and using EXECUTE to extend the meta-language.
The "format" and "filter" options as described in the INCLUDE
section below are applied to the processed SUBST result before
being inserted back into the document.
Some MetaText variables have a special meaning. Unless
specifically defined otherwise, the variable(s) listed below
generate the following output:
TIME The current system time in seconds since the epoch,
00:00:00 Jan 1 1970. Use the "format" option to
specify a time/date format.
INCLUDE
The INCLUDE directive instructs MetaText to load and process the
contents of the file or block specified. If the target is a
file, it should reside in the current directory or a directory
specified in the LIB configuration variable. Alternatively, the
target may be a text block specified with BLOCK..ENDBLOCK
directives (see below).
%% INCLUDE chapter1 %%
The target may also be a variable name and should be prefixed
with a '$' to identify it as such. On evaluation, the value of
the named variable will be used as the target:
Example:
%% DEFINE chapter=ch1 %%
%% INCLUDE $chapter %%
is equivalent to:
%% INCLUDE ch1 %%
Additional variables may be defined for substitution within the
file:
%% INCLUDE chapter2 bgcolor=#ffffff title="Chapter 2" %%
The contents of the file "chapter2":
%%title%%
...
would produce the output:
Chapter 2
...
Defining variables in this way is equivalent to using the DEFINE
directive. Variables remain in scope for the lifetime of the
file being processed and then revert to any previously defined
values (or undefined). Any additional files processed via
further INCLUDE directives within the file will also inherit any
defined variable values.
Example:
%% INCLUDE file1 name="World" %%
for the files:
file1: # name => "World" from INCLUDE directive
%% INCLUDE file2 %%
file2: # inherits "name" variable from file1
%% INCLUDE file3 %%
file3: # inherits "name" variable from file2
Hello %% name %%
produces the output:
Hello World
The output generated by INCLUDE and SUBST directives can be
formatted using a printf-like template. The format string should
be specified as a "format" option in the INCLUDE or SUBST
directive. Each line of the included text is formatted and
concatentated to create the final output. Within the format
string, '%s' is used to represent the text.
For example, the 'author' element below could be used to display
details of the author of the current document.
author:
File: %% file %%
Author: %% name %%
Date: %% date %%
For inclusion in an HTML document, the text can be encapsulated
in HTML comment tags ("") using a format string:
%% INCLUDE author
file = index.html
name = "Andy Wardley"
date = 19-Mar-1987
format = ""
%%
Which produces the following output:
Note that the print format is applied to each line of the
included text. To encapsulate the element as a whole, simply
apply the formatting outside of the INCLUDE directive:
In these examples, the formatting is applied as if the
replacement value/line is a character string. Any of the
standard printf(3) format tokens can be used to coerce the value
into a specific type.
As mentioned in the SUBST section above, the TIME variable is
used to represent the current system time in seconds since the
epoch (see time(2)). The "format" option can also be employed to
represent such values in a more user-friendly format. Any format
string that does not contain a '%s' token is assumed to be a
time-based value and is formatted using the time2str() function
from the Date::Format module (distributed as part of the
TimeDate package).
Example:
The date is %% TIME format="%d-%b-%y" %%
Generates:
The date is 19-Mar-98
See `perldoc Date::Format' for information on the formatting
characters available.
The pragmatic token '%P' can be added to a format to override
this behaviour and force the use of printf(). The '%P' token is
otherwise ignored.
Example:
%% DEFINE foo=123456789 %%
%% foo format="%d-%b-%y" %% # "day-month-year" using time2str
%% foo format="%d" %% # "day" using timestr
%% foo format="%P%d" %% # decimal value using printf
%% foo format="%s" %% # string value using printf
Generates:
29-Nov-73
29
123456789
123456789
Text that is inserted with an INCLUDE or SUBST directive can
also be filtered. There are two default filters provided,
'escape' which can be used to escape (prefix with a backslash
'\') certain characters, and 'sr' which is used to perform
simple search and replace actions. Other filters may be added
with the FILTER option when creating the object (see the FILTER
section in the section on "USING THE METATEXT MODULE", above).
Like the 'format' option, output filters work on a line of text
at a time. Any parameters required for the filter can be
specified in parentheses after the filter name. The 'escape'
filter expects a perl-style character class indicating the
characters to escape. The 'sr' filter expects two parameters, a
search pattern and a replacement string, separated by a comma.
Note that parameters that include embedded spaces should be
quoted. The quote characters themselves must also be escaped as
they already form part of a quoted string (the filter text).
(This way of representing parameters is admittedly far from
ideal and may be improved in a future version.)
Example:
%% DEFINE text="Madam I'm Adam" %%
%% SUBST text filter="escape(['])" %%
%% SUBST text filter="sr(Adam, \"Frank Bough\")" %%
Generates:
Madam I\'m Adam
Madam I'm Frank Bough
Conditional tests can be applied to INCLUDE blocks to determine
if the block should evaluated or ignored. Variables and absolute
values can be used and can be evaluated in the following ways:
a == b # a is equal to b
a != b # a is not equal to b
a > b # a is greater than b
a < b # a is less than b
a => b # a is greater than or equal to b
a <= b # a is less than or equal to b
a =~ b # a matches the perl regex pattern b
a !~ b # a does not match the perl regex pattern b
a in b,c,d # a appears in the list b, c, d (see DELIMITER)
The items on the right of the evaluations can be absolute values
or variable names which should be prefixed by a '$'. The items
on the left of the evaluation are assumed to be variable names.
There is no need to prefix these with a '$', but you can if you
choose.
The single equality, "a = b", is treated identically to a double
equality "a == b" although the two traditionally represent
different things (the first, an assignment, the second, a
comparison). In this context, I consider the former usage
confusing and would recommend use of the latter at all times.
Variables without any comparison operator or operand are tested
for a true/false value.
Examples:
%% INCLUDE foo if="name==fred" %%
%% INCLUDE foo if="$name==fred" %% # equivalent to above
%% INCLUDE foo if="name==$goodguy" %%
%% INCLUDE foo if="hour > 10" %%
%% INCLUDE foo if="tonk =~ [Ss]pl?at" %%
%% INCLUDE foo if="camper" %%
Multiple conditions can be joined using the following boolean
operators
a && b # condition 'a' and 'b'
a || b # condition 'a' or 'b'
a ^ b # condition 'a' xor 'b'
a and b # same as "a && b" but with lower precedence
a or b # same as "a || b" but with lower precedence
a xor b # same as "a ^ b" but with lower precedence
Conditional equations are evaluated left to right and may
include parentheses to explicitly set precedence.
Examples:
%% INCLUDE tonk
if="hardenuf && uid in abw,wrigley"
%%
%% INCLUDE tapestry
if="(girly && studly < 1) || uid == neilb"
%%
%% INCLUDE tapestry
if="($girly && $studly < 1) || $uid == neilb"
%%
Note that the third example above is identical in meaning to the
second, but explicitly prefixes variable names with '$'. This is
optional for elements on the left hand side of comparison
operators, but mandatory for those on the right that might
otherwise be interpreted as absolute values.
BLOCK..ENDBLOCK
In some cases it is desirable to have a block of text available
to be inserted via INCLUDE without having to define it in an
external file. The BLOCK..ENDBLOCK directives allow this.
A BLOCK directive with a unique identifier marks the start of a
block definition. The block continues, including any valid
MetaText directives, until an ENDBLOCK directive is found.
A BLOCK..ENDBLOCK definition may appear anywhere in the file. It
is in fact possible to INCLUDE the block before it has been
defined as long as the block definition resides in the same
file.
Processing of a file stops when it encounters the __END__ marker
on a line by itself. Blocks can be defined after this marker
even though the contents of the file after __END__ are ignored
by the processor.
# include a block defined later
%% INCLUDE greeting name=Prospero %%
__END__
%% BLOCK greeting %%
Hello %% name %%
%% ENDBLOCK %%
This produces the following output:
# include a block defined later
Hello Prospero
Additional variable definitions specified in an INCLUDE
directive will be applied to blocks just as they would to
external files.
A BLOCK..ENDBLOCK definition that appears in the main part of a
document (i.e. before, or in the absence of an __END__ line)
will not appear in the processed output. A simple "print" flag
added to the BLOCK directive overrides this behaviour, causing a
copy of the BLOCK to appear in it's place:
%% DEFINE name=Caliban %%
%% BLOCK greeting print %%
Hello %% name %%
%% ENDBLOCK %%
%% INCLUDE greeting name="Prospero" %%
produces the following output:
Hello Caliban
Hello Prospero
Conditions ("if" and "unless") can be applied to BLOCK
directives, but they affect how and when the BLOCK itself is
printed, rather than determining if the block gets defined or
not. Conditionals have no effect on BLOCK directives that do not
include a "print" flag.
EXTENDING METATEXT
MetaText may be used as a base class for deriving other text
processing modules. Any member function of a derived class can
be called directly as a MetaText directive. See the EXECUTE
configuration option for more details.
Pseudo-code example:
package MyMetaText;
@ISA = qw( Text::MetaText );
# define a new derived class method, get_name()
sub get_name {
my $self = shift;
my $params = shift;
# return name from an ID hash, for example
$self->{ PEOPLE }->{ $params->{'id'} } || 'nobody';
}
package main;
# use the new derived class
my $mmt = MyMetaText { EXECUTE => 1 };
# process 'myfile'
print $mmt->process('myfile');
which, for a sample file, 'myfile':
%% get_name id=foo %%
%% get_name id=bar %%
is equivalent to:
print $mmt->get_name({ 'id' => 'foo' }), "\n";
print $mmt->get_name({ 'id' => 'bar' }), "\n";
Alternatively, a simple calling script can be written that
defines functions that themselves can be called from within a
document:
my $mt = Text::MetaText->new( { EXECUTE => 2 } );
print $mt->process("myfile");
sub get_name {
my $params = shift;
$global_people->{ $params->{'id'} } || 'nobody';
}
Please note that the functionality provided by the EXECUTE
option, and inherently, the extensibility possible by deriving
MetaText sub-classes, relies in part on the operation of the
AUTOLOAD method. Authors of derived MetaText classes should be
aware of, and account for this, if re-defining the AUTOLOAD
method.
WARNINGS AND ERRORS
The following list indicates warning or error messages that
MetaText can generate and their associated meanings.
"Closing directive tag missing in %s"
A MetaText directive was found that was not terminated
before the end of the file. e.g. `%% INCLUDE something ...'
The processor attempts to compensate, but check your source
files and add any missing MAGIC tokens.
"Invalid configuration parameter: %s"
An invalid configuration parameter was identified in the
hash array passed to Text::MetaText->new(). See the section
on "USING THE METATEXT MODULE".
"Invalid debug/error function"
The debug or error handling routine specified for the ERROR
or DEBUG configuration options was not a code reference. See
the ERROR and/or DEBUG sections for more details.
"Invalid debug option: %s"
A token was specified for the DEBUGLEVEL configuration item
which was invalid. See the DEBUGLEVEL section for a complete
list of valid tokens.
"Invalid roque option: %s"
A token was specified for the ROGUE configuration item which
was invalid. See the ROGUE section for a complete list of
valid tokens.
"Maximum recursion exceeded"
The processed file had multiple INCLUDE directives that
nested to a depth greater than MAXDEPTH (default: 32). Set
MAXDEPTH higher to avoid this problem, or check your files
for circular dependencies.
"Missing directive keyword"
A MetaText directive was identified that had no keyword or
other content. e.g. `%% %%'
"Text::MetaText->new expects a hash array reference"
The new() method can accept a reference to a hash array as
the first parameter which contains configuration variables
and values. This error is generated if the parameter is not
a hash array reference.
"Unrecognise directive: %s"
An internal error that should never happen. The pre-
processor has identified a directive type that the processor
then failed to recognise.
"Unrecognised token: %s"
A `%% SUBST %%' or `%% %%' directive
was found for which there was no corresponding
defined. This warning is only generated when the 'warn'
token is set for the ROGUE option.
"Unmatched parenthesis: %s"
A conditional evaluation ("if" or "unless") for a directive
is missing a closing parenthesis. e.g. `%% INCLUDE foobar
if="(foo && bar || baz" %%'
"%s: non-existant or invalid filter"
An INCLUDE or SUBST directive included a "filter" option
that refers to a non-existant filter. e.g. `%% INCLUDE foo
filter=nosuchfilter() %%'
AUTHOR
Andy Wardley
See also:
http://www.kfs.org/~abw/
http://www.kfs.org/~abw/perl/metatext/
My thanks extend to the people who have used and tested
MetaText. In particular, the members of the Peritas Online team;
Simon Matthews, Simon Millns and Gareth Scott; who brutally
tested the software over a period of many months and provided
valuable feedback, ideas and of course, bug reports. I am also
indebted to the members of the SAS Team at Canon Research Centre
Europe Ltd; Tim O'Donoghue, Neil Bowers, Ave Wrigley, Martin
Portman, Channing Walton and Gareth Rees; although I'm not yet
sure why. :-)
I welcome bug reports, enhancement suggestions, comments,
criticisms (hopefully constructive) and patches related to
MetaText. I would appreciate hearing from you if you find
MetaText particularly useful or indeed if it *doesn't* do what
you want, for whatever reason. Hopefully this will help me make
MetaText help you more.
It pains me to say that MetaText comes without guarantee or
warranty of suitability for any purpose whatsoever. That doesn't
mean it doesn't do anything good, but just that I don't want
some scrupulous old git to sue me because they thought I implied
it did something it doesn't. **
REVISION
$Revision: 0.15 $
COPYRIGHT
Copyright (c) 1996-1998 Andy Wardley. All Rights Reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
SEE ALSO
The metapage utility, the Date::Format module.