Andy Wardley ([EMAIL PROTECTED]) said something to this effect on 06/15/2001:
> A new version of Apache::Template, version 0.03, is available from CPAN and:
>
> http://www.template-toolkit.org/download.html
>
> Changes follow.
>
> Enjoy!
>
> A
This might interest the general Apache::Template population.
We at Boston.com have been using a version of the Grover module I
submitted a few months ago as the basis for a series of
rearchitecturings (is that a word?) and redesigns for several of
our sites. The implementation of the module evolved in quite a
different direction from Apache::Template, but, in terms of
functionality, the module fills an (almost) identical role.
There are a few key differences:
* Form parameters, cookies, pnotes, and the like are closures
rather than data structures; updates to these from the
templates themselves are reflected in the request_rec and the
associated Perl objects. As a consequence, these are called
as from the templates as [% params('id') %] rather than
[% params.id %]. Setting these works as you would expect:
[% params("Fruit", "Lemon") %] sets fruit=lemon.
* 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.
With Apache::Template 0.03, I merged our changes into the
the core (it required changes to Template/Service/Apache.pm). If
there is interest, I would be happy to donate these changes to
the development of Apache::Template. A diff is attached.
As an aside, I fixed a small bug that resulted from the E-tag
patch I submitted for Apache::Template 0.2. My patch was
md5_hex'ing the reference to the content, not the content
itself... this is included in the attached diff.
I have four diffs. Apache.pm.diff is all the changes together,
while the other three are individual diffs: Apache.pm.diff-params
implements the parameters-as-closures feature;
Apache.pm.diff.E-tag has the E-tag fix; and Apache.pm.inc_path
has the tree walking subroutine. The three later can be applied
individually, or they can all be applied using Apache.pm.diff.
(darren)
--
Whatever is done for love is beyond good and evil.
-- Friedrich Neitzsche
--- Apache.pm.orig Fri Jun 15 09:36:25 2001
+++ Apache.pm Tue Jun 19 15:26:00 2001
@@ -111,14 +111,43 @@
return $params unless keys %$plist;
$r = Apache::Request->new($r);
- $params->{ env } = apache_table_to_hash( $r->subprocess_env() )
- if $all or $plist->{ env };
+
+ if ($all or $plist->{ env }) {
+ $params->{ env } = sub {
+ my $e = uc shift;
+ if (@_) {
+ $r->subprocess_env($e, shift);
+ return '';
+ }
+
+ return $r->subprocess_env($e) ||
+ $r->subprocess_env("HTTP_$e") ||
+ '';
+ };
+ }
$params->{ uri } = $r->subprocess_env('REDIRECT_URL') || $r->uri()
if $all or $plist->{ uri };
- $params->{ pnotes } = $r->pnotes()
- if $all or $plist->{ pnotes };
- $params->{ params } = apache_table_to_hash( $r->parms() )
- if $all or $plist->{ params };
+ if ($all or $plist->{ pnotes }) {
+ $params->{ pnotes } = sub {
+ my $pnote = shift;
+
+ $r->pnotes($pnote, shift) if (@_);
+ return $r->pnotes($pnote) || '';
+ };
+ }
+ if ($all or $plist->{ params }) {
+ $params->{ params } = sub {
+ my $p = shift;
+
+ if (@_) {
+ $r->param($p, shift);
+ return '';
+ }
+
+ my @val = $r->param($p) || ();
+ return @val;
+ };
+ }
$params->{ cookies } = {
map { $1 => escape_uri($2) if (/([^=]+)=(.*)/) }
grep(!/^$/, split(/;\s*/, $r->header_in('cookie'))),
--- Apache.pm.orig Fri Jun 15 09:36:25 2001
+++ Apache.pm Tue Jun 19 15:29:07 2001
@@ -62,6 +62,8 @@
return DECLINED unless -f $filename;
$self->{ TEMPLATE_ERROR } = undef;
+ $self->include_path(_inc_path($filename));
+
my ($template, $error) = $self->{ ROOT_PROVIDER }->fetch($filename);
if ($error == &Template::Constants::STATUS_DECLINED) {
return DECLINED;
@@ -169,6 +171,34 @@
}
+#------------------------------------------------------------------------
+# _inc_path($filename)
+#
+# This creates a list of directories to be returned to the provider,
+# and specifies how provider searches for included files. This hack
+# makes the provider walk up 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 _inc_path ($) {
+ my $f = shift;
+ my %uniq;
+ my @dir;
+ local $" = '/';
+
+ #
+ # 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 [
+ sort { length $b <=> length $a } # reverse sorted by length
+ grep { ++$uniq{$_} == 1 } # (unique directories only)
+ map { push @dir, $_; "/@dir"; } # a growing list of dirs
+ ($f =~ m:([^/]+)/:og) # gathered from the current
+ ]; # translated filename
+}
#------------------------------------------------------------------------
# _init()
@@ -200,6 +230,10 @@
my $rootprov = Template::Config->provider($rootcfg)
|| return $self->error(Template::Config->error());
+ my $normprov = Template::Config->provider($config)
+ || return $self->error(Template::Config->error());
+
+ $config->{ LOAD_TEMPLATES } = $normprov;
# now let the Template::Service superclass initialiser continue
$self->SUPER::_init($config)
@@ -213,6 +247,16 @@
my $item = $config->{ $_ } || [ ];
$self->{ $_ } = { map { $_ => 1 } @$item };
}
+
+ # Create an accessor method to update $normprov's include path
+ unless (defined &include_path) {
+ *include_path = sub {
+ my ($self, $paths) = @_;
+ $rootprov->include_path($paths);
+ $normprov->include_path($paths);
+ }
+ }
+
return $self;
}
--- Apache.pm.orig Fri Jun 15 09:36:25 2001
+++ Apache.pm Tue Jun 19 15:06:56 2001
@@ -147,7 +147,7 @@
if $all or $headers->{ modified } and $template;
$r->headers_out->add('Content-Length' => length $$content)
if $all or $headers->{ length };
- $r->headers_out->add('E-tag' => sprintf q{"%s"}, md5_hex($content))
+ $r->headers_out->add('E-tag' => sprintf q{"%s"}, md5_hex($$content))
if $all or $headers->{ etag };
$r->send_http_header;
}
--- Apache.pm.orig Fri Jun 15 09:36:25 2001
+++ Apache.pm Tue Jun 19 13:14:34 2001
@@ -62,6 +62,8 @@
return DECLINED unless -f $filename;
$self->{ TEMPLATE_ERROR } = undef;
+ $self->include_path(_inc_path($filename));
+
my ($template, $error) = $self->{ ROOT_PROVIDER }->fetch($filename);
if ($error == &Template::Constants::STATUS_DECLINED) {
return DECLINED;
@@ -95,6 +97,35 @@
#------------------------------------------------------------------------
+# _inc_path($filename)
+#
+# This creates a list of directories to be returned to the provider,
+# and specifies how provider searches for included files. This hack
+# makes the provider walk up 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 _inc_path ($) {
+ my $f = shift;
+ my %uniq;
+ my @dir;
+ local $" = '/';
+
+ #
+ # 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 [
+ sort { length $b <=> length $a } # reverse sorted by length
+ grep { ++$uniq{$_} == 1 } # (unique directories only)
+ map { push @dir, $_; "/@dir"; } # a growing list of dirs
+ ($f =~ m:([^/]+)/:og) # gathered from the current
+ ]; # translated filename
+}
+
+#------------------------------------------------------------------------
# params($request, $params)
#
# Create a set of processing parameters (i.e. template variables) for
@@ -111,14 +142,46 @@
return $params unless keys %$plist;
$r = Apache::Request->new($r);
- $params->{ env } = apache_table_to_hash( $r->subprocess_env() )
- if $all or $plist->{ env };
+ if ($all or $plist->{ env }) {
+ $params->{ env } = sub {
+ my $e = uc shift;
+ if (@_) {
+ $r->subprocess_env($e, shift);
+ return '';
+ }
+
+ return $r->subprocess_env($e) ||
+ $r->subprocess_env("HTTP_$e") ||
+ '';
+ };
+ }
+
$params->{ uri } = $r->subprocess_env('REDIRECT_URL') || $r->uri()
if $all or $plist->{ uri };
- $params->{ pnotes } = $r->pnotes()
- if $all or $plist->{ pnotes };
- $params->{ params } = apache_table_to_hash( $r->parms() )
- if $all or $plist->{ params };
+
+ if ($all or $plist->{ pnotes }) {
+ $params->{ pnotes } = sub {
+ my $pnote = shift;
+
+ $r->pnotes($pnote, shift) if (@_);
+ return $r->pnotes($pnote) || '';
+ };
+ }
+
+ if ($all or $plist->{ params }) {
+ $params->{ params } = sub {
+ my $p = shift;
+
+ if (@_) {
+ $r->param($p, shift);
+ return '';
+ }
+
+ my @val = $r->param($p) || ();
+ return @val;
+ };
+ }
+
$params->{ cookies } = {
map { $1 => escape_uri($2) if (/([^=]+)=(.*)/) }
grep(!/^$/, split(/;\s*/, $r->header_in('cookie'))),
@@ -147,7 +210,7 @@
if $all or $headers->{ modified } and $template;
$r->headers_out->add('Content-Length' => length $$content)
if $all or $headers->{ length };
- $r->headers_out->add('E-tag' => sprintf q{"%s"}, md5_hex($content))
+ $r->headers_out->add('E-tag' => sprintf q{"%s"}, md5_hex($$content))
if $all or $headers->{ etag };
$r->send_http_header;
}
@@ -200,6 +263,10 @@
my $rootprov = Template::Config->provider($rootcfg)
|| return $self->error(Template::Config->error());
+ my $normprov = Template::Config->provider($config)
+ || return $self->error(Template::Config->error());
+
+ $config->{ LOAD_TEMPLATES } = $normprov;
# now let the Template::Service superclass initialiser continue
$self->SUPER::_init($config)
@@ -214,6 +281,15 @@
$self->{ $_ } = { map { $_ => 1 } @$item };
}
+ # Create an accessor method to update $normprov's include path
+ unless (defined &include_path) {
+ *include_path = sub {
+ my ($self, $paths) = @_;
+ $rootprov->include_path($paths);
+ $normprov->include_path($paths);
+ }
+ }
+
return $self;
}