]> git.imager.perl.org - bse.git/blame - site/cgi-bin/modules/Squirrel/Template.pm
0.12_09 commit
[bse.git] / site / cgi-bin / modules / Squirrel / Template.pm
CommitLineData
41b9d8ec
TC
1package Squirrel::Template;
2use vars qw($VERSION);
3use strict;
08123550 4use Carp qw/cluck confess/;
41b9d8ec 5
7928764a 6$VERSION="0.05";
41b9d8ec
TC
7
8sub new {
9 my ($class, %opts) = @_;
10
11 return bless \%opts, $class;
12}
13
08123550 14sub low_perform {
41b9d8ec
TC
15 my ($self, $acts, $func, $args, $orig) = @_;
16
17 my $fmt;
7928764a 18 if ($acts->{_format} && $args =~ s/\|(\S+)\s*$//) {
41b9d8ec
TC
19 $fmt = $1;
20 }
08123550 21 $args = '' unless defined $args;
41b9d8ec
TC
22
23 if (exists $acts->{$func}) {
41b9d8ec 24 $args =~ s/^\s+|\s+$//g;
7928764a
TC
25 my $value;
26 my $action = $acts->{$func};
27 if (ref $action) {
28 if (ref $action eq 'CODE') {
29 $value = $action->($args, $acts, $func, $self);
30 defined $value or return $orig;
31 }
32 elsif (ref $action eq 'ARRAY') {
33 my ($code, @params) = @$action;
34 $value = $code->(@params, $args, $acts, $func, $self);
35 }
36 else {
37 return $orig;
38 }
39 }
40 else {
41 $value = $action;
42 }
08123550 43 return unless defined $value;
41b9d8ec
TC
44 return $fmt ? $acts->{_format}->($value, $fmt) : $value;
45 }
46 for my $match (keys %$acts) {
47 if ($match =~ m(^/(.+)/$)) {
48 my $re = $1;
49 if ($func =~ /$re/) {
50 $args =~ s/^\s+|\s+$//g;
51 my $value = $acts->{$match}->($func, $args);
08123550
TC
52 #defined $value
53 # or return "** function $func $args returned undef **";
54 if (defined($value) && $fmt) {
55 $value = $acts->{_format}->($value, $fmt);
56 }
57 return $value;
41b9d8ec
TC
58 }
59 }
60 }
61 if ($func eq 'summary') {
62 my $size = 80;
63 my $temp = $args;
64 $temp =~ s/^\s+|\s+$//g;
65 $size = $1 if $temp =~ s/^(\d+)\s+//;
66 my ($newfunc, $newargs) = split /\s+/, $temp, 2;
67 $newargs = '' if !defined $newargs;
68 if (exists $acts->{$newfunc}
ca9aa2bf 69 and defined(my $value = $self->perform($acts, $newfunc, $newargs))) {
41b9d8ec
TC
70 # work out a summary
71 return $value if length($value) < $size;
72 $value = substr($value, 0, $size);
73 $value =~ s/\s+\S*$/.../;
74 return $value;
75 }
76 # otherwise fall through
77 }
08123550 78
41b9d8ec
TC
79 return $self->{verbose} ? "** unknown function $func **" : $orig;
80}
81
08123550
TC
82sub perform {
83 my ($self, $acts, $func, $args, $orig) = @_;
84
85 $args = '' unless defined $args;
86
87 #print STDERR "perform $func $args\n";
88 my $value = $self->low_perform($acts, $func, $args, $orig);
89
90 unless (defined $value) {
91 cluck "** undefined value returned by $func $args **";
92 }
93
94 return $value;
95}
96
41b9d8ec
TC
97sub iterate {
98 my ($self, $name, $args, $input, $sep, $acts, $orig) = @_;
99
100 $args = '' unless defined $args;
101 $sep = '' unless defined $sep;
102
103 if (my $entry = $acts->{"iterate_$name"}) {
104 $args =~ s/^\s+|\s+$//g;
7928764a
TC
105
106 my $reset = $acts->{"iterate_${name}_reset"};
107 my ($resetf, @rargs);
108 if ($reset) {
109 if (ref $reset eq 'ARRAY') {
110 ($resetf, @rargs) = @$reset;
111 }
112 else {
113 $resetf = $reset;
114 }
115 }
116
117 my ($entryf, @eargs);
118 if (ref $entry eq 'ARRAY') {
119 ($entryf, @eargs) = @$entry;
120 }
121 else {
122 $entryf = $entry;
123 }
124
125 $resetf->(@rargs, $args, $acts, $name, $self) if $resetf;
41b9d8ec 126 my $result = '';
7928764a 127 while ($entryf->(@eargs, $name, $args)) {
41b9d8ec
TC
128 $result .= $self->replace_template($sep, $acts) if length $result;
129 $result .= $self->replace_template($input, $acts);
130 }
131 return $result;
132 }
133 else {
134 return $self->{verbose} ? "** No iterator $name **" : $orig;
135 }
136}
137
138sub cond {
139 my ($self, $name, $args, $true, $false, $acts, $orig) = @_;
140
edc5d096
TC
141 my $result =
142 eval {
143 if (exists $acts->{"if$name"}) {
08123550 144 my $cond = $self->low_perform($acts, "if$name", $args, '');
7928764a 145 return $cond ? $true : $false;
edc5d096
TC
146 }
147 elsif (exists $acts->{lcfirst $name}) {
08123550 148 my $cond = $self->low_perform($acts, lcfirst $name, $args, '');
7928764a 149 return $cond ? $true : $false;
edc5d096
TC
150 }
151 else {
152 return $orig;
153 }
154 };
7928764a
TC
155 if ($@) {
156 my $msg = $@;
157 $msg =~ /^ENOIMPL\b/
158 and return $orig;
ca9aa2bf 159 print STDERR "Eval error in cond: $msg\n";
7928764a
TC
160 $msg =~ s/([<>&])/"&#".ord($1).";"/ge;
161 return "<!-- ** $msg ** -->";
162 }
163
edc5d096 164 return $result;
41b9d8ec
TC
165}
166
ca9aa2bf
TC
167sub tag_param {
168 my ($params, $arg) = @_;
169
170 exists $params->{$arg} or return '';
171
172 $params->{$arg};
173}
174
41b9d8ec
TC
175sub replace_template {
176 my ($self, $template, $acts, $iter) = @_;
177
178 defined $template
179 or confess "Template must be defined";
180
181 # add any wrappers
7928764a 182 my %params;
41b9d8ec
TC
183 if ($self->{template_dir}) {
184 my $wrap_count = 0;
7928764a 185 while ($template =~ /^(\s*<:\s*wrap\s+(\S+?)(?:\s+(\S.*?))?:>)/i) {
41b9d8ec 186 my $wrapper = "$self->{template_dir}/$2";
7928764a
TC
187 unless (-e $wrapper) {
188 print STDERR "WARNING: Unknown wrap name: $wrapper\n";
189 last;
190 }
191 unless (++$wrap_count < 10) {
192 print STDERR "WARNING: Exceeded wrap count trying to load $wrapper\n";
193 last;
194 }
195 my $params = $3;
41b9d8ec
TC
196 if (open WRAPPER, "< $wrapper") {
197 my $wraptext = do { local $/; <WRAPPER> };
198 close WRAPPER;
199 $template = substr($template, length $1);
200 $wraptext =~ s/<:\s*wrap\s+here\s*:>/$template/i
201 and $template = $wraptext
202 or last;
ca9aa2bf
TC
203
204 unless (defined $params) {
205 while ($params =~ s/^\s*(\w+)\s*=>\s*\"([^\"]+)\"//
206 || $params =~ s/^\s*(\w+)\s*=>\s*([^\s,]+)//) {
207 $params{$1} = $2;
208 $params =~ s/\s*,\s*//;
209 }
210 $params =~ /^\s*$/
211 or print STDERR "WARNING: Extra data after parameters '$params'\n";
7928764a 212 }
7928764a
TC
213 }
214 else {
215 print "ERROR: Unable to load wrapper $wrapper: $!\n";
41b9d8ec
TC
216 }
217 }
218 }
219
ca9aa2bf
TC
220 $acts->{param} = [ \&tag_param, \%params ];
221
41b9d8ec
TC
222 # the basic iterator
223 if ($iter &&
224 (my ($before, $row, $after) =
225 $template =~ m/^(.*)
226 <:\s+iterator\s+begin\s+:>
227 (.*)
228 <:\s+iterator\s+end\s+:>
229 (.*)/sx)) {
230 until ($iter->EOF) {
231 my $temp = $row;
232 $temp =~ s/(<:\s*(\w+)(?:\s+([^:]*?))\s*:>)/ $self->perform($acts, $2, $3, $1) /egx;
233 $before .= $temp;
234 }
235 $template = $before . $after;
236 }
237
238 # more general iterators
239 $template =~ s/(<:\s*iterator\s+begin\s+(\w+)(?:\s+([^:]*))?\s*:>
240 (.*?)
241 (?:
242 <:\s*iterator\s+separator\s+\2\s*:>
243 (.*?)
244 ) ?
245 <:\s*iterator\s+end\s+\2\s*:>)/
246 $self->iterate($2, $3, $4, $5, $acts, $1) /segx;
247
248 # conditionals
249 my $nesting = 0; # prevents loops if result is an if statement
7928764a 250 1 while $template =~ s/(<:\s*if\s+(\w+)(?:\s+(.*?))?\s*:>
41b9d8ec
TC
251 (.*?)
252 <:\s*or\s+\2\s*:>
253 (.*?)
254 <:\s*eif\s+\2\s*:>)/
255 $self->cond($2, $3, $4, $5, $acts, $1) /sgex
256 && ++$nesting < 5;
7928764a 257 $template =~ s/(<:\s*if(\w+)(?:\s+(.*?))?\s*:>
41b9d8ec
TC
258 (.*?)
259 <:\s*or\s*:>
260 (.*?)
261 <:\s*eif\s*:>)/
262 $self->cond($2, $3, $4, $5, $acts, $1) /sgex;
263
7928764a
TC
264 $template =~ s/(<:\s*(\w+)(?:\s+(.*?))?\s*:>)/
265 $self->perform($acts, $2, $3, $1) /segx;
266
267 # replace any wrap parameters
ca9aa2bf
TC
268 # now done elsewhere
269 #$template =~ s/(<:\s*param\s+(\w+)\s*:>)/
270 # exists $params{$2} ? $params{$2} : $1 /eg;
41b9d8ec
TC
271
272 return $template;
273}
274
275sub show_page {
276 my ($self, $base, $page, $acts, $iter) = @_;
277
278 $acts ||= {};
279
7928764a 280 my $file = $page ? "$base/$page" : $base;
41b9d8ec
TC
281 open TMPLT, "< $file"
282 or die "Cannot open template $file: $!";
283 my $template = do { local $/; <TMPLT> };
284 close TMPLT;
285
286 return $self->replace_template($template, $acts, $iter);
287}
288
2891;
290
291__END__
292
293=head1 NAME
294
295 Squirrel::Template - simple templating system
296
297=head1 SYNOPSIS
298
299=head1 DESCRIPTION
300
301=head1 METHODS
302
303=over 4
304
305=item $templ = Squirrel::Template->new(%opts);
306
307Create a new templating object.
308
309Possible options are:
310
311=over 4
312
313=item verbose
314
315If a tag isn't found in the actions then it is replaced with an error
316message rather than being left in place.
317
318=item template_dir
319
320Used by the wrapper mechanism to find wrapper templates. See
321L<WRAPPING> below.
322
323=back
324
325=item $text = $templ->show_page($base, $template, $acts, $iter)
326
327Performs template replacement on the text from the file $template in
328directory $base.
329
330=item $text = $templ->replace_template($intext, $acts, $iter)
331
332Performs template replacement on $intext.
333
334=back
335
336=head1 TEMPLATES
337
338=over 4
339
340=item <: name args :>
341
342Replaced with $acts->{name}->(args)
343
344=item <: iterator begin name args :> text <: iterator separator name :> separator <: iterator end name :>
345
346Replaced with repeated templating of text separated by separator while
347$acts->{iterator_name}->($args, $name) is true.
348
349
350=item <: iterator begin name args :> text <: iterator end name :>
351
352Replaced with repeated templating of text while
353$acts->{iterate_name}->($args, $name) is true.
354
355This may be nested or repeated.
356
357=item <: iterator begin :> text <: iterator end :>
358
359Replaced with repeated templating of text while $iter->EOF is true.
360
361=item <: ifname args :> true <: or :> false <: eif :>
362
363Emits true if $acts->{ifname}->($args) is true, otherwise the false text.
364
365=item <: if name args :> true <: or name :> false <: eif name :>
366
367Emits true if $acts->{ifname}->($args) is true, otherwise the false text.
368
369Has the advantage that it can be nested (the other form doesn't
370support nesting - this isn't a proper parser.
371
372=back
373
374=head1 WRAPPING
375
376If you define the template_dir option when you create your templating
377object, then a mechnism to wrap the current template with another is
378enabled.
379
380For the wrapping to occur:
381
382=over 4
383
384=item *
385
386The template specified in the call to replace_template() or
387show_page() needs to start with:
388
389<: wrap I<templatename> :>
390
391=item *
392
393The template specified in the <: wrap ... :> tag must exist in the
394directory specified by the I<template_dir> option.
395
396=item *
397
398The template specified in the <: wrap ... :> tag must contain a:
399
400 <: wrap here :>
401
402tag.
403
404=back
405
406The current template text is then replaced with the contents of the
407template specified by I<templatename>, with the <: wrap here :>
408replaced by the original template text.
409
410This is then repeated for the new template text.
411
412=head1 SPECIAL ACTIONS
413
414So far there's just one:
415
416=over 4
417
418=item _format
419
420If the _format action is defined in your $acts then if a function tag
421has |text at the end of it then the function is evaluated, and the
422resulting text and the text after the | is passed to the format
423function.
424
425=back
426
427=head1 SEE ALSO
428
429 Squirrel::Row(3p), Squirel::Table(3p)
430
431=cut