1 #####################################################################
3 # Artemus - Template Toolkit
5 # Copyright (C) 2000/2008 Angel Ortega <angel@triptico.com>
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #####################################################################
32 $Artemus::VERSION
= '4.1.1-dev';
38 Artemus - Template Toolkit
46 "copyright" => 'Copyright 2002', # normal variable
47 "number" => 100, # another
48 "about" => '{-copyright} My Self', # can be nested
49 "link" => '<a href="$0">$1</a>' # can accept parameters
52 # functions as templates
54 "rnd" => sub { int(rand(100)) }, # normal function
55 "sqrt" => sub { sqrt($_[0]) } # can accept parameters
58 # create a new Artemus instance
59 $ah = new Artemus( "vars" => \%vars, "funcs" => \%funcs );
62 $out = $ah->process('Click on {-link|http://my.page|my page}, {-about}');
63 $out2 = $ah->process('The square root of {-number} is {-sqrt|{-number}}');
67 Artemus is yet another template toolkit. Though it was designed
68 to preprocess HTML, it can be used for any task that involves
69 text substitution. These templates can be plain text, text with
70 parameters and hooks to real Perl code. This document describes
71 the Artemus markup as well as the API.
73 You can download the latest version of this package and get
74 more information from its home page at
76 http://triptico.com/software/artemus.html
78 =head1 THE ARTEMUS MARKUP
80 =head2 Simple templates
82 The simplest Artemus template is just a text substitution. If
83 you set the 'about' template to '(C) 2000/2002 My Self', you
84 can just write in your text
86 This software is {-about}.
88 and found it replaced by
90 This software is (C) 2000/2002 My Self.
92 Artemus templates can be nestable; so, if you set another
93 template, called 'copyright' and containing '(C) 2000/2002', you
94 can set 'about' to be '{-copyright} My Self', and obtain the
95 same result. Though they can be nested nearly ad-infinitum, making
96 circular references is unwise.
98 =head2 Templates with parameters
100 This wouldn't be any cool if templates where just text substitutions.
101 But you can create templates that accept parameters just by including
102 $0, $1, $2... marks inside its content. This marks will be replaced
103 by the parameters used when inserting the call.
105 So, if you create the 'link' template containing
107 <a href = "$0">$1</a>
109 you can insert the following call:
111 {-link|http://triptico.com|Angel Ortega's Home Page}
113 As you can see, you use the | character as a separator
114 among the parameters and the template name itself.
116 =head2 Perl functions as templates
118 Anything more complicated than this would require the definition
119 of special functions provided by you. To do it, you just add
120 templates to the 'funcs' hash reference when the Artemus object
121 is created which values are references to Perl functions. For
122 example, you can create a function returning a random value
125 $funcs{'rnd'} = sub { int(rand(100)) };
127 And each time the {-random} template is found, it is evaluated
128 and returns a random number between 0 and 99.
130 Functions also can accept parameters; so, if you define it as
132 $funcs{'rnd'} = sub { int(rand($_[0])) };
134 then calling the template as
138 will return each time it's evaluated a random value between 0 and 499.
140 =head2 Aborting further execution from a function
142 If the I<abort-flag> argument is set to a scalar reference when creating
143 the Artemus object, template processing can be aborted by setting
144 this scalar to non-zero from inside a template function.
146 =head2 Caching templates
148 If a template is expensive or time consuming (probably because it
149 calls several template functions that take very much time), it can be
150 marked as cacheable. You must set the 'cache-path' argument for
151 this to work, and include the following special Artemus code
156 where I<number> is a number of days (or fraction of day) the
157 cache will remain cached before being re-evaluated. Individual
158 template functions cannot be cached; you must wrap them in a
159 normal template if need it.
161 =head2 Documenting templates
163 Artemus templates can contain documentation in Perl's POD format.
164 This POD documentation is stripped each time the template is evaluated
165 unless you create the Artemus object with the I<contains-pod> argument
168 See http://www.perldoc.com/perl5.8.0/pod/perlpod.html and
169 http://www.perldoc.com/perl5.8.0/pod/perlpodspec.html for information
170 about writing POD documentation.
172 =head2 Unresolved templates
174 If a template is not found, it will be replaced by its name (that is,
175 stripped out of the {- and } and left there). Also, the names of the
176 unresolved templates are appended to an array referenced by the
177 I<unresolved> argument, if one was defined when the Artemus object
180 =head2 Predefined templates
187 {-if|condition|text_if_true|text_unless_true}
189 If I<condition> is true, this template returns I<text>, or nothing
190 otherwise; in the 3 argument version, returns I<text_if_true> or
191 I<text_unless_true>. A condition is true if is not zero or the empty
192 string (the same as in Perl).
196 This is an alias for the I<if> template provided for backwards-compatibility.
201 {-ifeq|term1|term2|text}
202 {-ifeq|term1|term2|text_if_true|text_unless_true}
204 If I<term1> is equal to I<term2>, this template returns I<text>, or nothing
205 otherwise. in the 4 argument version, returns I<text_if_true> or
210 {-ifneq|term1|term2|text}
212 If I<term1> is not equal to I<term2>, this template returns I<text>, or
217 This is an alias for the I<ifeq> template provided for backwards-compatibility.
225 This functions add or substract the values and returns the result.
227 =item B<gt>, B<lt>, B<eq>
233 This functions compare if I<value1> is greater-than, lesser-than or equal to
234 I<value2>. Meant primarily to use with the I<if> template.
238 {-random|value1|value2|...}
240 This function returns randomly one of the values sent as arguments. There can
241 any number of arguments.
245 {-and|value_or_condition_1|value_or_condition_2}
247 If both values are true or defined, returns I<value_or_condition_2>; otherwise,
248 returns the empty string.
252 {-or|value_or_condition_1|value_or_condition_2}
254 If I<value_or_condition_1> is true or defined, returns it; otherwise, if
255 I<value_or_condition_2> is true or defined, returns it; otherwise, returns
262 Returns the negation of I<condition>.
266 {-set|template_name|value}
268 Assigns a value to a template. Same as setting a value from the 'vars'
269 argument to B<new>, but from Artemus code.
271 If you must change a variable from inside an I<if> directive, don't
272 forget to escape the I<set> directive, as in
274 {-ifeq|{-user}|admin|\{-set\|powers\|EVERYTHING\}}
276 IF you don't escape it, the I<powers> variable will be inevitably set
281 {-foreach|list:of:colon:separated:values|output_text|separator}
283 Iterates the list of colon separated values and returns I<output_text>
284 for each one of the values, separating each of them with I<separator>
285 (if one is defined). Each element itself can be a list of comma
286 separated values that will be split and assigned to the $0, $1... etc
287 parameters set to I<output_text>. For example, to create a I<select>
290 <select name = 'work_days'>
291 {-foreach|Monday,1:Tuesday,2:Wednesday,3:Thursday,4:Friday,5|
292 <option value = '\$1'>\$0</option>
296 Remember to escape the dollar signs to avoid being expanded too early,
297 and if the I<output_text> include calls to other Artemus templates,
298 to escape them as well.
304 Marks a template as cacheable and sets its cache time. See above.
310 Returns current Artemus version.
316 If you set these templates, they will be appended (\BEGIN) and
317 prepended (\END) to the text being processed.
323 Escaping has been briefly mentioned above; this is a way to avoid
324 prematurely expanding and executing Artemus templates, and a direct
325 derivative of the simple text substitution approach of the Artemus
328 To escape an Artemus template call you must escape ALL characters
329 that has special meaning to the uber-simple Artemus parser (that is,
330 the opening and closing braces, the pipe argument separator and
331 the optional dollar prefixes for arguments). If you nest some
332 directives (for example, two I<foreach> calls), you must
333 double-escape everything. Yes, this can get really cumbersome.
335 =head1 FUNCTIONS AND METHODS
342 [ "vars" => \%variables, ]
343 [ "funcs" => \%functions, ]
344 [ "inv-vars" => \%inverse_variables, ]
345 [ "include-path" => $dir_with_templates_in_files, ]
346 [ "cache-path" => $dir_to_store_cached_templates, ]
347 [ "abort-flag" => \$abort_flag, ]
348 [ "unresolved" => \@unresolved_templates, ]
349 [ "use-cr-lf" => $boolean, ]
350 [ "contains-pod" => $boolean, ]
351 [ "paragraph-separator" => $separator, ]
352 [ "strip-html-comments" => $boolean, ]
353 [ "AUTOLOAD" => \&autoload_func ]
356 Creates a new Artemus object. The following arguments (passed to it
357 as a hash) can be used:
363 This argument must be a reference to a hash containing
364 I<template> - I<content> pairs.
368 This argument must be a reference to a hash containing
369 I<template name> - I<code reference> pairs. Each time one of these
370 templates is evaluated, the function will be called with
371 the template parameters passed as the function's arguments.
375 This argument must be a reference to a hash containing
376 I<text> - I<content> pairs. Any occurrence of I<text> will be
377 replaced by I<content>. They are called 'inverse variables'
378 because they use to store variables that expand to Artemus
379 markup, but can contain anything. This is really a plain
380 text substitution, so use it with care (B<NOTE>: this
381 option is disabled by now until it works correctly).
383 =item I<include-path>
385 If this string is set, it must point to a readable directory
386 that contains templates, one on each file. The file names
387 will be treated as template names. Many directories can
388 be specified by separating them with colons.
392 If this string is set, it must contain the path to a readable
393 and writable directory where the cacheable templates are cached.
394 See L<Caching templates> for further information.
398 This argument must be a reference to a scalar. When the template
399 processing is started, this scalar is set to 0. Template functions
400 can set it to any other non-zero value to stop template processing.
404 If this argument points to an array reference, it will be filled
405 with the name of any unresolved templates. Each time a template
406 processing is started, the array is emptied.
410 If this flag is set, all lines are separated using CR/LF instead
411 of just LF (useful to generate MSDOS/Windows compatible text files).
413 =item I<contains-pod>
415 If this flag is set, the (possible) POD documentation inside the
416 templates are not stripped-out. Understand this flag as saying
417 'this template has pod as part of its content, so do not strip it'.
418 See L<Documenting templates>.
420 =item I<paragraph-separator>
422 If this argument is set to some string, all empty lines will be
423 substituted by it (can be another Artemus template).
425 =item I<strip-html-comments>
427 If this flag is set, HTML comments are stripped before any
432 If this argument points to a sub reference, the subrutine will
433 be executed when a template is unresolved and its return value used
434 as the final substitution value. Similar to the AUTOLOAD function
435 in Perl standard modules. The unresolved template name will be
436 sent as the first argument.
444 my ($class, %params) = @_;
446 my $a = bless({ %params }, $class);
449 $a->{vars
}->{'\n'} = "\n";
450 $a->{vars
}->{'\BEGIN'} ||= '';
451 $a->{vars
}->{'\END'} ||= '';
452 $a->{vars
}->{'\VERSION'} = $Artemus::VERSION
;
455 $a->{funcs
}->{localtime} = sub { scalar(localtime) };
457 $a->{funcs
}->{if} = sub { $_[0] ?
$_[1] : (scalar(@_) == 3 ?
$_[2] : '') };
458 $a->{funcs
}->{ifelse
} = $a->{funcs
}->{if};
460 $a->{funcs
}->{ifeq
} = sub { $_[0] eq $_[1] ?
$_[2] : (scalar(@_) == 4 ?
$_[3] : '') };
461 $a->{funcs
}->{ifneq
} = sub { $_[0] ne $_[1] ?
$_[2] : (scalar(@_) == 4 ?
$_[3] : '') };
462 $a->{funcs
}->{ifeqelse
} = $a->{funcs
}->{ifeq
};
464 $a->{funcs
}->{add
} = sub { $_[0] + $_[1]; };
465 $a->{funcs
}->{sub} = sub { $_[0] - $_[1]; };
466 $a->{funcs
}->{gt} = sub { $_[0] > $_[1]; };
467 $a->{funcs
}->{lt} = sub { $_[0] < $_[1]; };
468 $a->{funcs
}->{eq} = sub { $_[0] eq $_[1] ?
1 : 0; };
469 $a->{funcs
}->{random
} = sub { $_[rand(scalar(@_))]; };
471 $a->{funcs
}->{and} = sub { ($_[0] && $_[1]) || ''; };
472 $a->{funcs
}->{or} = sub { $_[0] || $_[1] || ''; };
473 $a->{funcs
}->{not} = sub { $_[0] ?
0 : 1; };
475 $a->{funcs
}->{foreach} = sub {
477 my $code = shift || '$0';
478 my $sep = shift || '';
481 my @l = split(/\s*:\s*/, $list);
484 my @e = split(/\s*,\s*/, $l);
486 push(@ret, $a->params($code, @e));
489 return join($sep, @ret);
492 $a->{funcs
}->{set
} = sub { $a->{vars
}->{$_[0]} = $_[1]; return ''; };
494 $a->{funcs
}->{case
} = sub {
500 # if args are odd, the last one is
501 # the 'otherwise' case
502 if (scalar(@_) / 2 != int(scalar(@_) / 2)) {
521 $a->{funcs
}->{env
} = sub { $ENV{$_[0]} || ''; };
524 $a->{_unresolved
} = [];
526 # ensure 'abort-flag' and 'unresolved' point to
527 # appropriate holders
528 $a->{'abort-flag'} ||= \
$a->{_abort
};
529 $a->{unresolved
} ||= \
$a->{_unresolved
};
537 $str = $ah->armor($str);
539 Translate Artemus markup to HTML entities, to avoid being
540 interpreted by the parser.
549 $t =~ s/\|/\|/g;
560 $str = $ah->unarmor($str);
562 Translate back the Artemus markup from HTML entities. This
563 is the reverse operation of B<armor>.
572 $t =~ s/\|/\|/g;
583 $str = $ah->strip($str);
585 Strips all Artemus markup from the string.
593 $t =~ s/{-([-\\\w_ \.]+)[^{}]*}/$1/g;
601 $str = $ah->params($str,@params);
603 Interpolates all $0, $1, $2... occurrences in the string into
604 the equivalent element from @params.
610 my ($ah, $t, @params) = @_;
612 for(my $n = 0; $t =~ /\$$n/; $n++) {
613 $t =~ s/(^|[^\\])\$$n/$1$params[$n]/g;
622 $str = $ah->process($str);
624 Processes the string, translating all Artemus markup. This
625 is the main template processing method. The I<abort-flag> flag and
626 I<unresolved> list are reset on each call to this method.
632 my ($ah, $data) = @_;
635 ${$ah->{'abort-flag'}} = 0;
637 # no unresolved templates by now
638 @
{$ah->{'unresolved'}} = ();
640 # surround with \BEGIN and \END
641 $data = $ah->{'vars'}->{'\BEGIN'} . $data . $ah->{'vars'}->{'\END'};
643 # really do it, recursively
644 $data = $ah->_process_do($data);
646 # finally, convert end of lines if necessary
647 if ($ah->{'use-cr-lf'}) {
648 $data =~ s/\n/\r\n/g;
652 $data =~ s/{%[^}]+}//g;
660 my ($ah, $data, $template_name) = @_;
664 print STDERR
sprintf('Artemus: template="%s", data="%s"',
665 $template_name || 'NONE', $data || ''), "\n";
668 # test if the template includes cache info
669 if ($data =~ s/{-\\CACHE\W([^}]*)}//) {
670 if ($template_name and $ah->{'cache-path'}) {
673 # convert strange chars to :
674 $template_name =~ s/[^\w\d_]/:/g;
676 my ($f) = "$ah->{'cache-path'}/$template_name";
678 if (-r
$f and -M
$f < $cache_time) {
681 $data = join('', <F
>);
689 # strip POD documentation, if any
690 if ($data =~ /=cut/ and not $ah->{'contains-pod'}) {
693 foreach (split("\n", $data)) {
694 unless (/^=/ .. /^=cut/) {
699 $data = join("\n", @d);
702 # strips HTML comments
703 if ($ah->{'strip-html-comments'}) {
704 $data =~ s/<!--.*?-->//gs;
707 # if defined, substitute the paragraphs
708 # with the paragraph separator
709 if ($ah->{'paragraph-separator'}) {
710 $data =~ s/\n\n/\n$ah->{'paragraph-separator'}\n/g;
713 # inverse substitutions
714 # (disabled until it works)
715 # while (my ($i, $v) = each(%{$ah->{'inv-vars'}})) {
716 # $data =~ s/\b$i\b/$v/g;
719 # main function, variable and include substitutions
720 while ($data =~ /{-([^{}\\]*(\\.[^{}\\]*)*)}/s) {
723 # take key and params
724 my ($key, $params) = ($found =~ /^([-\\\w_]+)\|?(.*)$/s);
726 # replace escaped chars
727 $params =~ s/\\{/{/g;
728 $params =~ s/\\}/}/g;
729 $params =~ s/\\\$/\$/g;
734 while (length($params) && $params =~ s/^([^\|\\]*(\\.[^\|\\]*)*)\|?//s) {
744 if (defined $ah->{'vars'}->{$key}) {
745 $text = $ah->{'vars'}->{$key};
746 $text = $ah->params($text, @params);
750 elsif (defined $ah->{'funcs'}->{$key}) {
753 $func = $ah->{'funcs'}->{$key};
754 $text = $func->(@params);
756 # functions can abort further execution
758 if (${$ah->{'abort-flag'}}) {
764 elsif ($ah->{'include-path'}) {
765 foreach my $p (split(/:/, $ah->{'include-path'})) {
766 if (open(INC
, "$p/$key")) {
767 $text = join('', <INC
>);
770 # cache it as a variable
771 $ah->{vars
}->{$key} = $text;
773 $text = $ah->params($text, @params);
782 push(@
{$ah->{'unresolved'}}, $found);
784 if (ref $ah->{'AUTOLOAD'}) {
785 $text = $ah->{'AUTOLOAD'}($found);
792 # if params are not to be cached,
793 # use $key instead of $found
794 $text = $ah->_process_do($text, $found) || '';
796 # make the substitution
797 $data =~ s/{-\Q$found\E}/$text/;
800 # if the template included cache info,
801 # store the result there
803 open F
, '>' . $ah->{'cache-path'} . '/' . $template_name;
815 Angel Ortega angel@triptico.com