1package Text::NeatTemplate;
2$Text::NeatTemplate::VERSION = '0.1101';
3use strict;
4use warnings;
5
6=head1 NAME
7
8Text::NeatTemplate - a fast, middleweight template engine.
9
10=head1 VERSION
11
12version 0.1101
13
14=head1 SYNOPSIS
15
16    use Text::NeatTemplate;
17
18    my $tobj = Text::NeatTemplate->new();
19
20    $result = $tobj->fill_in(data_hash=>\%data,
21			     show_names=>\%names,
22			     template=>$text);
23
24=head1 DESCRIPTION
25
26This module provides a simple, middleweight but fast template engine,
27for when you need speed rather than complex features, yet need more features
28than simple variable substitution.
29
30=head2 Markup Format
31
32The markup format is as follows:
33
34=over
35
36=item {$varname}
37
38A variable; will display the value of the variable, or nothing if
39that value is empty.
40
41=item {$varname:format}
42
43A formatted variable; will apply the formatting directive(s) to
44the value before displaying it.
45
46=item {?varname stuff [$varname] more stuff}
47
48A conditional.  If the value of 'varname' is not empty, this will
49display "stuff value-of-variable more stuff"; otherwise it displays
50nothing.
51
52    {?var1 stuff [$var1] thing [$var2]}
53
54This would use both the values of var1 and var2 if var1 is not
55empty.
56
57=item {?varname stuff [$varname] more stuff!!other stuff}
58
59A conditional with "else".  If the value of 'varname' is not empty, this
60will display "stuff value-of-variable more stuff"; otherwise it displays
61"other stuff".
62
63This version can likewise use multiple variables in its display parts.
64
65    {?var1 stuff [$var1] thing [$var2]!![$var3]}
66
67=item {&funcname(arg1,...,argN)}
68
69Call a function with the given args; the return value of the
70function will be what is put in its place.
71
72    {&MyPackage::myfunc(stuff,[$var1])}
73
74This would call the function myfunc in the package MyPackage, with the
75arguments "stuff", and the value of var1.
76
77Note, of course, that if you have a more complicated function and
78are processing much data, this will slow things down.
79
80=back
81
82=head2 Limitations
83
84To make the parsing simpler (and therefore faster) there are certain
85restrictions in what this module can do:
86
87=over
88
89=item *
90
91One cannot escape '{' '}' '[' or ']' characters.  However, the substitution
92is clever enough so that you may be able to use them inside conditional
93constructs, provided the use does not resemble a variable.
94
95For example, to get a value surrounded by {}, the following
96will not work:
97
98{{$Var1}}
99
100However, this will:
101
102{?Var1 {[$Var1]}}
103
104=item *
105
106One cannot have nested variables.
107
108=item *
109
110Conditionals are limited to testing whether or not the variable
111has a value.  If you want more elaborate tests, or tests on more
112than one value, you'll have to write a function to do it, and
113use the {&function()} construct.
114
115=item *
116
117Function arguments (as given with the {&funcname(arg1,arg2...)} format)
118cannot have commas in them, since commas are used to separate the
119arguments.
120
121=back
122
123=head2 Justification For Existence
124
125When I was writing SQLite::Work, I originally tried using L<Text::Template>
126(my favourite template engine) and also tried L<Text::FillIn>.  Both
127of them had some lovely, powerful features.  Unfortunately, they were
128also relatively slow.  In testing them with a 700-row table, using
129Text::Template took about 15 seconds to generate the report, and using
130Text::FillIn took 45 seconds!  Rolling my own very simple template
131engine cut the time down to about 7 seconds.
132
133The reasons for this aren't that surprising.  Because Text::Template
134is basically an embedded Perl engine, it has to run the interpreter
135on each substitution.  And Text::FillIn has a lot to do, what with being
136very generic and very recursive.
137
138The trade-off for the speed-gain of Text::NeatTemplate is that
139it is quite simple.  There is no nesting or recursion, there are
140no loops.  But I do think I've managed to grab some of the nicer features
141of other template engines, such as limited conditionals, and formatting,
142and, the most powerful of all, calling external functions.
143
144This is a middleweight engine rather than a lightweight one, because
145I needed more than just simple variable substitution, such as one
146has with L<Template::Trivial>.  I consider the trade-off worth it,
147and others might also, so I made this a separate module.
148
149=head1 FORMATTING
150
151As well as simple substitution, this module can apply formatting
152to values before they are displayed.
153
154For example:
155
156{$Money:dollars}
157
158will give the value of the I<Money> variable formatted as a dollar value.
159
160Formatting directives are:
161
162=over
163
164=item alpha
165
166Convert to a string containing only alphanumeric characters
167(useful for anchors or filenames)
168
169=item alphadash
170
171Convert to a string containing alphanumeric characters, dashes
172and underscores; spaces are converted to underscores.
173(useful for anchors or filenames)
174
175=item comma_front
176
177Put anything after the last comma at the front (as with an author name)
178For example, "Smith,Sarah Jane" becomes "Sarah Jane Smith".
179
180=item dollars
181
182Return as a dollar value (float of precision 2)
183
184=item email
185
186Convert to a HTML mailto link.
187
188=item float
189
190Convert to float.
191
192=item hmail
193
194Convert to a "humanized" version of the email, with the @ and '.'
195replaced with "at" and "dot".  This is useful to prevent spambots
196harvesting email addresses.
197
198=item html
199
200Convert to simple HTML (simple formatting)
201
202=item int
203
204Convert to integer
205
206=item itemI<num>
207
208Assume that the value is multiple values separated by the "pipe" symbol (|) and
209select the item with an index of I<num> (starting at zero)
210
211=item items_I<directive>
212
213Assume that the value is multiple values separated by the "pipe" symbol (|) and
214split the values into an array, apply the I<directive> directive to them, and
215join them together with a space.
216
217=item itemsjslash_I<directive>
218
219Like items_I<directive>, but the results are joined together with a slash between them.
220
221=item itemslashI<num>
222
223Assume that the value is multiple values separated by the "slash" symbol (/) and
224select the item with an index of I<num> (starting at zero)
225Good for selecting out components of pathnames.
226
227=item lower
228
229Convert to lower case.
230
231=item month
232
233Convert the number value to an English month name.
234
235=item namedalpha
236
237Similar to 'alpha', but prepends the 'name' of the value.
238Assumes that the name is only alphanumeric.
239
240=item nth
241
242Convert the number value to a N-th value.  Numbers ending with 1 have 'st'
243appended, 2 have 'nd' appended, 3 have 'rd' appended, and everything
244else has 'th' appended.
245
246=item percent
247
248Show as if the value is a percentage.
249
250=item pipetocomma
251
252Assume that the value is multiple values separated by the "pipe" symbol (|) and replace
253those with a comma and space.
254
255=item pipetoslash
256
257Assume that the value is multiple values separated by the "pipe" symbol (|) and replace
258those with a forward slash (/).
259
260=item proper
261
262Convert to a Proper Noun.
263
264=item string
265
266Return the value with no change.
267
268=item title
269
270Put any trailing ",The" ",A" or ",An" at the front (as this is a title)
271
272=item truncateI<num>
273
274Truncate to I<num> length.
275
276=item upper
277
278Convert to upper case.
279
280=item url
281
282Convert to a HTML href link.
283
284=item wikilink
285
286Format the value as the most common kind of wikilink, that is [[I<value>]]
287
288=item wordsI<num>
289
290Give the first I<num> words of the value.
291
292=back
293
294=cut
295
296
297=head1 CLASS METHODS
298
299=head2 new
300
301my $tobj = Text::NeatTemplate->new();
302
303Make a new template object.
304
305=cut
306
307sub new {
308    my $class = shift;
309    my %parameters = @_;
310    my $self = bless ({%parameters}, ref ($class) || $class);
311
312    return ($self);
313} # new
314
315
316=head1 METHODS
317
318=head2 fill_in
319
320Fill in the given values.
321
322    $result = $tobj->fill_in(data_hash=>\%data,
323			     show_names=>\%names,
324			     template=>$text);
325
326The 'data_hash' is a hash containing names and values.
327
328The 'show_names' is a hash saying which of these "variable names"
329ought to be displayed, and which suppressed.  This can be useful
330if you want to use a more generic template, and then dynamically
331suppress certain values at runtime.
332
333The 'template' is the text of the template.
334
335=cut
336sub fill_in {
337    my $self = shift;
338    my %args = (
339	data_hash=>undef,
340	show_names=>undef,
341	template=>undef,
342	@_
343    );
344
345    my $out = $args{template};
346    $out =~ s/{([^}]+)}/$self->do_replace(data_hash=>$args{data_hash},show_names=>$args{show_names},targ=>$1)/eg;
347
348    return $out;
349} # fill_in
350
351=head2 get_varnames
352
353Find variable names inside the given template.
354
355    @varnames = $tobj->get_varnames(template=>$text);
356
357=cut
358sub get_varnames {
359    my $self = shift;
360    my %args = (
361	template=>undef,
362	@_
363    );
364    my $template = $args{template};
365
366    return '' if (!$template);
367
368    my %varnames = ();
369    # { (the regex below needs matching)
370    while ($template =~ m/{([^}]+)}/g)
371    {
372	my $targ = $1;
373
374	if ($targ =~ /^\$(\w+[-:\w]*)$/)
375	{
376	    my $val_id = $1;
377	    $varnames{$val_id} = 1;
378	}
379	elsif ($targ =~ /^\?([-\w]+)\s(.*)!!(.*)$/)
380	{
381	    my $val_id = $1;
382	    my $yes_t = $2;
383	    my $no_t = $3;
384
385	    $varnames{$val_id} = 1;
386
387	    foreach my $substr ($yes_t, $no_t)
388	    {
389		while ($substr =~ /\[(\$[^\]]+)\]/)
390		{
391		    $varnames{$1} = 1;
392		}
393	    }
394	}
395	elsif ($targ =~ /^\?([-\w]+)\s(.*)$/)
396	{
397	    my $val_id = $1;
398	    my $yes_t = $2;
399
400	    $varnames{$val_id} = 1;
401	    while ($yes_t =~ /\[(\$[^\]]+)\]/)
402	    {
403		$varnames{$1} = 1;
404	    }
405	}
406	elsif ($targ =~ /^\&([-\w:]+)\((.*)\)$/)
407	{
408	    # function
409	    my $func_name = $1;
410	    my $fargs = $2;
411	    while ($fargs =~ /\[(\$[^\]]+)\]/)
412	    {
413		$varnames{$1} = 1;
414	    }
415	}
416    }
417    return sort keys %varnames;
418} # get_varnames
419
420=head2 do_replace
421
422Replace the given value.
423
424    $val = $tobj->do_replace(targ=>$targ,
425			     data_hash=>$data_hashref,
426			     show_names=>\%show_names);
427
428Where 'targ' is the target value, which is either a variable target,
429or a conditional target.
430
431The 'data_hash' is a hash containing names and values.
432
433The 'show_names' is a hash saying which of these "variable names"
434ought to be displayed, and which suppressed.
435
436This can do templating by using the exec ability of substitution, for
437example:
438
439    $out =~ s/{([^}]+)}/$tobj->do_replace(data_hash=>$data_hash,targ=>$1)/eg;
440
441=cut
442sub do_replace {
443    my $self = shift;
444    my %args = (
445	targ=>'',
446	data_hash=>undef,
447	show_names=>undef,
448	@_
449    );
450    my $targ = $args{targ};
451
452    return '' if (!$targ);
453    if ($targ =~ /^\$(\w+[-:\w]*)$/)
454    {
455	my $val = $self->get_value(val_id=>$1,
456	    data_hash=>$args{data_hash},
457	    show_names=>$args{show_names});
458	if (defined $val)
459	{
460	    return $val;
461	}
462	else # not a variable -- return nothing
463	{
464	    return '';
465	}
466    }
467    elsif ($targ =~ /^\?([-\w]+)\s(.*)!!(.*)$/)
468    {
469	my $val_id = $1;
470	my $yes_t = $2;
471	my $no_t = $3;
472	my $val = $self->get_value(val_id=>$val_id,
473	    data_hash=>$args{data_hash},
474	    show_names=>$args{show_names});
475	if ($val)
476	{
477	    $yes_t =~ s/\[(\$[^\]]+)\]/$self->do_replace(data_hash=>$args{data_hash},show_names=>$args{show_names},targ=>$1)/eg;
478	    return $yes_t;
479	}
480	else # no value, return alternative
481	{
482	    $no_t =~ s/\[(\$[^\]]+)\]/$self->do_replace(data_hash=>$args{data_hash},show_names=>$args{show_names},targ=>$1)/eg;
483	    return $no_t;
484	}
485    }
486    elsif ($targ =~ /^\?([-\w]+)\s(.*)$/)
487    {
488	my $val_id = $1;
489	my $yes_t = $2;
490	my $val = $self->get_value(val_id=>$val_id,
491	    data_hash=>$args{data_hash},
492	    show_names=>$args{show_names});
493	if ($val)
494	{
495	    $yes_t =~ s/\[(\$[^\]]+)\]/$self->do_replace(data_hash=>$args{data_hash},show_names=>$args{show_names},targ=>$1)/eg;
496	    return $yes_t;
497	}
498	else # no value, return nothing
499	{
500	    return '';
501	}
502    }
503    elsif ($targ =~ /^\&([-\w:]+)\((.*)\)$/)
504    {
505	# function
506	my $func_name = $1;
507	my $fargs = $2;
508        # split the args first, and replace each one separately
509        # just in case the data values have commas
510        my @fargs = split(/,/,$fargs);
511        my @processed = ();
512        foreach my $fa (@fargs)
513        {
514	    $fa =~ s/\[(\$[^\]]+)\]/$self->do_replace(data_hash=>$args{data_hash},show_names=>$args{show_names},targ=>$1)/eg;
515            push @processed, $fa;
516        }
517	{
518	    no strict('refs');
519	    return &{$func_name}(@processed);
520	}
521    }
522    else
523    {
524	print STDERR "UNKNOWN ==$targ==\n";
525    }
526    return '';
527} # do_replace
528
529=head2 get_value
530
531$val = $tobj->get_value(val_id=>$val_id,
532			data_hash=>$data_hashref,
533			show_names=>\%show_names);
534
535Get and format the given value.
536
537=cut
538sub get_value {
539    my $self = shift;
540    my %args = (
541	val_id=>'',
542	data_hash=>undef,
543	show_names=>undef,
544	@_
545    );
546    my ($varname, @formats) = split(':', $args{val_id});
547
548    my $value;
549    if (exists $args{data_hash}->{$varname})
550    {
551	if (!$args{show_names}
552	    or $args{show_names}->{$varname})
553	{
554	    $value = $args{data_hash}->{$varname};
555	}
556	else
557	{
558	    return '';
559	}
560    }
561    else
562    {
563	return undef;
564    }
565
566    # we have a value to format
567    foreach my $format (@formats) {
568	$value = $self->convert_value(value=>$value,
569	    format=>$format,
570	    name=>$varname);
571    }
572    if ($value and $self->{escape_html})
573    {
574	# filter out some HTML stuff
575	$value =~ s/ & / &amp; /g;
576    }
577    return $value;
578} # get_value
579
580=head2 convert_value
581
582    my $val = $tobj->convert_value(value=>$val,
583				   format=>$format,
584				   name=>$name);
585
586Convert a value according to the given formatting directive.
587
588See L</FORMATTING> for details of all the formatting directives.
589
590
591=cut
592sub convert_value {
593    my $self = shift;
594    my %args = @_;
595    my $value = $args{value};
596    my $style = $args{format};
597    my $name = $args{name};
598
599    $value ||= '';
600    ($_=$style) || ($_ = 'string');
601    SWITCH: {
602	/^upper/i &&     (return uc($value));
603	/^lower/i &&     (return lc($value));
604	/^int/i &&       (return (defined $value ? int($value) : 0));
605	/^float/i &&     (return (defined $value && sprintf('%f',($value || 0))) || '');
606	/^string/i &&    (return $value);
607	/^trunc(?:ate)?(\d+)/ && (return substr(($value||''), 0, $1));
608	/^dollars/i &&
609	    (return (defined $value && length($value)
610		     && sprintf('%.2f',($value || 0)) || ''));
611	/^percent/i &&
612	    (return (($value<0.2) &&
613		     sprintf('%.1f%%',($value*100))
614		     || sprintf('%d%%',int($value*100))));
615	/^url/i &&    (return "<a href='$value'>$value</a>");
616	/^wikilink/i &&    (return "[[$value]]");
617	/^email/i &&    (return "<a mailto='$value'>$value</a>");
618	/^hmail/i && do {
619	    $value =~ s/@/ at /;
620	    $value =~ s/\./ dot /g;
621	    return $value;
622	};
623	/^html/i &&	 (return $self->simple_html($value));
624	/^title/i && do {
625	    $value =~ s/(.*)[,;]\s*(A|An|The)$/$2 $1/;
626	    return $value;
627	};
628	/^comma_front/i && do {
629	    $value =~ s/(.*)[,]([^,]+)$/$2 $1/;
630	    return $value;
631	};
632	/^proper/i && do {
633	    $value =~ s/(^w|\b\w)/uc($1)/eg;
634	    return $value;
635	};
636	/^month/i && do {
637	    return $value if !$value;
638	    return ($value == 1
639		    ? 'January'
640		    : ($value == 2
641		       ? 'February'
642		       : ($value == 3
643			  ? 'March'
644			  : ($value == 4
645			     ? 'April'
646			     : ($value == 5
647				? 'May'
648				: ($value == 6
649				   ? 'June'
650				   : ($value == 7
651				      ? 'July'
652				      : ($value == 8
653					 ? 'August'
654					 : ($value == 9
655					    ? 'September'
656					    : ($value == 10
657					       ? 'October'
658					       : ($value == 11
659						  ? 'November'
660						  : ($value == 12
661						     ? 'December'
662						     : $value
663						    )
664						 )
665					      )
666					   )
667					)
668				     )
669				  )
670			       )
671			    )
672			  )
673			  )
674	    );
675	};
676	/^nth/i && do {
677	    return $value if !$value;
678	    return ($value =~ /1[123]$/
679		    ? "${value}th"
680		    : ($value =~ /1$/
681		       ? "${value}st"
682		       : ($value =~ /2$/
683			  ? "${value}nd"
684			  : ($value =~ /3$/
685			     ? "${value}rd"
686			     : "${value}th"
687			    )
688			 )
689		      )
690		   );
691	};
692	/^facettag/i && do {
693	    $value =~ s!/! !g;
694            $value =~ s/^\s+//;
695            $value =~ s/\s+$//;
696	    $value =~ s/[^\w\s:_-]//g;
697	    $value =~ s/\s\s+/ /g;
698	    $value =~ s/ /_/g;
699	    $value = join(':', $name, $value);
700	    return $value;
701	};
702	/^namedalpha/i && do {
703	    $value =~ s/[^a-zA-Z0-9]//g;
704	    $value = join('_', $name, $value);
705	    return $value;
706	};
707	/^alphadash/i && do {
708	    $value =~ s!/! !g;
709	    $value =~ s/[^a-zA-Z0-9_\s-]//g;
710            $value =~ s/^\s+//;
711            $value =~ s/\s+$//;
712	    $value =~ s/\s\s+/ /g;
713	    $value =~ s/ /_/g;
714	    return $value;
715	};
716	/^alpha/i && do {
717	    $value =~ s/[^a-zA-Z0-9]//g;
718	    return $value;
719	};
720	/^pipetocomma/i && do {
721	    $value =~ s/\|/, /g;
722	    return $value;
723	};
724	/^pipetoslash/i && do {
725	    $value =~ s/\|/\//g;
726	    return $value;
727	};
728	/^words(\d+)/ && do {
729	    my $ct = $1;
730	    ($ct>0) || return '';
731	    my @sentence = split(/\s+/, $value);
732	    my (@words) = splice(@sentence,0,$ct);
733	    return join(' ', @words);
734	};
735	/^wlink_(\w+)/ && do {
736	    my $prefix = $1;
737	    return "[[$prefix/$value]]";
738	};
739	/^tagify/i && do {
740	    $value =~ s/\|/,/g;
741	    $value =~ s!/! !g;
742	    $value =~ s/!/ /g;
743            $value =~ s/^\s+//;
744            $value =~ s/\s+$//;
745	    $value =~ s/[^\w,\s_-]//g;
746	    $value =~ s/\s\s+/ /g;
747	    $value =~ s/ /_/g;
748	    return $value;
749	};
750	/^item(\d+)/ && do {
751	    my $ct = $1;
752	    ($ct>=0) || return '';
753	    my @items = split(/\|/, $value);
754	    return $items[$ct];
755	};
756	/^itemslash(\d+)/ && do {
757	    my $ct = $1;
758	    ($ct>=0) || return '';
759	    my @items = split(/\//, $value);
760	    return $items[$ct];
761	};
762	/^items_(\w+)/ && do {
763	    my $next = $1;
764	    my @items = split(/[\|,]\s*/, $value);
765	    my @next_items = ();
766	    foreach my $item (@items)
767	    {
768		push @next_items, $self->convert_value(%args, value=>$item, format=>$next);
769	    }
770	    return join(' ', @next_items);
771	};
772	/^itemsjslash_(\w+)/ && do {
773	    my $next = $1;
774	    my @items = split(/[\|,]\s*/, $value);
775	    my @next_items = ();
776	    foreach my $item (@items)
777	    {
778		push @next_items, $self->convert_value(%args, value=>$item, format=>$next);
779	    }
780	    return join(' / ', @next_items);
781	};
782	/^itemsjcomma_(\w+)/ && do {
783	    my $next = $1;
784	    my @items = split(/[\|,]\s*/, $value);
785	    my @next_items = ();
786	    foreach my $item (@items)
787	    {
788		push @next_items, $self->convert_value(%args, value=>$item, format=>$next);
789	    }
790	    return join(',', @next_items);
791	};
792
793	# otherwise, give up
794	return "  {{{ style $style not supported }}}  ";
795    }
796} # convert_value
797
798=head2 simple_html
799
800$val = $tobj->simple_html($val);
801
802Do a simple HTML conversion of the value.
803bold, italic, <br>
804
805=cut
806sub simple_html {
807    my $self = shift;
808    my $value = shift;
809
810    $value =~ s#\n[\s][\s][\s]+#<br/>\n&nbsp;&nbsp;&nbsp;&nbsp;#sg;
811    $value =~ s#\s*\n\s*\n#<br/><br/>\n#sg;
812    $value =~ s#\*([^*]+)\*#<i>$1</i>#sg;
813    $value =~ s/\^([^^]+)\^/<b>$1<\/b>/sg;
814    $value =~ s/\#([^#<>]+)\#/<b>$1<\/b>/sg;
815    $value =~ s/\s&\s/ &amp; /sg;
816    return $value;
817} # simple_html
818
819=head1 Callable Functions
820
821=head2 safe_backtick
822
823{&safe_backtick(myprog,arg1,arg2...argN)}
824
825Return the results of a program, without risking evil shell calls.
826This requires that the program and the arguments to that program
827be given separately.
828
829=cut
830sub safe_backtick {
831    my @prog_and_args = @_;
832    my $progname = $prog_and_args[0];
833
834    # if they didn't give us anything, return
835    if (!$progname)
836    {
837	return '';
838    }
839    # call the program
840    # do a fork and exec with an open;
841    # this should preserve the environment and also be safe
842    my $result = '';
843    my $fh;
844    my $pid = open($fh, "-|");
845    if ($pid) # parent
846    {
847	{
848	    # slurp up the result all at once
849	    local $/ = undef;
850	    $result = <$fh>;
851	}
852	close($fh) || warn "$progname program script exited $?";
853    }
854    else # child
855    {
856	# call the program
857	# force exec to use an indirect object,
858	# so that evil shell stuff will die, even
859	# for a program with no arguments
860	exec { $progname } @prog_and_args or die "$progname failed: $!\n";
861	# NOTREACHED
862    }
863    return $result;
864} # safe_backtick
865
866=head2 format_items
867
868{&format_items(fieldname,value,delim,outdelim,format,prefix,suffix)}
869
870Format a field made of multiple items.
871
872=cut
873sub format_items {
874    my $fieldname = shift;
875    my $value = shift;
876    my @args = @_;
877
878    # if they didn't give us anything, return
879    if (!$fieldname)
880    {
881	return '';
882    }
883    if (!$value)
884    {
885	return '';
886    }
887
888    my $delim = $args[0] || '|';
889    my $outdelim = $args[1] || ' ';
890    my $format = $args[2] || 'raw';
891    my $prefix = $args[3] || '';
892    my $suffix = $args[4] || '';
893    $delim =~ s/comma/,/g;
894    $delim =~ s/pipe/|/g;
895    $delim =~ s!slash!/!g;
896    $outdelim =~ s/comma/,/g;
897    $outdelim =~ s/pipe/|/g;
898    $outdelim =~ s!slash!/!g;
899    my @items = split(/\Q$delim\E\s*/, $value);
900    my @next_items = ();
901    foreach my $item (@items)
902    {
903        push @next_items,
904        Text::NeatTemplate->convert_value(name=>$fieldname,
905                                          value=>$item,
906                                          format=>$format);
907    }
908    return $prefix . join($outdelim, @next_items) . $suffix;
909} # format_items
910
911
912=head1 REQUIRES
913
914    Test::More
915
916=head1 INSTALLATION
917
918To install this module, run the following commands:
919
920    perl Build.PL
921    ./Build
922    ./Build test
923    ./Build install
924
925Or, if you're on a platform (like DOS or Windows) that doesn't like the
926"./" notation, you can do this:
927
928   perl Build.PL
929   perl Build
930   perl Build test
931   perl Build install
932
933In order to install somewhere other than the default, such as
934in a directory under your home directory, like "/home/fred/perl"
935go
936
937   perl Build.PL --install_base /home/fred/perl
938
939as the first step instead.
940
941This will install the files underneath /home/fred/perl.
942
943You will then need to make sure that you alter the PERL5LIB variable to
944find the module.
945
946Therefore you will need to change the PERL5LIB variable to add
947/home/fred/perl/lib
948
949	PERL5LIB=/home/fred/perl/lib:${PERL5LIB}
950
951=head1 SEE ALSO
952
953L<Text::Template>
954L<Text::FillIn>
955L<Text::QuickTemplate>
956L<Template::Trivial>
957L<Template::Toolkit>
958L<HTML::Template>
959
960=head1 BUGS
961
962Please report any bugs or feature requests to the author.
963
964=head1 AUTHOR
965
966    Kathryn Andersen (RUBYKAT)
967    perlkat AT katspace dot com
968    http://www.katspace.org/tools
969
970=head1 COPYRIGHT AND LICENCE
971
972Copyright (c) 2006 by Kathryn Andersen
973
974This program is free software; you can redistribute it and/or modify it
975under the same terms as Perl itself.
976
977=cut
978
9791; # End of Text::NeatTemplate
980__END__
981