Text::FillIn.pm - a class implementing a fill-in template |
Text::FillIn.pm - a class implementing a fill-in template
use Text::FillIn;
# Set the functions to do the filling-in: Text::FillIn->hook('$', sub { return ${$_[0]} }); # Hard reference Text::FillIn->hook('&', "main::run_function"); # Symbolic reference sub run_function { return &{$_[0]} }
$template = new Text::FillIn('some text with [[$vars]] and [[&routines]]'); $filled_in = $template->interpret(); # Returns filled-in template print $filled_in; $template->interpret_and_print(); # Prints template to currently # selected filehandle
# Or $template = new Text::FillIn(); $template->set_text('the text is [[ $[[$var1]][[$var2]] ]]'); $TVars{'var1'} = 'two_'; $TVars{'var2'} = 'parter'; $TVars{'two_parter'} = 'interpreted'; $template->interpret_and_print(); # Prints "the text is interpreted"
# Or $template = new Text::FillIn(); $template->get_file('/etc/template_dir/my_template'); # Fetches a file
# Or $template = new Text::FillIn(); $template->path('.', '/etc/template_dir'); # Where to find templates $template->get_file('my_template'); # Gets ./my_template or # /etc/template_dir/my_template
This module provides a class for doing fill-in templates. These templates may be used as web pages with dynamic content, e-mail messages with fill-in fields, or whatever other uses you might think of. Text::FillIn provides handy methods for fetching files from the disk, printing a template while interpreting it (also called streaming), and nested fill-in sections (i.e. expressions like [[ $th[[$thing2]]ing1 ]] are legal).
Note that the version number here is 0.04 - that means that the interface may change a bit. In fact, it's already changed some with respect to 0.02 (see the CHANGES file). In particular, the $LEFT_DELIM, $RIGHT_DELIM, %HOOK, and @TEMPLATE_PATH variables are gone, replaced by a default/instance variable system.
I might also change the default hooks or something. Please read the CHANGES file before upgrading to find out whether I've changed anything you use.
In this documentation, I generally use ``template'' to mean ``an object of class Text::FillIn''.
The delimiters that set fill-in sections of your form apart from the rest of the form are generally [[ and ]], but they don't have to be, you can set them to whatever you want. So you could do this:
Text::FillIn->Ldelim('{'); Text::FillIn->Rdelim('}'); $template->set_text('this is a {$variable} and a {&function}.');
Whatever you set the delimiter to, you can put backslashes before them in your templates, to force them to be interpreted as literals:
$template->set_text('some [[$[[$var2]][[$var]]]] and \[[ text \]]'); $template->interpret_and_print(); # Prints "some stuff and [[ text ]]"
You cannot currently have several different kinds of delimiters in a single template.
Text::FillIn
needs to know how to treat
different kinds of [[tags]] it finds. The way it accomplishes this is through
``hook functions.'' These are various functions that Text::FillIn
will run
when confronted with various kinds of fill-in fields. There are two
hooks provided by default:
Text::FillIn->hook('$') is \&find_value, Text::FillIn->hook('&') is \&run_function.
So if you leave these hooks the way they are, when Text::FillIn sees
some text like ``some [[$vars]] and some [[&funk]]'', it will run
&Text::FillIn::find_value
to find the value of [[$vars]], and it will
run &Text::FillIn::run_function
to find the value of [[&funk]]. This
is based on the first non-whitespace character after the delimiter,
which is required to be a non-word character (no letters, numbers, or
underscores). You can define hooks for any non-word character you want:
$template = new Text::FillIn("some [[!mushrooms]] were in my shoes!"); $template->hook('!', "main::scream_it"); # or \&scream_it sub scream_it { my $text = shift; return uc($text); # Uppercase-it } $new_text = $template->interpret(); # Returns "some MUSHROOMS were in my shoes!"
Every hook function will be passed all the text between the delimiters, without any surrounding whitespace or the leading identifier (the & or $, or whatever). Hooks can be given as either hard references or symbolic references, but if they are symbolic, they need to use the complete package name and everything.
Beginning in version 0.04, you may use some object's methods as hook functions. For
example, if you have a template $template
and another object $myObj
, you can
instruct $template
to call $myObj->find_value()
and
$myObj->run_function()
to fill in templates. See the $template->object()
method below.
&Text::FillIn::find_value
and &Text::FillIn::run_function
. They are
extremely simple. I suggest you take a look at them to see how they work.
What follows here is a description of how these functions will fill in your
templates.
The &find_value
function looks for an entry in a hash called %main::TVars.
So put an entry in this hash if you want it to be available to templates:
my $template = new Text::FillIn( 'hey, [[$you]]!' ); $::TVars{'you'} = 'Sam'; $template->interpret_and_print(); # Prints "hey, Sam!"
The &run_function
function looks for a function in the TExport
package and
runs it. The reason it doesn't look in the main package is that you probably
don't want to make all the functions in your program available to the templates
(not that putting all your program's functions in the main package is always
the greatest programming style). Here are a couple of ways to make functions
available:
sub TExport::add_numbers { my $result; foreach (@_) { $result += $_; } return $result; }
# or, if you like:
package TExport; sub add_numbers { my $result; foreach (@_) { $result += $_; } return $result; }
The &run_function
function will split the argument string at commas, and pass
the resultant list to your function:
my $template = new Text::FillIn( 'Pi is about [[&add_numbers(3,.1,.04,.001,.0006)]]' ); $template->interpret_and_print;
In the original version of Text::FillIn
, I didn't provide any hook functions.
I expected people to write their own, partly because I didn't want to stifle
creativity or anything. I now include hook functions because the ones I give
will probably work okay for most people, and providing them means it's easier
to use the module right out of ``the box.'' But I hope you won't be afraid to write
your own hooks - if mine don't work well for you, by all means go ahead and
replace them with your own. If you think you've written some really killer hooks,
let me know. I may include cool ones with future distributions.
Text::FillIn
where to look for templates:
Text::FillIn->path('.', '/etc/template_dir'); $template->get_file('my_template'); # Gets ./my_template or /etc/template_dir/my_template
$template = new Text::FillIn("some [[$vars]] and some [[&funk]]");
$template->get_file( "my_template" ); $template->get_file( "/weird/place/with/template" );
The default path is ('.').
interpret()
$interpreted_text = $template->interpret();
This, along with interpret_and_print, are the main point of this whole module.
interpret_and_print()
If it encounters an expression like ``stuff1 [[thing1]] stuff2 [[thing2]]'', it will print stuff1, then the value of [[thing1]], then stuff2, then the value of [[thing2]]. This is as streamed as possible if you want nested brackets to resolve correctly.
The following methods all get and/or set certain attributes of the template. They can
all be called as instance methods, a la $template->Ldelim()
, or as static methods,
a la Text::FillIn->Ldelim()
. Using an instance method only changes the given
template, it does not affect the properties of any other template. Using a static method
will change the default behavior of all templates created in the future.
I think I need to reserve the right to change what happens when you create a template
$t, then change the default behavior of all templates, then call $t->interpret()
--
should it use the new defaults or the old defaults? Currently it uses the old
defaults, but that might change.
Ldelim($new_delimiter)
Rdelim($new_delimiter)
text($new_text)
get_file()
method.
object($obj)
$t = new Text::FillIn("some [[$animal]]s"); $obj = new MyClass(animal=>'chicken'); # Create some object $t->object($obj); # Tell $t to use methods of $obj as hooks $t->hook('$', 'lookup_var'); # Set the method name for '$' $t->interpret_and_print(); # Calls $obj->lookup_var()
The object methods will be passed the same arguments as regular (static) hook functions.
$template->property('color', 'blue'); # Set the color # ... some code... $color = $template->property('color'); # Get the color
The Text::FillIn class doesn't actually pay any attention whatsoever to the properties - it's purely for your own convenience, so that small changes in functionality can be achieved without having to subclass Text::FillIn.
If you want to use nested fill-ins on your template, make sure things get
printed in the order you think they'll be printed. If you have something like this:
[[$var_number_[[&get_number]]]]
, and your &get_number prints a number,
you won't get the results you probably want. Text::FillIn will print your number,
then try to interpret [[$var_number_]]
, which probably won't work.
The solution is to make &get_number return its number rather than print it.
Then Text::FillIn will turn [[$var_number_[[&get_number]]]]
into
[[$var_number_5]]
, and then print the value of $var_number_5
. That's
probably what you wanted.
The deprecated methods get_text(), set_text(), get_property(), and set_property()
will be removed in version 0.06 and greater. Use text()
and property()
instead.
By slick use of local()
variables, it would be possible to have Text::FillIn keep track of when
it's doing nested tags and when it's not, allowing the user to nest tags using arbitrary
depth and not have to worry about the above ``common mistake.'' This would let hook
functions be oblivious to whether they're supposed to print their results or return them,
since Text::FillIn would keep track of it all. This will take some doing on my part,
but it's not insurmountable. It would probably involve evaluating the tags from
the outside in, rather than the inside out.
The interpreting engine can be fooled by certain backslashing sequences like \\[[$var]]
,
which looks to it like the [[
is backslashed. I think I know how to fix this, but I need
to think about it a little.
Ken Williams (ken@forum.swarthmore.edu)
Copyright (c) 1998 Swarthmore College. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
Text::FillIn.pm - a class implementing a fill-in template |