|
||||
|
||||
|
| View source (Text/Wigwam.pm) |
NameText::Wigwam - A user-extensible template parser.
Synopsis
Simple# Parse a file use Text::Wigwam; my $wwobj = Text::Wigwam->new( file => 'path/to/filename.txt' ) or die $Text::Wigwam::ERROR; print $wwobj->parse; # # Parse a string use Text::Wigwam; my $wwobj = Text::Wigwam->new( text => 'foo=[!!foo!!] bar=[!!bar!!]' ) or die $Text::Wigwam::ERROR; print $wwobj->parse;
Verbose
use Text::Wigwam;
my $Restrict = {
options_ltag_id => '<<',
options_rtag_id => '>>',
default_engine => 'Fusion, FusionX',
};
my $Defaults = {
engine => 'Fusion, FusionX',
set_strict_tags => 1,
set_ltag_entity => '[!!',
set_rtag_entity => '!!]',
set_lblk_entity => '<!!',
set_rblk_entity => '!!>',
set_kill_wspace => '~',
set_numeric_off => 0,
set_spew_stacks => 0,
set_priv_vspace => 0,
set_save_vspace => undef,
set_pckg_root => undef,
set_pckg_path => undef,
};
my $Settings = {
plugins => 'DirectiveSet',
modules => 'CgiTools, HtmlTools',
set_spew_option => 0,
set_spew_stacks => 0,
set_no_drip_cache => 0,
};
my $varspace = { greet => "Hello", entity => "World" };
my $string = '[!!greet!!], [!!entity!!]';
my $wwobj = Text::Wigwam->new(
text => $string,
$Restrict,
$Defaults,
$Settings,
) or die $Text::Wigwam::ERROR;
$wwobj->set_path( template => '~/wigwam/templates/', '/wigwam/templates/' );
my( $error, $text ) = $wwobj->parse( $varspace );
die $error if $error;
print $text; # outputs "Hello, World"
AboutThe purpose of this documentation is to provide a categorized and detailed reference into the specifics of the Wigwam templating system. Even though we have arranged these categories in a manner which we hope provides the smoothest flow when read from beginning to end, there are still situations where it is necessary to make references to things not yet covered up to a particular point in the documentation in order to cover all aspects of a specific category. We have made an attempt to point this out whenever it occurs, but the bottom line is that despite our best efforts to the contrary, this document is not a substitute for a tutorial. We hope to provide some on-line tutorials in the future, which will no doubt be posted on the web site at http://www.wigwamhq.org. Until then, we suggest you skim through this document to pick up the basics, then try things out for a while and refer back later to grasp more of the details. Many of the examples found throughout this documentation make use of directives which are included in the DirectiveSet plug-in which is loaded by default unless it's explicitly overridden. The directives used in most of the examples herein will be self-explanatory for some, but for the rest, refer to the documentation for Text::Wigwam::Plugins::DirectiveSet.
DescriptionWigwam is a user-extensible/customizable template processor which provides a well-defined framework for intermingling text with dynamic content in a manner which is flexible, extensible, reusable, and efficient.
OverviewWhat differentiates Wigwam from most other template parsers we've come across is its extensibility, versatile data handling features, dynamic parsing options, and full-featured scripting environment. Another differentiating factor is that Wigwam is not largely regex driven, but is more of a token-based parser.
MechanicsWigwam can be divided into three fundamental components:
FeaturesWigwam offers a templating framework which provides:
Wigwam's default DirectiveSet plug-in extends this templating environment to include a powerful scripting language which offers:
PhilosophyInsert yours here.
FlexibilityRather than impose our own templating philosophies on anyone, we chose to design Wigwam to be flexible. Although Wigwam provides for some fairly complex code in templates by default, it can quite easily be reduced to nothing more than a variable interpolator, which may be all you are looking for in a template parser. Or, perhaps somewhere in between. Wigwam allows you this flexibility.
ExtensibilityThe primary goal throughout the development of Wigwam was to make it as easily user-extensible as possible, to facilitate custom directives with all the robustness of any of the directives found in the DirectiveSet plug-in via a relatively simple API and with a minimal amount of code & effort.
EfficiencyFor template coding and parsing efficiency, we've adopted a straight-forward Polish notation style syntax, as it requires the simplest of algorithms to process and is easy to learn. Under this scheme, template code is well-defined, parsed efficiently, and requires a minimal amount of pre-process time during tokenization, so templates execute with minimum initial overhead. Wigwam's automatic casting mechanism and prototype scheme were designed with developmental efficiency in mind. They're designed to keep the amount of code necessary to add custom directives to a minimum, and at the same time to reduce the likelihood of runtime errors during template execution.
InterfaceWigwam employs an object oriented interface which is the mechanism through which a user invokes Wigwam to construct a template object, set up parameters, and execute (parse) the template.
ConstructorThe new constructor returns a Wigwam template object, or undef upon error. It requires at least two arguments, the first of which indicates whether to parse a given string or a file. The second parameter is the string or filename which contains the root template to be processed. use Text::Wigwam; my $wwobj = Text::Wigwam->new( text => $string ) or die $Text::Wigwam::ERROR; # ...or... my $wwobj = Text::Wigwam->new( file => $filename ) or die $Text::Wigwam::ERROR; Optionally, three more parameters can be passed to the constructor, each of which are hash references. The key value pairs within these hash references are typically referred to as ``options'', or simply ``Wigwam options'' and are used to control some of the behavioral characteristics of the parsing engine, among other things. Most of these options can be set using any of the three hash reference parameters described below, however, each hash reference holds its own unique scope and/or priority level.
use Text::Wigwam;
my $wwobj = Text::Wigwam->new(
file => 'path/to/root_template.txt',
\%Restrict,
\%Defaults,
\%Settings,
) or die $Text::Wigwam::ERROR;
print $wwobj->parse( \%varspace );
RestrictOptions defined within this parameter are global in scope and cannot be overridden throughout the life of the template object. It is used to fix options to a specific value not just for the root template, but any given child template as well. This is a handy mechanism for locking out features, such as specifying a fixed set of modules for use by templates, or limiting templates to specific branches of the directive tree, and so on..
DefaultsOptions defined within this parameter represent the initial settings for the root template as well as all child templates, if any. These settings hold the lowest priority and are easily overridden, even from within a template.
SettingsOptions defined within this parameter only apply to the root template, and are not passed to child templates. These settings will override conflicting settings in the Defaults parameter, but they can be overridden by conflicting options within the Restrict parameter, or a Wigwam configuration tag within the root template.
Standard Wigwam Options(The available options will vary depending upon which parsing engine is in use) The following options can only be defined via the Restrict parameter.
The following can be defined via any parameter.
MethodsThe following methods can be invoked on a template object returned by the aforementioned constructor.
General
Directory PathsWigwam maintains several independent lists of directory paths which are used to locate various types of files. Whenever a file is required, Wigwam will attempt to locate it by searching the relevant path list in left-to-right order. Each list can be manipulated by invoking one of the following methods with the list class to be modified, followed by a list of directory paths.
Where class (the first argument) is one of the following non-case-sensitive list types:
Unrecognized class types will be silently ignored, and an empty list will always be returned as a result. use Text::Wigwam; $wwobj=Text::Wigwam->new( text => '[!!#template "blah" !!]' ); $wwobj->add_path( template => '/var/www/templates' ); print $wwobj->parse; A copy of any given list can be retrieved by calling the non-destructive add_path list manipulation method with an empty list. print "Template paths: " . join( ', ', $wwobj->add_path( template => () ) ); print "Module paths: ". join( ', ', $wwobj->add_path( module => () ) ); Directory paths can be given priority by placing them first in the list. This can be accomplished by first retrieving any existing list items (which is done by passing an empty list to the add_path method) and inserting the new items where appropriate. $wwobj->set_path( module => '/give/this/path/priority', $wwobj->add_path( 'module' ) );
Varspace relatedThe following methods can be used to pre-populate, or otherwise condition the template's variable space prior to template execution, or to retrieve data from the template's variable space after the template has executed. The function of each of the following methods are explained in more detail in the API section. It's also helpful to understand variables, which are explained in detail in the Variables section.
Globals relatedThe following methods can be used to pre-populate, or otherwise condition the template's Globals prior to template execution, or to retrieve data from the Globals facility after template execution. The function of each of the following methods are explained in more detail in the API section. It's also helpful to understand Globals, which are explained in the Globals section.
TemplatesWigwam templates are simply bodies of plain text enhanced with a customizable scripting language which is embedded within the text inside of predefined boundary identifiers (tag entities).
TagsWigwam tags are the elements of templates which invoke Wigwam code. They are denoted by the default delimiters [!! and !!], which typically encompass one or more Wigwam tokens. The Wigwam engine replaces these tags along with their contents with the resulting string of text that is produced after all of the encompassed tokens have been executed. Here is the gratuitous "[!!"Hello"!!], [!!"World"!!]" example. After the above example is parsed by the engine, it becomes: Here is the gratuitous "Hello, World" example. Tags are not limited to a single expression. The Wigwam engine will concatenate the results of each expression in a given tag, as demonstrated below. [!! "Hello" ", " "World" !!] produces: Hello, World Here is a somewhat more complex example which introduces a couple of arbitrary variables, as well as the #if directive (included in the DirectiveSet plug-in).
[!!
#if foo { "
This is some text
which will only be
output if 'foo' has
a true value.
That value is " foo "."
#if bar { "
bar is also true.
Its value is "
bar "."
}
}
!!]
BlocksBlocks are simply a means of representing an argument for any given directive in the form of a block of text (which may consist of embedded Wigwam tags and nested blocks) rather than a long string of tokens. A new block is opened whenever a block tag delimiter (!!> by default) is encountered, and is terminated by its counterpart, the block terminator tag (<!! by default). The block of text between these block tag delimiters is typically used as an argument for a directive inside the tag which opened the block.
[!!
#if foo !!>
This is some text
which will only be
output if 'foo' has
a true value.
That value is [!! foo !!].
[!! #if bar !!>
bar is also true.
Its value is [!! bar !!].
<!! !!]
<!!
!!]
Notice that the preceding two examples use the same #if directive. This demonstrates that blocks are not tied to any particular directive or set of directives. Because blocks are not directive specific, they may be used to represent an argument for any given directive. This contributes to Wigwam's flexibility.
TerminatorsA block terminator is simply a block terminator entity (<!! by default). [!! #if zig !!> Take off <!! #else !!> Great justice <!! !!] Blocks aren't limited to representing the final argument for a given directive, although this is the typical coding method. Consider the following example where we use a block as the first argument for the #if directive. [!! #if !!>1<!! "True" !!]
Side effectsBlocks also provide some interesting and useful side effects, the first of which is that the Wigwam engine (in passive mode) can look past, skip over, or otherwise ignore a template element defined within a Block much quicker than it could if the same code were expressed as a long string of tokens within conventional tag entities. Blocks are also handy in situations where it is desirable to define unexecuted code blocks (EXPR types, explained later) which contain directives whose corresponding module or plug-in may not yet have been loaded. The mechanics of this may be difficult to understand without intimate knowledge of how the Wigwam engine works, and it is unlikely that most people who will be using Wigwam will have this knowledge, so suffice it to say that the following template code will produce an exception unless the plug-in or module that handles the encompassed directives was loaded prior to execution.
[!! #proc foo { #bzork #bazz #biff } !!]
The same declaration can be done using Block tag entities, in which case it does not matter whether or not the encompassed directives' plug-ins and/or modules were loaded. Obviously, it does matter when the attempt is eventually made to execute the code. [!! #proc foo !!>[!!#bzork #bazz #biff!!]<!! !!] This can be useful for pre-defining pseudo-procedures (subroutines, macros) without requiring the corresponding plug-ins/modules to be loaded until they are necessary. This may not make much sense to you if this is your first read through this documentation, and you may never require the need to exploit such a quirk. However, this behavior is by design and thus it has been documented.
White-space modifierWhite-space surrounding any given tag can be eliminated at parse time by adding a white-space modifier
character ('~' by default) to the inside of the tag entity. The white-space modifier character can be redefined
using the This example removes the leading white-space to the left of the tag. Howdy, [!!~ " Stranger" !!] produces Howdy, Stranger This character can also be used at the closing tag to remove white-space up to and including the first encountered new-line character to the right of the tag. [!! "Hi, " ~!!] There! produces: Hi, There! White-space outside of the first encountered new-line character is left untouched, however. The purpose of this feature is to remove white-space used in formatting tag code - indenting nested tags & such.
Welcome
[!!~ #if 1 !!>
[!!~ "foobar" !!]
<!! !!]
Assuming there is a single space after ``Welcome'', produces Welcome foobar White-space produced by preceding tags is unaffected, however.. [!! "Welcome, " !!] [!!~ "Mister" !!] produces Welcome, Mister
CommentsAny text existing between the character sequences /* and */ within a Wigwam tag will be ignored by the engine. This is useful for commenting your template code. [!! /* Greeting */ "Welcome!" !!] produces Welcome!
SyntaxWigwam code is written using Polish notation in which operators precede their operands. This not only allows for well-defined expressions without the need for parentheses or other grouping delimiters, but also offers a consistent syntax and contributes to Wigwam's parsing efficiency. Ordinary Expression: 1 + 2 Polish Notation: + 1 2 Wigwam Code: #add 1 2
Example$text = 'one plus two equals [!!#add 1 2!!]'; use Text::Wigwam; my $wwobj = Text::Wigwam->new( text => $text ); print $wwobj->parse(); produces: one plus two equals 3
TokensWigwam tokens exist within Wigwam tags, and are separated by white-space. These tokens come in only three varieties: literals; variables; and directives. Together, they provide the power and flexibility needed to generate useful Wigwam templates.
LiteralsThere are two forms of literals...
QuotedQuoted literals are identified by their surrounding quotation marks: [!! "My Dog Has Fleas" !!] [!! "37" !!] [!! "Quote marks can be \"escaped\" with the backslash character" !!] Quoted literals can also be defined using the single quotation marks: [!! 'My Dog Has Ticks' !!] [!! 'The "double quote" marks don\'t need to be escaped here' !!] [!! 'However, the \'single quotes\' do' !!] Both methods are identical in terms of function, the only difference being the character which requires the escape character when used within the string itself. The special character sequences \n, \r, and \t in any quoted literal will produce a new-line, carriage return, and tab, respectively.
NumericUnquoted numeric literals are any string of characters that equates to a valid numeric value: [!! 42 !!] [!! 98.6 !!] [!! 186E3 !!]
VariablesWigwam variables are identified by their lack of surrounding decorations (e.g. var1). They represent values which can be examined, defined and undefined. They're used to access values within Wigwam's variable space, which is simply a hash reference as the following example demonstrates.
Wigwam Variables Perl Equivalents
foobar $varspace->{foobar}
var1 $varspace->{var1}
quux $varspace->{quux}
Path namesPath names are simply Wigwam variables with the introduction of dot and colon characters. These characters are part of a simplified syntax which is useful for directly accessing data within complex structures. Each individual component of a path name represents either a hash key, or an array element. The first component of any path name is always a hash key, each subsequent component is identified by the dot or colon character which precedes it. Components preceded by a dot character represent hash keys, and are restricted to valid Perl hash key characters.
Wigwam Variables Perl Equivalents
foo.var1 $varspace->{foo}->{var1}
foo.bar $varspace->{foo}->{bar}
foo.bar.baz $varspace->{foo}->{bar}->{baz}
Components preceded by a colon character represent array elements, and should always equate to a numeric value.
Wigwam Variables Perl Equivalents
foo:23 $varspace->{foo}->[23]
bar:1 $varspace->{bar}->[1]
baz:2:4.quux $varspace->{baz}->[2]->[4]->{quux}
Path names make a trivial task of traversing even the most complex data structures. They're also a hell of a lot more readable than their Perl equivalents, especially when you throw self-interpolation into the mix...
Self-interpolationWigwam variables can, themselves, be interpolated using the square-brackets, '[' and ']', allowing values to be accessed indirectly.
Wigwam Variables Perl Equivalents
foo.[quux] $varspace->{foo}->{$varspace->{quux}}
foo[quux] $varspace->{"foo$varspace->{quux}"}
fum:[baz.quux] $varspace->{fum}->[$varspace->{baz}->{quux}]
[fi:[fi.[fo]]] $varspace->{$varspace->{fi}->[$varspace->{fi}->{$varspace->{fo}}]}
Escaped CharactersAny of these identifying characters can be escaped using the back-slash character '\'.
Wigwam Variables Perl Equivalents
foo\.bar.baz $varspace->{'foo.bar'}->{baz}
baz\:2:4.quux $varspace->{'baz:2'}->[4]->{quux}
fum\:[baz.quux] $varspace->{'fum:'.$varspace->{baz}->{quux}}
\[fi\]:[fi.[fo]] $varspace->{'[fi]'}->{$varspace->{fi}->{$varspace->{fo}}}
DirectivesDirective tokens are identified by the hash mark '#' preceding them. They take the following form: #directive arg1 arg2 ... All operations including defining variables, weighing values against each other, comparing strings, and connecting to a database, are performed by directives. Directives are, in reality, just Perl subroutines which are typically organized in modules or plug-ins and are invoked by the parsing engine whenever a directive token is encountered in a template. Once executed, the directive and all of its associated arguments are interpolated by the parsing engine with the directive's return value.
ArgumentsEach directive requires its own fixed number of arguments. For example, the #define directive in the DirectiveSet plug-in requires two arguments, a variable name and a value. It is important to understand that the number of arguments for each directive is fixed. It is crucial to provide the exact number of arguments required by a particular directive when writing Wigwam templates, otherwise you are bound to encounter unpredictable results. #define var "value" It is perfectly acceptable to use directives as arguments. In the following example, the #define directive's arguments consist of the variable name 'var', and the #add directive. The #add directive requires two arguments (1 and 2, in the example below), and since its function is to return the sum of its arguments, it returns '3', which then replaces the expression #add 1 2. Ultimately, the value used as the second argument for the #define directive is '3', where it is assigned to the variable 'var'. #define var #add 1 2 ...becomes... #define var 3
Directive treeThe directive tree is a hierarchy designed around Perl's package structure to organize directives into categories. The internal base of the directive tree is fixed in the Text::Wigwam::Directives name space. By default, any simple directive such as #foo is expected to be located in the Text::Wigwam::Directives name space. Subsequent nested packages represent branches of the directive tree and can be accessed explicitly using the following directive syntax: #name::space::here::directive_name_here In the following example, the Wigwam engine would expect to find the connect directive within the Text::Wigwam::Directives::mysql name space. #mysql::connect ... Certain features are available which can be used to temporarily overload directives, or restrict templates from accessing certain directives within the directive tree. These features are controlled by the The The following table lists some examples of how the engine will attempt to locate a given directive.
set_pckg_root directive name space in which the directive is presumed to exist
(null) #foo Text::Wigwam::Directives
quux #foo Text::Wigwam::Directives::quux
blah #fee::fye::foo Text::Wigwam::Directives::blah::fee::fye
roe::sham #boe::foo Text::Wigwam::Directives::roe::sham::boe
The
set_pckg_root set_pckg_path directive name space search order
(null) bazz,bazz::quux #foo Text::Wigwam::Directives::bazz
Text::Wigwam::Directives::bazz::quux
Text::Wigwam::Directives
set_pckg_root set_pckg_path directive name space search order
(null) bazz,quux #fe::fi::foo Text::Wigwam::Directives::bazz::fe::fi
Text::Wigwam::Directives::quux::fe::fi
Text::Wigwam::Directives::fe::fi
As the previous examples demonstrate, the root name space will assume the last value in the path. The root name space can be specified elsewhere in the path with an empty entry, as the following table attempts to demonstrate.
set_pckg_root set_pckg_path directive name space search order
(null) ,bazz,bazz::quux #foo Text::Wigwam::Directives
Text::Wigwam::Directives::bazz
Text::Wigwam::Directives::bazz::quux
set_pckg_root set_pckg_path directive name space search order
(null) bazz,,quux #fe::fi::foo Text::Wigwam::Directives::bazz::fe::fi
Text::Wigwam::Directives::fe::fi
Text::Wigwam::Directives::quux::fe::fi
The following tables list some examples of how the engine searches for directives given various
set_pckg_root set_pckg_path directive name space search order
foobar bazz,quux #foo Text::Wigwam::Directives::foobar::bazz
Text::Wigwam::Directives::foobar::quux
Text::Wigwam::Directives::foobar
set_pckg_root set_pckg_path directive name space search order
bar bazz,quux #fe::fi::foo Text::Wigwam::Directives::bar::bazz::fe::fi
Text::Wigwam::Directives::bar::quux::fe::fi
Text::Wigwam::Directives::bar::fe::fi
set_pckg_root set_pckg_path directive name space search order
foobar bazz,,quux #foo Text::Wigwam::Directives::foobar::bazz
Text::Wigwam::Directives::foobar
Text::Wigwam::Directives::foobar::quux
A clever administrator could, in theory, use this hierarchy to implement a set of strategically designed plug-ins and modules which, when used with these restrictive option settings, limit a template coder to a specific set of directives. It can also be used to enable any given child template to temporarily overload directives.
InterpolationWigwam variable interpolation is supported in directive names, much like it is done with Wigwam variables. The motivation for this feature was to add a layer of abstraction in calling directives which exist within a sub-level of the current branch of the directive tree.
[!!
#define db "pgsql"
#[db]::connect
!!]
translates to
[!!
#pgsql::connect
!!]
This feature is not limited to the name space portion of the directive handle, as demonstrated in the following example.
[!!
#define action "add"
#[action] 2 3
!!]
translates to
[!!
#add 2 3
!!]
which produces 5
Dynamic parsing optionsWigwam's dynamic parsing options are typically used to define the behavioral characteristics of the parsing engine. They are manipulated via a uniquely identified and specially formatted configuration tag. This mechanism can be used to override pre-specified default values for the current template, or to restrict (lock) or alter the default settings for child templates. They take the following form: << Wigwam class: key1=value1; key2=value2; >> Where class is one of Options, Restrict, Defaults, or Settings (case insensitive). The key=value pairs that follow the class declaration are made members of that class. Configuration tags are very passive. Any unsupported class definitions and all key=value pairs defined therein will be silently ignored. Furthermore, no checks are made to determine whether any given option key serves a purpose, nor whether its value is of the required type or format. The Options class is the only one that will affect the current template. The remainder of the classes are used to define defaults or restrictions for subsequently invoked child templates, and they directly correlate to their counterparts described in the Interface section. Note also that any predeclared Restrict settings still have precedence over all. Only a single configuration tag may exist in any given template file, as only the first encountered configuration tag will be parsed. Any other configuration tags within the template will be ignored and treated as plain text. However, the various classes may be cascaded within this single configuration tag: << Wigwam Options: set_ltag_entity=[!!; set_rtag_entity=!!]; Restrict: engine=Foomatic; >> Duplicate keys within a common class will be ignored.
<< Wigwam
Options:
set_ltag_entity=[!!;
set_rtag_entity=!!];
Restrict:
engine=Foomatic;
Options:
set_ltag_entity=[%; /* ignored */
set_strict_tags=0;
set_rtag_entity=%]; /* ignored */
>>
The colon, semicolon, and equals characters must be escaped using the back-slash character '\' when used as part of a key or value in a configuration tag.
<< Wigwam
Options:
set_pckg_root=bazz\:\:quux;
set_pckg_path=foo\:\:bar;
some\=crazy\:key=some\;wacky\=value;
>>
Here's an example that demonstrates some available option keys and their respective standard values:
<< Wigwam
Options:
set_engine=Fusion;
set_strict_tags=1;
set_strict_code=1;
set_ltag_entity=[!!;
set_rtag_entity=!!];
set_lblk_entity=<!!;
set_rblk_entity=!!>;
set_kill_wspace=~;
set_priv_vspace=0;
set_numeric_off=0;
set_spew_option=0;
set_spew_stacks=0;
plugins=DirectiveSet;
modules=CgiTools, HtmlTools;
>>
The default Wigwam configuration tag entities, << and >>, can be changed to whatever characters you see fit by using the options_ltag_id and options_rtag_id options exclusively within the Restrict hash reference parameter during the object construction phase. This is a global alteration and applies not only to the root template, but to all child templates as well. They cannot be changed throughout the life of the template object. As an example, a CGI wrapper may alter the Wigwam configuration tag identifiers so that they take on a form similar to SSI in order to better blend in with html documents: <!--Wigwam Options: set_engine=Fusion; set_priv_vspace=0; -->
Development environment
ModulesModules are simply Perl external library files which are used to organize the subroutines that make up custom directives.
The directives contained within these modules become available to templates by loading them via the << Wigwam Options: modules=CgiTools, HtmlTools; >> As with all Perl external library files, the last executable statement in a Wigwam module should result in a true value. Simply putting ``1;'' or ``return 1;'' as this last executable value in the file will suffice. However, sometimes it is necessary for a module or plug-in to declare some Globals upon initialization. This can be accomplished by returning a reference to a subroutine instead of simply ``1;'' as the last executable statement in the file. This code will be executed and passed an API object as its only argument each time the module is initialized by a new template object. Package declarations within Wigwam plug-ins and modules must begin with the base name space Text::Wigwam::Directives (the base of the directive tree). Subsequent nested name spaces must consist of all lower case characters, however (i.e. Text::Wigwam::Directives::blah::blah). Modules which do not explicitly declare a package will be loaded into the base name space, Text::Wigwam::Directives by default, as this is the default name space in which the Wigwam engine will look for directives.
Plug-insPlug-ins are functionally equivalent to modules, but differ in that they may associate directives with single-character symbols (i.e. '{' => #do, '(' => #list, etc.) and are given priority over modules in terms of loading and initialization. Plug-ins are stored separately from modules in the 'Text/Wigwam/Plugins' folder and are loaded via the << Wigwam Options: plugins=DirectiveSet, CustomLingo; >>
Writing directivesWriting your own directive is extremely simple, as has been the intention throughout the development of Wigwam. All that is required for any single directive are two specifically named subroutines consisting of a prototype, and a handler. Handlers use the following naming convention:
sub _directive{ }
Prototypes are named like their handler counterpart but have ``_proto'' prepended:
sub _proto_directive{ }
These two Perl subroutines make up a directive named #directive. The existence of these subroutines is detected at run-time when a directive is encountered by the engine while parsing a template. If either of these subroutines is absent, the engine will throw an exception. Note that both subroutine names must use all lower case, as the engine will associate #directive, #Directive, #DIRective, #DIRECTIVE, etc. to the same all-lower-case handler/prototype pair.
PrototypesThe Wigwam engine uses the information gleaned from prototypes to ensure that directive handlers receive the type of data that they expect for each of their arguments. The engine will perform the necessary type-casting when required to ensure this. This technique puts much of the burden of housekeeping on the Wigwam engine, which contributes to simplifying the task of directive coding. Prototypes should do nothing more than return a simple array reference. Each argument required by the directive handler is represented by its corresponding element in this array. Each argument's requested data type is represented by the value stored in their corresponding array element. Consequently, the number of arguments required by any given directive is determined by the number of elements in this array. The values stored within this array consist of numeric constants which represent the various data types. These constants are accessible via methods provided by an object which is passed as an argument to the prototype upon its invocation, or they can be imported from the Text::Wigwam::Const class. The various constants are described in detail below. As you might expect, prototypes are called by the Wigwam engine before the handler is called. However,
prototypes are typically only called once per parsing session upon first encountering any given directive.
The information is typically pulled from a cache upon subsequent encounters. This cache can be de-activated
via the Besides providing useful information to the Wigwam engine, prototypes also provide an overview into the design of any given directive. This is useful for making sense of Wigwam modules written by someone other than yourself.
HandlersHandlers perform the function of the directive. The Wigwam engine replaces the directive and its associated arguments in the template with the directive handler's return value. Directive handlers are required to pull exactly all of the arguments declared in their corresponding prototype. Pulling too few arguments, or attempting to pull too many, will cause the Wigwam engine to throw an exception. Arguments are processed by calling methods of the Wigwam API object which is provided as an argument
by the engine upon invocation of the directive handler. These methods are explained in more detail later,
but for typical module coding, you'll rarely need to use anything besides the methods:
ExamplesHere's a simple directive called #fnord, which concatenates two given scalars and inserts ``(fnord)'' between them: #fnord's prototype:
sub _proto_fnord{ [ $_[0]->SCALAR, $_[0]->SCALAR ] }
This directive's prototype indicates to the Wigwam engine that it requires two arguments of type SCALAR. It does this by returning an array reference containing two elements whose values both indicate SCALAR type. #fnord's handler:
sub _fnord{ return( $_[0]->get_arg." (fnord) ".$_[0]->get_arg ); }
The handler in the preceding example makes two calls to the The following example demonstrates an alternative method of coding the same thing.
use Text::Wigwam::Const qw(:all); # Imports Wigwam constants
sub _proto_fnord{ [ SCALAR, SCALAR ] }
sub _fnord{ my $API=shift; return( $API->get_arg." (fnord) ".$API->get_arg ); }
Let's see our #fnord directive in action in a template - we'll assume that the module containing our #fnord directive routines was already saved as ``Fnord.pm'' into the appropriate modules folder... << Wigwam Options: modules=Fnord; >> [!!#fnord "Wigwam modules" "are fun!"!!] produces: Wigwam modules (fnord) are fun!
CaveatIt is important that you use the
sub _proto_unless{ my $WWC=shift; return [ $WWC->SCALAR, $WWC->ANY ]; }
sub _unless {
my $API=shift;
if( $API->get_arg ){
$API->get_arg; # retrieve the next argument,
return; # and return null.
}
return $API->get_arg;
}
This works fine in situations like this: [!!#unless foo bar!!] But consider situations like this: [!!#unless foo #undefine bar!!] In the latter example, #undefine bar will be executed regardless of the value of foo, which is probably not the behavior you want. The directive handler ought to be rewritten to use
sub _unless {
my $API=shift;
if( $API->get_arg ){
$API->kill_arg(1); # destroy the next argument,
return; # and return null.
}
return $API->get_arg;
}
ConstantsThe Text::Wigwam::Const class provides several constants which are to be used within directive prototypes to specify argument attributes required by their corresponding directive handlers.
Data type
Constant Instructs the Wigwam engine to execute the next argument, and return...
HASH a hash reference.
ARRAY an array reference.
SCALAR_REF a scalar reference.
* BLESSED a blessed reference (or else throw an exception).
* NUM a numeric scalar value (or else throw an exception).
* ANY any value - no casting or type-checking is performed.
SCALAR a scalar value.
VAR a scalar value.. unless the given argument is a variable name, in which case,
return the raw variable name rather than its value. When used in conjunction
with ARRAY or HASH, the Wigwam engine will vivify this variable's value with
an empty array or hash, respectively, if it was previously undefined.
* To be used exclusively. Do not combine with any other data type constant.
In most cases, the following argument modifiers should be used in conjunction with at least one of the above data type constants. Multiple modifiers can be specified per argument, so long as it makes sense.
Modifier
Constant Signals the Wigwam engine that we wish to...
BLOCK Execute this argument in a new block scope.
(see the Globals section).
EXPR Retrieve the next argument in the form of an expression so that it may be
executed at a later time, multiple times, or perhaps not at all.
Note: An EXPR object is executed by invoking its 'execute' method.
STRICT Disallow casting (accepting only the specified data type(s)). Generates
an exception if the value encountered is of a different type than what is
specified for this argument.
TERM Keep pulling arguments until an #end terminator token is encountered.
(see C<list_ends> in the API section).
(rarely needed)
Access to the constants can be achieved in several ways. They can all be imported into the current name space, thusly. use Text::Wigwam::Const qw(:all); Or, more selectively... use Text::Wigwam::Const qw( ARRAY HASH SCALAR ); Importing is probably the prettiest way, as prototypes look more readable.
use Text::Wigwam::Const qw( ARRAY HASH SCALAR );
sub _proto_blah { [ SCALAR, HASH, ARRAY ] }
Or, as methods of the constants object which is passed in to each prototype upon invocation, for convenience.
sub _proto_blah { [ $_[0]->SCALAR, $_[0]->HASH, $_[0]->ARRAY ] }
These attributes can also be strung together using Perl's bitwise-or operator '|' to indicate a number of acceptable data types for your directive handler. To demonstrate, here is the code for the #reverse directive straight out of the DirectiveSet plug-in...
sub _proto_reverse{ [ $_[0]->ARRAY | $_[0]->HASH ] }
sub _reverse{
my $arg = $_[0]->get_arg;
return { reverse( %{$arg} ) } if $_[0]->is_hash( $arg ); # Hash ref?
return [ reverse( @{$arg} ) ]; # Assume it's an array ref
}
The prototype in the above example guarantees that we will receive either an array reference
or a hash reference upon calling the
The EXPR data type
sub _proto_badong { [ EXPR ] }
# is identical to:
sub _proto_badong { [ EXPR | ANY ] }
# Or we can force the EXPR type to return a specific data type when executed:
sub _proto_badong { [ EXPR | ARRAY ] }
sub _badong {
my $expr = $_[0]->get_arg;
my $array = $expr->execute; # We're guaranteed an array reference here.
}
EXPRs execute within the same environment as the template from which they originate. This means that if an expression object is executed from within a template other than the one where it was defined, the EXPR may not have access to the same varspace, directives, etc. as the template that invoked it. Conversely, the EXPR may have access to directives and resources that the calling template doesn't. This feature can be used as a means for providing some advanced functions to templates which are restricted to a very limited branch of the directive tree. Some other standard methods available in EXPRs:
APIThe Wigwam API is an object that is passed by the engine as the first (and only) argument to any given directive handler upon invocation. It provides methods which can be called by directive handlers to perform basic functions, such as handling arguments, manipulating variables, managing Globals, and other useful functions. They are invoked using one of the following techniques: my $arg = $_[0]->get_arg; # do something with $arg... # ..or.. my $API=shift; my $arg = $API->get_arg; # do something with $arg...
Argument functions
Basic variable functionsThese functions perform operations on variables stored within Wigwam's variable space.
Data type detection functions
Note: The preceding methods will detect the underlying referent types of blessed references, with the exception of inherent Wigwam data types, such as EXPR. Of the data type detection methods described here, the is_expr method is the only one that will return true when passed an EXPR object, regardless of its true underlying referent type.
Miscellaneous functions
Globals functionsFor a more detailed description of what Globals are and how to use them, read the Globals section.
Advanced variable functionsThe following variable functions are made available to directive handlers for rare situations where it's required to traverse arbitrary data complexes which are not necessarily stored within the normal varspace. They are primarily used by the Wigwam engine and are not typically used in directive handlers, since the basic variable functions will suffice 99% of the time.
Where...
Caveat: If a path name is embedded within a var inside of square brackets (i.e. foo.[bar]), that embedded element's value is retrieved based on the root varspace regardless of the ref value. As an example, the following routine probably will not return ``bazz'' as you might expect...
my $ref = { foo => { quux => 'bazz' }, bar => 'quux' };
return $API->generalized_get_value( $ref, 0, 'foo.[bar]' );
...instead, the call to
return $ref->{foo}{$API->get_value('bar')};
CastingPrototype information is used to determine the type of data required by the directive which is requesting the argument. Casting is performed by the Wigwam engine only if needed, so if a particular directive requests several acceptable types and the retrieved value matches any one of those types, no casting is performed and the value is returned as is. All blessed references are treated according to their underlying referent data type unless the BLESSED attribute is set, in which case the raw blessed reference value is returned. Should the BLESSED attribute be set and a non-blessed reference is encountered, an exception is generated. Should the cast facility encounter an EXPR type, it will be executed, and its resulting value is cast and returned accordingly.
Type Type Casting
Requested Retrieved Method (Perl) Which Returns
SCALAR SCALAR N/A The unaltered scalar value.
SCALAR SCALAR_REF $$value The dereferenced scalar value.
SCALAR ARRAY scalar @$a The number of array elements.
SCALAR HASH scalar keys %$h The number of hash keys.
ARRAY SCALAR [ $scalar ] An array reference with a single scalar element, or
null string [] an empty array reference if the scalar is a null string.
ARRAY SCALAR_REF [ $$scal_ref ] The value is dereferenced and cast as a SCALAR.
ARRAY ARRAY N/A The unaltered array reference.
ARRAY HASH [ %$hash ] An array reference whose elements take on the
keys/values of the hash in some arbitrary order.
HASH SCALAR { $scalar } A hashref with the scalar value as its only key, or
null string {} An empty hash reference if the scalar is a null string.
HASH SCALAR_REF { $$scal_ref } The value is dereferenced and cast as a SCALAR.
HASH ARRAY { @$a } A hash reference whose key/value pairs are populated by
the array elements.
HASH HASH N/A The unaltered hash reference.
ANY any N/A The unaltered value - no casting.
Any time an exception is generated while an argument is being processed for any given directive, the cast routine will return a null version of the requested data type so that the parser can make a clean exit.
GlobalsWigwam Globals have nothing to do with Wigwam's variable space, rather, it's a stack management utility which can be used by directive handlers to pass data and state information to other directive handlers within a common scope, as well as providing a means to dynamically control the engine's parsing mode. Globals are manipulated by way of specific methods of the API (see the API section for a full list of available methods). Globals make the following capabilities possible:
Child templates inherit their parent template's Globals data upon invocation, and all directive handlers have access to the same Globals data regardless of the name space they inhabit. This is the reason they are referred to as Globals. Globals are not generally needed in most directive handlers, but they are particularly useful when coding directives that affect process control (#break, #continue, #return, #exit, etc.), or those which interact with other directives within a common scope (#if, #elsif, #else, #given, #when, etc.). Globals are declared by calling the $API->new_global( scope, name, default, eflag );
Global values within the current scope are manipulated by way of the $API->set_global( name, value ); $foo = $API->get_global( name );
Block scopeBLOCK scope Globals must be declared by calling the $API->new_global( 'BLOCK', name, default, eflag ); The directives #if, #elsif, and #else make use of a BLOCK scoped Global variable to maintain state. These directives take the following generalized form: #if expression block #elsif expression block #else block The BLOCK scoped Global elseval is pre-declared in the module init routine to maintain state:
return sub { my $API=shift; $API->new_global( 'BLOCK', 'elseval', undef, 0 ); }
BLOCK scope is declared on a per-argument basis in the directive's argument prototype:
sub _proto_if{ my $WWC=shift; return [ $WWC->SCALAR, $WWC->ANY | $WWC->BLOCK ]; }
sub _proto_elsif{ my $WWC=shift; return [ $WWC->SCALAR, $WWC->ANY | $WWC->BLOCK ]; }
sub _proto_else{ my $WWC=shift; return [ $WWC->ANY | $WWC->BLOCK ]; }
When the Wigwam engine encounters a BLOCK argument, a new BLOCK scope is generated automatically, and thus a new instance of elseval vivified to its default value. Once that BLOCK argument has been processed, the previous BLOCK scope is restored immediately before the result is returned.
Local scopeLOCAL scope Globals can be pre-declared within a module init routine by calling the $API->new_global( 'LOCAL', name, default, eflag ); LOCAL scope is invoked by the engine on specified variable names before the directive handler is called. These variable names are declared in an array reference existing as the second argument of the prototype:
sub _proto_call{ return( [ $_[0]->VAR, $_[0]->ARRAY ], [ 'returnval', 'return' ] ); }
This signals the engine to push a new default value onto the returnval and return stacks before calling the directive handler. Once the directive handler has completed execution, the Wigwam engine restores the original values by popping the stacks. LOCAL scoped Globals need not be pre-declared by calling the
sub _proto_bazz{ return( [ ], [ 'bazzvar' ] ); }
Assuming bazzvar was never pre declared using $API->new_global( 'LOCAL', 'bazzvar', undef, 0 );
Template scopeTEMPLATE scope variables must be pre-declared by invoking the $API->new_global( 'TEMPLATE', name, default, eflag ); TEMPLATE scope is like BLOCK scope in that all variables declared as such are updated as a whole. TEMPLATE scoped variables are refreshed each time an external template is processed. As an example, the #exit directive uses the 'bail' Global to halt further processing of a template. It is declared with 'TEMPLATE' scope and its eflag set to 1, which signals the engine to stop processing tokens within the current TEMPLATE scope as long as its value remains true.
sub _proto_exit{ return [ ]; }
sub _exit{ $_[0]->set_global( bail => 1 ); return; }
return sub { $_[0]->new_global( 'TEMPLATE', 'bail', 0, 1 ); } # module init routine
Global scopeGLOBAL scope variables must be pre-declared by calling the $API->new_global( 'GLOBAL', name, default, eflag ); GLOBAL scope is just that. The Wigwam engine never generates a new GLOBAL scope. Any Global
declared with GLOBAL scope will retain its value until altered by The GLOBAL scoped die Global is the only Global inherently declared by the Wigwam engine. Its eflag is set to true so that all processing of the current template and parent templates (if any) will cease if its value becomes true. $API->new_global( 'GLOBAL', 'die', undef, 1 ); The API sets this GLOBAL's value upon invocation of the exception method.
EngineThe Wigwam engine is essentially Wigwam itself. It provides the basic framework for parsing templates by taking care of the low-level details involved. Although the engine directly handles literal-type and variable-type tokens, the directive tokens are passed off to plug-ins and modules. The engine monitors all data being passed between these directives filters it through its data type casting facility. This data type casting mechanism contributes to minimizing the amount of code needed within directive handlers.
Basic functions
Parsing modesYou really don't need to know much about the engine to make use of Wigwam. However, if you plan to write your own directives, it's helpful to understand the engine's parsing modes.
ActiveActive mode is what we call it when the engine is happily executing tokens.
PassivePassive mode is what we call it when the engine is pulling tokens without executing them. This mode is often automatically enabled by 'magical' eflag enabled Globals whenever any one of them accumulates a true value (see the Globals section).
ExportNone by default.
See AlsoHTML::TEMPLATE TEMPLATE::TOOLKIT
AuthorDJOHNSTON, <djohnston@cpan.org> WINGNUT, <wingnut@cpan.org> The latest Wigwam version and documentation are available at http://www.wigwamhq.org
Copyright and LicenseCopyright 2004 by Scot Woodward and Daniel Johnston This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. |