Category: | Text Processing |
Author/Contact Info | sauoq PM me. |
Description: | Yeah... sigh... it's another templating system. I know CPAN is a bit overrun with them. (Just try to eke out a reasonable namespace for another templating module.) But, I've found it to be so useful over the years I can't not share it, so I'm sharing it here. I've cleaned it up a bit and fleshed out the documentation for public consumption, but it started as a quickie hack, so forgive me if it still seems like one. I've always called it Local::SimpleTemplate. I've removed the "Local" for posting here. If you don't like the name for any reason feel free to change it; it's just a matter of changing the package line. Originally, I just needed a replacement for a brittle little shell script that generated a bunch of config files from here documents and shell variables. I broke it up into a bunch of separate but similar perl scripts, one for each config file with the config's template in the __DATA__ section of the script. Each script read key/value pairs from stdin and substituted corresponding tags in the template with the input values. That little bit of history will explain this module's quirky (but surprisingly useful) default behavior. The included POD documentation should be sufficient to learn how it works, but if you have questions please let me know. If you find any bugs, please let me know about those too. It has worked for me in a variety of uses but I did make a few tweaks before posting and may not have tested completely. |
package SimpleTemplate; use strict; use warnings; use vars qw( $VERSION ); $VERSION = '1.00'; my $default; # Holds the default template object when class methods ar +e used. sub new { my $class = shift; my $content = shift || _ST_default_content(); my $self = { '_delims' => [qw/ <% %> /], '_content' => $content, '_listsep' => "\n", }; bless $self, $class; } sub list_separator { my $self = shift; if (! ref $self) { $self = $default = $default || __PACKAGE__->new() + } return @_ ? $self->{_listsep} = shift : $self->{_listsep}; } sub delimiters { my $self = shift; if (! ref $self) { $self = $default = $default || __PACKAGE__->new() + } $self->{_delims} = shift if @_ == 1; $self->{_delims} = [shift, shift] if @_ == 2; return $self->{_delims}; } sub tags { my $self = shift; if (! ref $self) { $self = $default = $default || __PACKAGE__->new() + } my ($L, $R) = @{$self->{_delims}}; my @list = $self->{_content} =~ m/\Q$L\E\s*(.*?)\s*\Q$R\E/gm; return @list; } sub fill { my $self = shift; if (! ref $self) { $self = $default = $default || __PACKAGE__->new() + } my $args = shift; my $replace = shift; my ($L, $R) = @{$self->{_delims}}; my $text = $self->{_content}; $text =~ s/\Q$L\E\s*(.*?)\s*\Q$R\E/$self->_expand($1,$args)/egm; $self->{_content} = $text if $replace; return \$text; } # Private Methods sub _expand { my $self = shift; my ($key, $hashr) = @_; my $value = $hashr->{$key}; return '' unless defined $value; for (ref $value) { /CODE/ and do { return $value->() }; /ARRAY/ and do { return join $self->{_listsep}, @$value }; /SCALAR/ and do { return $$value }; /^$/ and do { return $value }; } } # Private class methods sub _ST_default_content { my $FH; my $pkg = caller; no strict 'refs'; # if DATA exists in the calling package use that. if ( exists ${ $pkg . '::' }{DATA} and defined *{${$pkg . '::'}{DATA +}}{IO}) { $FH = *{${$pkg . '::'}{DATA}}{IO}; # else if DATA exists in main, use that. } elsif ( exists $main::{DATA} and defined *main::DATA{IO} ){ $FH = *main::DATA{IO}; # else use <> } else { $FH = \*ARGV; } do { undef $/; <$FH> }; } q( My two cents aren't worth a dime. ); __END__ =head1 NAME SimpleTemplate - Perl extension for very simple templates. =head1 SYNOPSIS use SimpleTemplate; my $template = SimpleTemplate->new("Hello, <%World%>!\n"); my $ref = $template->fill({ World => 'PerlMonks' }); print $$ref exit(0); # Also try the bells, whistles, and variations! Like... # Filling placeholders with other thingies! my $textref = $template->fill( { Foo => \$scalar_ref, Bar => \@or_a_list, Baz => \&a_sub_ref, } ); # Default Content! my $template = SimpleTemplate->new(); # Default Content via a class method! SimpleTemplate->fill({ PlaceHolder => 'Your Favorite Value' }); # Change your delimiters! $template->delimiters('[:', ':]'); # Change your list separator! $template->list_separator(':'); # Find the tag names in your Content! my @tags = $template->tags(); =head1 DESCRIPTION This module allows for filling in placeholders in templates. It is mea +nt to exceedingly simple. It does not make it possible for you to actually p +ut code in your templates. If that is what you want, there are many other (les +s simple) modules that would be a better choice. SimpleTemplate works in two (intermixable) modes. When the provided me +thods are called as class methods, they operate on a default template object sto +red in a private class variable. This default template is created the first tim +e any of the methods are called as class methods. It is created by calling the constructor without any arguments. I've found this module to be extreeeeeeeeeeeeeeeeeeeeeeeemely useful. +I use it for all manner of things. Here's a hint... just because SimpleTemplate + is simple that doesn't mean your use of it has to be. SimpleTemplate can +do a lot, especially if you make heavy use of the delimiters() method and the op +tional argument to fill(). =head1 METHODS =over 8 =item new() The constructor can be called with either a single scalar argument or +none at all. When it is called with an argument, the argument is taken to be t +he template content. When it is called without an argument, the template +content is read from a filehandle. If the DATA filehandle is found in the call +ing package, the template content is read from it. Else if the DATA fileha +ndle is found in main::, the template content is read from there. Otherwise, t +he template content is read from the ARGV filehandle. =item fill() This method expects a single hashref as an argument. The keys of the h +ash referred to by the hashref should coincide with the placeholder names +and the values should be the data to be substituted (or references to the data +.) If a value is found to be a reference, it will be called if it is a code re +ference, dereferenced and joined with the defined list separator if it is a ref +erence to an array, or dereferenced if it is a reference to a scalar. Note that +a reference to a hash isn't handled specially at all. An optional argume +nt can be supplied. If it is a true value, the template object's content will + be replaced with the result of filling in its placeholders. This can be u +seful for recursively filling templates. =item delimiters() This method takes 0, 1, or 2 arguments. When called with at least one argument, it sets the delimiters used to define placeholders in th +e template. If there is exactly one argument, it is assumed to be a refe +rence to an array containing the left and right delimiters in that order. When +called with two arguments, they are assumed to be the left and right delimite +rs in that order. When called with no arguments no attempt is made to set t +he delimiters. This method returns a reference to an array containing the + left and right delimiters in that order. The default delimiters are '<%' and '% +>' but feel free to change them; that's what this method is here for. =item list_separator() This method takes either 0 arguments or exactly one argument. If it is called with an argument, the list separator value is set to t +hat value. It returns the list separator value. By default, the list separ +ator is a newline ("\n"). The list separator is a string which is used to separa +te the values when a placeholder is filled with an array. =item tags() This method takes no arguments. It returns a list of the placeholder n +ames used in the template. =back =head1 IMPORTANT NOTE Placeholder names must not have leading/trailing whitespace. Whitespac +e should be avoided in delimiters too. =head1 LIMITATIONS SimpleTemplate reads the whole template into memory. If your templates + are huge, it will be a memory hog. This helps to keep SimpleTemplate SIMPL +E! =head1 BUGS Yeah, right! As if... Look - this module is just too simple to have bu +gs. But, if you find any anyway, please let me know at perlmonks.org. =head1 AUTHOR sauoq =head1 SEE ALSO L<perl>. =cut |