Here's a templating system with a twist - it installs directly into your script. This allows you to run your script on a machine without having to install a templating module first. Rather than invent a new templating language, I've used the Mason syntax - or a least as much of it as I could fit into 80 lines of code.

I built this for use in configuration management - generating Apache config files was the main driver.

I'm not sure whether this should be uploaded to CPAN. The templating space seems to be confused enough already.

package Template::MasonLite; use strict; use warnings; use Carp; our $VERSION = '0.9'; my( $nl, $init_sect, $perl_sect, $perl_line, $comp_def, $comp_call, $expression, $literal ); BEGIN { $nl = qr{(?:[ \r]*\n)}; $init_sect = qr{<%init>(.*?)</%init>$nl?}s; $perl_sect = qr{<%perl>(.*?)</%perl>$nl?}s; $perl_line = qr{(?:(?<=\n)|^)%(.*?)(\n|\Z)}s; $comp_def = qr{<%def\s+([.\w+]+)>$nl(.*?)</%def>$nl?}s; $comp_call = qr{<&\s*([\w._-]+)(?:\s*,)?(.*?)&>}s; $expression = qr{<%\s*(.*?)%>}s; $literal = qr{(.*?(\n|(?=<%|<&|\Z)))}; } sub new { return bless $_[0]->_parse($_[1]), $_[0]; } sub new_from_file { return bless $_[0]->_parse_file($_[1]), $_[0]; } sub apply { my $self = shift; return $self->(@_) }; sub _parse_file { my($class, $template) = @_; open my $fh, '<', $template or croak "$!: $template"; sysread $fh, $_, -s $template; return $class->_parse($_); } sub _parse { my($class, $template) = @_; die "No template!\n" unless defined($template); $_ = $template; my(@head, @body, %comp); while(!/\G\Z/sgc) { if (/\G$init_sect/sgc ) { push @head, $1; } elsif(/\G$perl_sect/sgc ) { push @body, $1; } elsif(/\G$perl_line/sgc ) { push @body, $1; } elsif(/\G$comp_def/sgc ) { $comp{$1} = $2; } elsif(/\G$comp_call/sgc ) { push @body, [ 0, "\$comp{'$1'}->apply($2)" ] +; } elsif(/\G$expression/sgc) { push @body, [ 0, $1 ]; } elsif(/\G$literal/sgc ) { push @body, [ 1, $1 ]; } else {/(.*)/sgc && croak "could not parse: '$1'"; } }; while(my($name, $source) = each %comp) { $comp{$name} = $class->new($source); } unshift @head, 'my @r; my %ARGS; %ARGS = @_ unless(@_ % 2);'; push @body, 'return join "", @r'; my $code = join("\n", map { ref($_) ? ( $_->[0] ? _literal($_->[1]) : _expr($_->[1]) ) : $_; } @head, @body); $_ = ''; my $sub = eval "sub { $code }"; croak $@ if $@; return $sub; } sub _expr { "push \@r, $_[0];"; } sub _literal { $_ = shift; s/'/\\'/g; s/\\\n//s; _expr("'$_'"); } # End of Template::MasonLite sub install { my $target = (@_, @ARGV)[0] or die "target filename required"; local($/); my $tm_code = qr{package.*?# End of Template::MasonLite\s*}s; open my $fh, '<', __FILE__ or die "$! - open(" . __FILE__ . ")"; $_ = <$fh>; my($code) = m{^.*?($tm_code)}s; open $fh, '<', $target or die "$! - open($target)"; $_ = <$fh>; s{$tm_code|(?=^__END__$|^__DATA__$|\Z)}{$code}m; open $fh, '>', $target or die "$! - open(>$target)"; print $fh $_; close($fh); } 1; __END__ =head1 NAME Template::MasonLite - add a small templating system to your script =head1 SYNOPSIS To install the template engine (about 80 lines of code) directly into +your script: perl -MTemplate::MasonLite -e Template::MasonLite::install scriptnam +e Then you can define a template in your script: my $template = <<'EOF'; <VirtualHost <% $ARGS{ip_addr} %>> ServerName <% $ARGS{domain_name} %> DocumentRoot /var/www/<% $ARGS{domain_name} %> % if($ARGS{want_cgi}) { ScriptAlias /bin/ /usr/lib/cgi-bin/<% $ARGS{domain_name} %> % } Options Indexes MultiViews\ % if($ARGS{want_ssi}) { Includes\ % } </VirtualHost> EOF And turn it into a template object: my $t = Template::MasonLite->new($template); Finally, call the template's C<apply> method and pass it some data. print $t->apply( ip_addr => '', domain_name => '', want_cgi => 0, want_ssi => 1, ); =head1 DESCRIPTION Template::MasonLite is a templating system that's so small, you can in +clude it directly in your script. This allows you to deploy your script withou +t having to install a templating module first. This is especially useful for g +enerating config files. =head1 INSTALLATION If you have this module installed on your development host, you can co +py the relevant code into your script using this command: perl -MTemplate::MasonLite -e Template::MasonLite::install scriptnam +e You can rerun that command to update the code or to replace it if you' +ve somehow managed to break it. =head1 METHODS =head2 new (string) Constructs a template object from a string. =head2 new_from_file (filename) Constructs a template object from the contents of a file. =head2 apply (arguments ...) Combines supplied arguments with template and returns resulting string +. =head1 TEMPLATE SYNTAX Template::MasonLite implements a small subset of the HTML::Mason templ +ating syntax. Essentially it allows you to embed Perl in your template. =head2 Template Arguments Any arguments passed to the C<apply()> method will be available to you +r template in C<@_> and if an even number of arguments were provided, th +ey will also be available as key=>value pairs in %ARGS. =head2 Expressions You can insert the value of Perl variables or expressions into your te +mplate by enclosing them in C<< <% ... %> >>, eg: ServerName <% $ARGS{domain_name} %> Options <% join ' ', @options %> =head2 Perl Lines Any line which starts with a '%' character will be treated as Perl cod +e. This is especially useful for conditional sections or loops, eg: % if($ARGS{want_cgi}) { ScriptAlias /bin/ /usr/lib/cgi-bin/<% $ARGS{domain_name} %> % } =head2 Perl Sections A number of lines of Perl code can be wrapped in C<< <%perl> ... </%pe +rl> >> tags. Both opening and closing tags must occur at the start of a line +, eg: <%perl> my $domain_name = $ARGS{domain_name} or die "Template requires a 'domain_name' parameter\n"; my $timestamp = localtime; </%perl> You can also use C<< <%init> ... <%/init> >> tags to bracket a block o +f Perl code that is run before all other code - regardless of where it a +ppears in the template. =head2 Embedded Components You can define named subcomponents - templates within your template, u +sing the C<< <%def name> ... </%def> >> tags. The name consists of characters +in the set [\w._-]. <%def .allow_from> % my $allowed = shift; order deny,allow deny from all allow from <% $allow %> </%def> You can call a subcomponent using its name and any arguments in C<< <& ... &> >>, eg: <& .allow_from, '' &> Note the comma after the component name. Subcomponents have a separat +e lexical scope from the main template. Any variables which a component needs t +o see should be passed as arguments. =head2 Suppressing newlines If you end a line with a backslash ('\'), neither the backslash nor th +e following newline will not appear in the output. =head1 HTML::Mason (in)compatibility The syntax elements listed above are the complete syntax supported by +this module. A non-exhaustive list of cool stuff that L<HTML::Mason> can d +o that this module can't includes: C<< <%attr> >>, C<< <%flags> >>, C<< <%cleanup> >>, C<< <%once> >>, C<< <%shared> >>, C<< <%filter> >>, C<< <%doc> >> and C<< <%text> >> sections; escaping; filtering compone +nts; inheritance and other OO things; autohandlers and dhandlers; caching; +$m and $r objects; Apache/mod_perl integration. =head1 BUGS Errors in templates do not always lead to useful error messages. =head1 SEE ALSO If you're looking for a templating tool to build web pages, L<HTML::Ma +son> would be a much better choice than this module. =head1 COPYRIGHT Copyright 2004 Grant McLean E<lt>grantm@cpan.orgE<gt> This library is free software; you can redistribute it and/or modify i +t under the same terms as Perl itself. =cut