/[gedafe]/trunk/lib/perl/Text/CPPTemplate.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/lib/perl/Text/CPPTemplate.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (hide annotations)
Mon Feb 14 18:52:26 2005 UTC (19 years, 2 months ago) by dpavlin
File size: 10084 byte(s)
import of Gedafe 1.2.2

1 dpavlin 1 # Text::CPPTemplate 0.3
2     # copyright (c) 2000, ETH Zurich
3     # released under the GNU General Public License
4    
5     package Text::CPPTemplate;
6    
7     use strict;
8    
9     use vars qw($VERSION);
10     $VERSION = 0.3;
11    
12     =head1 NAME
13    
14     Text::CPPTemplate - CPP-Style Templates
15    
16     =head1 SYNOPSIS
17    
18     use Text::CPPTemplate;
19    
20     my $templ = new Text::CPPTemplate('/var/web/templates','.html');
21    
22     print $templ->template({
23     PAGE => 'index',
24     ELEMENT => 'header',
25     TITLE => 'Test'
26     });
27    
28     =head1 DESCRIPTION
29    
30     CPPTemplate is a templating system using a CPP-style (C Pre-Processor) syntax.
31     CPPTemplate is quite fast so that it can be used in online Applications such
32     as CGI scripts to separate program the code from the HTML. CPPTemplate is not
33     HTML specific, so it can be used for other applications. For performance
34     reasons, the files containing the templates are read only once and are cached
35     for further use. This is especially handy when working with long running
36     scripts which use the same template over and over again. Apache mod_perl is
37     such an environment.
38    
39     An application can use a large number of templates. They could for example represent
40     different parts of output generated by the aplication.
41     Each template can contain variables and CPP style if-then-else structures.
42     When the template gets activated, all the variables will get substituted
43     and the if-then-else structures will get processed.
44    
45     =head1 FILE NAMES
46    
47     When you activate a template, you do not specify a file-name, but only
48     variables. Based on the contents of some special variables, CPPTemplate will try
49     to load an apropriate template file from disk. It tries to do this using a number of
50     different file-names. The first one to exist will be used.
51     A directory where the templates reside and a suffix have to be specified
52     with the C<new> method. The following list shows which variables cause CPPTemplate to
53     look for which files:
54    
55     =over 4
56    
57     =item *
58    
59     I<PAGE>B<_>I<ELEMENT> (I<PAGE> and I<ELEMENT> are variables)
60    
61     =item *
62    
63     I<ELEMENT>
64    
65     =item *
66    
67     I<PAGE>
68    
69     =item *
70    
71     C<default> (as is, not a variable)
72    
73     =back
74    
75     In addition, if I<THEME> is specified, the template will be first searched in
76     the subdirectory specified by that variable of the templates directory.
77    
78     In the example given in L<SYNOPSIS>, the following files will be
79     opened in turn until one is found to exist (in the directory F</var/web/templates>):
80     F<index_header.html>, F<header.html>, F<index.html> and F<default.html>.
81    
82     =head1 VARIABLE SUBSTITUTION
83    
84     Variables are marked C<##var##> in the templates. If no variable is found with
85     that specified name, the ##var## text remains unchanged.
86    
87     =head1 CPP-STYLE DIRECTIVES
88    
89     "MiniCPP" directives permit the selection of parts of the template based on
90     some condition. The language is very very basic, it seems to be good-enough
91     for most applications. The following directives are supported:
92    
93     =over 4
94    
95     =item C<// comment>
96    
97     The whole line is removed from the output
98    
99     =item C<#ifdef VAR>
100    
101     If variable VAR is defined, the following text will be selected.
102    
103     =item C<#if expr>
104    
105     If the expression C<expr> evaluates to true (see L<"EXPRESSIONS">), the
106     following text will be selected. You can use substitutions in the expression
107     with the syntax '##VAR##'.
108    
109     =item C<#elif expr>
110    
111     If the previous C<#if> (or C<#elif>) expression was false, evaluate this
112     C<expr> and if true select the following text.
113    
114     =item C<#else>
115    
116     If the previous C<#if> (or C<#elif>) expression was false, select the following text.
117    
118     =item C<#endif>
119    
120     Ends an C<#ifdef> or an C<#if>.
121    
122     =back
123    
124     Note that these elements can be nested.
125    
126     The newlines will be removed unless two consecutive lines without MiniCPP
127     directives are found. Spaces and tabs will be removed from the beginning and
128     the end of each line. Use '\ ' (backslash space) to insert spaces at the
129     beginning or the end of the line.
130    
131    
132     =head1 EXPRESSIONS
133    
134     At the moment only the following expressions are supported (don't laugh :-))
135    
136     =over 4
137    
138     =item A = B
139    
140     If A is equal to B (the text), then the expression is true.
141    
142     =item A ~ B
143    
144     Match A against the regular expression (perl) B. True if it does match, false
145     otherwise.
146    
147     =back
148    
149     =head1 EXAMPLE
150    
151     #if ##ELEMENT## = ruler
152     <HR>
153     #elif ##ELEMENT## = buttons
154     #ifdef ADD_URL
155     <A href="##ADD_URL##">Add</A>
156     #endif
157     #ifdef PREV_URL
158     <A href="##PREV_URL##">Prev</A>
159     #endif
160     #ifdef NEXT_URL
161     <A href="##NEXT_URL##">Next</A>
162     #endif
163     </P>
164     #endif
165    
166    
167     =head1 PER-METHOD DOCUMENTATION
168    
169     =over 4
170    
171     =cut
172    
173     ######## MINI CPP #########
174    
175     sub _process_if
176     {
177     my $state= shift;
178     my $expr = shift;
179    
180     my $last_state = $state->[$#$state];
181     if($expr) {
182     push @$state, $last_state;
183     }
184     else {
185     push @$state, 0;
186     }
187     }
188    
189     sub _substitute
190     {
191     $_ = shift;
192     my $vars = shift;
193     my $val;
194     s/##(\w+)##/$val=$vars->{$1};defined $val?$val:"##$1##"/ge;
195     return $_;
196     }
197    
198     sub _eval_expr
199     {
200     my $expr = shift;
201     my $vars = shift;
202    
203     $expr =~ s/^\s+//; $expr =~ s/\s+$//;
204     if($expr =~ /^(.+?)\s*=\s*(.*)$/) {
205     my $a = _substitute($1, $vars);
206     my $b = _substitute($2, $vars);
207     #print "<BR>@@@ $a eq $b ?\n";
208     return $a eq $b;
209     }
210     elsif($expr =~ /^(.+?)\s*~\s*(.*)$/) {
211     my $a = _substitute($1, $vars);
212     my $b = _substitute($2, $vars);
213     #print "<BR>@@@ $a ~ $b ?\n";
214     return ($a =~ /$b/);
215     }
216     else {
217     return $expr;
218     }
219     }
220    
221     sub _mini_cpp
222     {
223     my $self = shift;
224     my $in = shift;
225     my $vars = shift;
226     my $out = '';
227    
228     my @state = (1);
229     my $line;
230     my $next_linefeed=0;
231     foreach $line (@$in) {
232     #print "@@@@@@ |".join('',@state)."| $line\n";
233     if($line !~ /^#[a-z]/) { # data
234     if($state[$#state]==1) {
235     $out .= "\n" if $next_linefeed;
236     $out .= _substitute($line,$vars);
237     $next_linefeed=1;
238     }
239     next;
240     }
241     $next_linefeed=0;
242     if($line =~ /^#endif/) { # #endif
243     if($#state<=0) {
244     $out .= "\n!!! SYNTAX ERROR: UNEXPECTED #endif !!!\n";
245     }
246     else {
247     $#state--; # fast pop :-)
248     }
249     next;
250     }
251     if($line =~ /^#else/) { # #else
252     if($#state<=0) {
253     $out .= "\n!!! SYNTAX ERROR: UNEXPECTED #else !!!\n";
254     }
255     else {
256     next unless $state[$#state-1];
257     push @state, pop @state ? 0 : 1;
258     }
259     next;
260     }
261     if($line =~ /^#elif\s+(.+)$/) { # #elif
262     if($#state<=0) {
263     $out .= "\n!!! SYNTAX ERROR: UNEXPECTED #elif !!!\n";
264     }
265     else {
266     next unless $state[$#state-1];
267     if($state[$#state]) {
268     $state[$#state] = 2;
269     }
270     else {
271     $#state--;
272     _process_if(\@state, _eval_expr($1,$vars));
273     }
274     }
275     next;
276     }
277     if($line =~ /^#if\s+(.+?)$/) { # #if
278     _process_if(\@state, $state[$#state] ? _eval_expr($1,$vars) : 0);
279     next;
280     }
281     if($line =~ /^#ifdef\s+(\w+)/) { # #ifdef
282     my $def = $vars->{$1};
283     _process_if(\@state, ((defined $def) and ($def ne '')));
284     next;
285     }
286    
287     $out .= "\n!!! SYNTAX ERROR: UNRECOGNIZED TOKEN !!!\n";
288     }
289    
290     return $out;
291     }
292    
293     ####### TEMPLATE #######
294    
295     sub _slurp_file
296     {
297     my $self = shift;
298     my $file = shift;
299    
300     #print "<BR>reading $file...<BR>\n";
301    
302     my $RS_bak = $/;
303     undef $/;
304     open(SLURP, "<$file") or do {
305     return "\n!!! ERROR: couldn't find $file !!!\n";
306     };
307     my $data = <SLURP> || '';
308     close(SLURP);
309     $/=$RS_bak;
310    
311     # replace '\ ' to a special unprobable string
312     $data =~ s/\\ /<<SpAcE>>/g;
313    
314     # trim
315     $data =~ s/^[ \t]+//gm;
316     $data =~ s/[ \t]+$//gm;
317    
318     # replace the forced-space string to a space
319     $data =~ s/<<SpAcE>>/ /g;
320    
321     # strip comments
322     $data =~ s|^//.*||gm;
323    
324     my @ldata = split("\n",$data,-1);
325     return \@ldata;
326     }
327    
328     sub _get_template
329     {
330     my $self = shift;
331     my $name = shift;
332    
333     my $tdir = $self->{dir};
334     my $suff = $self->{suff};
335    
336     if(!exists $self->{cache}{$name}) {
337     my @names = split /\|/, $name;
338     my $ok = 0;
339     my $filename;
340     foreach (@names) {
341     $filename = "$tdir/$_$suff";
342     if(-r $filename) {
343     $self->{cache}{$name} = $self->_slurp_file($filename);
344     $ok = 1;
345     last;
346     }
347     }
348     if(!$ok) {
349     return ["!!! ERROR: couldn't find a template for $name (t-dir: $tdir) !!!"];
350     }
351     }
352    
353     return $self->{cache}{$name};
354     }
355    
356     =item C<new($templates_dir, $suffix)>
357    
358     Create a new CPPTemplate object. C<$templates_dir> is the directory where the templates
359     are stored and C<$suffix> is a text to append to the file-names.
360    
361     =cut
362    
363     sub new {
364     my $proto = shift;
365     my $tdir = shift;
366     my $suff = shift;
367    
368     my $class = ref($proto) || $proto;
369     my $self = {};
370    
371     $self->{cache} = {};
372     $self->{dir}=$tdir;
373     $self->{suff}=$suff;
374    
375     bless($self,$class);
376     return $self;
377     }
378    
379     =item C<template(\%vars)>
380    
381     Return a processed template. C<\%vars> is a hashref containing the variables used for building
382     the file-name, for the substitutions and the CPP-style directives.
383    
384     =cut
385    
386     sub template($)
387     {
388     my $self = shift;
389     my $vars = shift;
390    
391     my $name = $vars->{PAGE}.'_'.$vars->{ELEMENT}.'|'.
392     $vars->{ELEMENT}.'|'.
393     $vars->{PAGE}.'|default';
394    
395     # themes:
396     if(defined $vars->{THEME}) {
397     $name = $vars->{THEME}.'/'.$vars->{PAGE}.'_'.$vars->{ELEMENT}.'|'.
398     $vars->{THEME}.'/'.$vars->{ELEMENT}.'|'.
399     $vars->{THEME}.'/'.$vars->{PAGE}.'|'.
400     $vars->{THEME}.'/default'.'|'.
401     $name;
402     }
403     my $templ = $self->_get_template($name);
404     my $out = $self->_mini_cpp($templ, $vars);
405     chomp $out;
406     return $out;
407     }
408    
409     1;
410    
411     =back
412    
413     =head1 SEE ALSO
414    
415     Text::TagTemplate(3)
416    
417     =head1 COPYRIGHT
418    
419     Copyright (c) 2000 ETH Zurich, All rights reserved.
420    
421     =head1 LICENSE
422    
423     This program is free software; you can redistribute it and/or modify
424     it under the terms of the GNU General Public License as published by
425     the Free Software Foundation; either version 2 of the License, or
426     (at your option) any later version.
427    
428     This program is distributed in the hope that it will be useful,
429     but WITHOUT ANY WARRANTY; without even the implied warranty of
430     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
431     GNU General Public License for more details.
432    
433     You should have received a copy of the GNU General Public License
434     along with this program; if not, write to the Free Software
435     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
436     This library is free software; you can redistribute it and/or
437     modify it under the terms of the GNU Lesser General Public
438     License as published by the Free Software Foundation; either
439     version 2.1 of the License, or (at your option) any later version.
440    
441     =head1 AUTHOR
442    
443     David Schweikert <dws@ee.ethz.ch>,
444     Tobi Oetiker <oetiker@ee.ethz.ch>

  ViewVC Help
Powered by ViewVC 1.1.26