I made a few patches over the weekend; three for Template, one for
Template::Provider, and one for Template::Stash.
=== Template ===
Three patches for Template.pm: one fixes a comment typo, and two add
functionality. They are all simple patches, and are separate so some or
all of them can be ignored. All patches are against Template.pm version
2.42 (from CVS HEAD).
The first patch (Template-2.42.comment-typo.dlc.patch) fixes a typo in a
comment. Pretty silly, but it bugged me. :)
I had the occasion to need to have Template::process push onto an array;
I wanted to do this:
my @a;
for my $template (@templates) {
$t->process($template, \%params, \@a);
}
But I had to do this:
my @a;
for my $template (@templates) {
my $output;
$t->process($template, \%params, \$output);
push @a, $output;
}
I tried the desired code and got this lovely message:
output_handler() cannot determine target type (ARRAY(0x8107a64))
Which was not the effect I was going for. The second patch
(Template-2.42.output-to-array.dlc.patch) adds ARRAY references as a
type that can be passed as the third parameter to process.
Finally, on the one (thankfully) recent occasion that I was using
Windows, I noticed that Template::_output has a binmode flag was is not
being set from within Template (it appears to only be set in one call in
Template::Filters). I've added code to attempt to detect whether
binmode should be set in process (Template-2.42.binmode.dlc.patch).
=== Template::Provider ===
About a year ago, this discussion occured on the template list, as
recorded in todo/walkup in the CVS repository:
* For historical reasons, our HTML developers require that
files that are INCLUDEd or PROCESSed be searched for in a
tree-walking manner, starting with the directory of
$r->filename and ascending to /. This has the effect of
including the closest copy of files declared in TT2PreProcess
and TT2PostProcess, which works out very well for using a
single set of header and footer files for an entire site,
while having section specific navigation bars and features
that get included without having to specify paths and the
like.
abw: I like the idea of implementing this in the provider
someone. Maybe specify a flag on an INCLUDE_PATH like:
INCLUDE_PATH => '/usr/web/tt2/lib/+' or '/usr/web/tt2/lib/-'
where the '+' is 'walkdown' (from root to leaf) and '-' is
'walkup' (from leaf to root) based on the current template
location.
I've implemented this (Template-Provider-2.54.walkup.dlc.patch), as Andy
describes it. Tests continue to pass.
=== Template::Stash ===
The top of the TODO file in CVS lists:
* mylist.sort.uniq
in the miscellaneous section. I implemented this
(Template-Stash-2.59.uniq.dlc.patch), although it does not require a
sorted list. I also modified t/vmeth.t (t-vmeth.t-2.13.uniq.dlc.patch).
All patches apply cleanly against the CVS HEAD, as of this morning.
(darren)
--
Mosher's Law of Software Engineering:
Don't worry if it doesn't work right.
If everything did, you'd be out of a job.
--- Template.pm.orig Sat Jul 6 23:35:05 2002
+++ Template.pm Sat Jul 6 23:35:57 2002
@@ -28,7 +28,7 @@
require 5.005;
use strict;
-use vars qw( $VERSION $AUTOLOAD $ERROR $DEBUG );
+use vars qw( $VERSION $AUTOLOAD $ERROR $DEBUG $BINMODE );
use Template::Base;
use Template::Config;
use Template::Provider;
@@ -42,6 +42,7 @@
$VERSION = '2.07a';
$ERROR = '';
$DEBUG = 0;
+$BINMODE = ($^O eq 'MSWin32') ? 1 : 0;
#------------------------------------------------------------------------
@@ -67,7 +68,7 @@
# send processed template to output stream, checking for error
return ($self->error($error))
- if ($error = &_output($outstream, $output));
+ if ($error = &_output($outstream, $output, $BINMODE));
return 1;
}
--- Template.pm.orig Sat Jul 6 23:35:05 2002
+++ Template.pm Sat Jul 6 23:37:40 2002
@@ -145,6 +145,10 @@
elsif ($reftype eq 'SCALAR') {
$$where .= $text;
}
+ # push onto ARRAY ref
+ elsif ($reftype eq 'ARRAY') {
+ push @$where, $text;
+ }
# call the print() method on an object that implements the method
# (e.g. IO::Handle, Apache::Request, etc)
elsif (UNIVERSAL::can($where, 'print')) {
--- Template.pm.orig Sat Jul 6 23:35:05 2002
+++ Template.pm Sat Jul 6 23:36:59 2002
@@ -133,7 +133,7 @@
my $reftype;
my $error = 0;
- # call a CODE referenc
+ # call a CODE reference
if (($reftype = ref($where)) eq 'CODE') {
&$where($text);
}
--- Stash.pm.orig Sun Jul 7 00:01:12 2002
+++ Stash.pm Sun Jul 7 00:06:52 2002
@@ -179,6 +179,7 @@
map { [ $_, lc $_ ] }
@$list
},
+ 'uniq' => sub { my %u; [ grep { ++$u{$_} == 1 } @{$_[0]} ] },
defined $LIST_OPS ? %$LIST_OPS : (),
};
--- Provider.pm.orig Sat Jul 6 00:01:20 2002
+++ Provider.pm Sat Jul 6 00:14:15 2002
@@ -427,8 +427,21 @@
last INCLUDE;
}
+ my @paths = ();
+ for (@{ $self->{ INCLUDE_PATH } }) {
+ if (/\+$/) {
+ # walk down the tree (root to leaf)
+ push @paths, reverse $self->_include_paths($_);
+ } elsif (/\-$/) {
+ # walk up the tree (leaf to root)
+ push @paths, $self->_include_paths($_);
+ } else {
+ push @paths, $_;
+ }
+ }
+
# search the INCLUDE_PATH for the file, in cache or on disk
- foreach $dir (@{ $self->{ INCLUDE_PATH } }) {
+ foreach $dir (@paths) {
next unless $dir;
$path = "$dir/$name";
@@ -813,6 +826,35 @@
#------------------------------------------------------------------------
+# _include_paths($filename)
+#
+# Private method to create a list of directories to be returned to the
+# provider, which specifies how provider searches for included files.
+# This makes the provider walk up or down the directory hierarchy to
+# find the closest occurance of a file to include. This facilitates,
+# for example, putting different headers and footers at various places
+# along the tree.
+#------------------------------------------------------------------------
+sub _include_paths {
+ my $self = shift;
+ my $f = shift;
+ my %uniq;
+ my @dir = (''); # empty element is necessary!
+
+ #
+ # This bit of code returns a reference to a list of directories,
+ # sorted in reverse order by length, starting from the directory
+ # in which the translated filename lives, and ending with /.
+ #
+ return (
+ grep { ++$uniq{$_} == 1 }
+ sort { length $b <=> length $a }
+ map { push @dir, $_; File::Spec->catfile(@dir) }
+ ($f =~ m:([^/]+)/:og)
+ );
+}
+
+#------------------------------------------------------------------------
# _dump()
#
# Debug method which returns a string representing the internal object
--- vmeth.t.orig Sun Jul 7 00:22:04 2002
+++ vmeth.t Sun Jul 7 00:21:48 2002
@@ -92,6 +92,7 @@
qw( Tom Dick Larry ) ],
numbers => [ map { My::Object->new($_) }
qw( 1 02 10 12 021 ) ],
+ duplicates => [ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5],
};
@@ -309,6 +310,10 @@
-- expect --
2, 3, 5, 7, 11, 13, 17, 19
+-- test --
+[% duplicates.uniq.join(', ') %]
+--expect --
+1, 2, 3, 4, 5
# USER DEFINED LIST OPS