[Templates] Patch for "run-time" PLUGIN_BASE modifications
Blake D. Mills IV
blakem@blakem.com
Sat, 9 Mar 2002 00:59:15 -0500
I have several domains served off a single machine and each of them
needs to have its own PLUGIN_BASE settings. At first, I was forced to
use separate Template objects because there wasn't a mechanism to
modify the PLUGIN_BASE after instantiating the Template object. This
was obviously a huge memory waste, since I was keeping many Template
objects around even though they were nearly identical.
I hacked around in the code and came up with an even kludgier solution
than the one I posted for "very local" includes. However, I can now
set PLUGIN_BASE on a per-request basis like this in my handler:
use vars qw($tt); # template object is package global
sub handler {
my $r = shift;
# get some config values from httpd.conf
my @tt2plugins = $r->dir_config->get('TT2Plugin');
my @tt2includepaths = $r->dir_config->get('TT2Include_Path');
# Build a single Template object
$tt ||= Template->new({
OUTPUT => $r, # direct output to Apache request
INCLUDE_PARENTS_PATH => 1,
});
# set include and plugin paths on a per-request basis
my $provider = $tt->service->context->load_templates->[0];
$provider->include_path([@tt2includepaths]);
$provider->{PARAMS}{PLUGIN_BASE} = [@tt2plugins]; # ugly but works...
# some other stuff (set $file, $params, etc)
# .....
$tt->process($file, $params);
}
After applying the patch, this handler will now honor PLUGIN_BASE
settings in <Directory> <Location> and <VIRTUALHOST> stanzas in my
httpd.conf
# system wide settings
PerlAddVar TT2Plugin Template::Plugin::SystemWide
PerlAddVar TT2Include_Path /systemwide/templates
<Directory /web/site/numberone>
PerlAddVar TT2Plugin Template::Plugin::NumberOne
PerlAddVar TT2Include_Path /numberone/templates
.......
</Directory>
<Directory /web/site/numbertwo>
PerlAddVar TT2Plugin Template::Plugin::NumberTwo
PerlAddVar TT2Include_Path /numbertwo/templates
.......
</Directory>
My Template object is now smart enough to distinguish between:
Template::Plugin::NumberOne::Foo
and
Template::Plugin::NumberTwo::Foo
/numberone/templates/foo.templ
and
/numbertwo/templates/foo.templ
As before, the patch is a bit kludgy as I'm just starting to find my way
around the code. Questions? Comments?
-Blake
Patch is against v2.06
contains support for:
"very local" includes
on-the-fly PLUGIN_BASE changes
optional subdirectories tacked onto INCLUDE_PATH
diff -C 2 old/Context.pm new/Context.pm
*** old/Context.pm Sat Mar 9 00:12:56 2002
--- new/Context.pm Sat Mar 9 00:14:15 2002
***************
*** 79,82 ****
--- 79,85 ----
my $providers;
+ # Keep track of the filename of the template we're being pulled into
+ my $parentfile = $self->{STASH}->get(['component',0,'file',0]);
+
# references to Template::Document (or sub-class) objects objects, or
# CODE references are assumed to be pre-compiled templates and are
***************
*** 127,131 ****
# reference by name
foreach my $provider (@$providers) {
! ($template, $error) = $provider->fetch($name);
return $template unless $error;
# return $self->error($template)
--- 130,134 ----
# reference by name
foreach my $provider (@$providers) {
! ($template, $error) = $provider->fetch($name,$parentfile);
return $template unless $error;
# return $self->error($template)
***************
*** 334,338 ****
# localise the variable stash with any parameters passed
$stash = $self->{ STASH } = $self->{ STASH }->clone($params);
-
eval {
foreach $name (@$template) {
--- 337,340 ----
diff -C 2 old/Plugins.pm new/Plugins.pm
*** old/Plugins.pm Sat Mar 9 00:13:30 2002
--- new/Plugins.pm Sat Mar 9 00:13:45 2002
***************
*** 96,103 ****
unshift @$args, $context;
! $factory = $self->{ FACTORY }->{ $name } ||= do {
! ($factory, $error) = $self->_load($name, $context);
! return ($factory, $error) if $error; ## RETURN
! $factory;
};
--- 96,107 ----
unshift @$args, $context;
!
! my $params = $context->load_templates->[0]->{PARAMS};
! $self->{ PLUGIN_BASE } = $params->{ PLUGIN_BASE };
! my $factorykey = join ('+',@{$self->{PLUGIN_BASE}}) . $name;
! $factory = $self->{ FACTORY }->{ $factorykey } ||= do {
! ($factory, $error) = $self->_load($name, $context);
! return ($factory, $error) if $error; ## RETURN
! $factory;
};
***************
*** 168,172 ****
my ($self, $name, $context) = @_;
my ($factory, $module, $base, $pkg, $file, $ok, $error);
-
if ($module = $self->{ PLUGINS }->{ $name }) {
# plugin module name is explicitly stated in PLUGIN_NAME
--- 172,175 ----
diff -C 2 old/Provider.pm new/Provider.pm
*** old/Provider.pm Sat Mar 9 00:13:13 2002
--- new/Provider.pm Sat Mar 9 00:13:58 2002
***************
*** 87,91 ****
sub fetch {
! my ($self, $name) = @_;
my ($data, $error);
--- 87,91 ----
sub fetch {
! my ($self, $name, $parentfile) = @_;
my ($data, $error);
***************
*** 119,123 ****
# otherwise, it's a file name relative to INCLUDE_PATH
($data, $error) = $self->{ INCLUDE_PATH }
! ? $self->_fetch_path($name)
: (undef, Template::Constants::STATUS_DECLINED);
}
--- 119,123 ----
# otherwise, it's a file name relative to INCLUDE_PATH
($data, $error) = $self->{ INCLUDE_PATH }
! ? $self->_fetch_path($name, $parentfile)
: (undef, Template::Constants::STATUS_DECLINED);
}
***************
*** 323,326 ****
--- 323,330 ----
$self->{ ABSOLUTE } = $params->{ ABSOLUTE } || 0;
$self->{ RELATIVE } = $params->{ RELATIVE } || 0;
+ $self->{ INCLUDE_PARENTS_PATH } = $params->{ INCLUDE_PARENTS_PATH } || 0;
+ $self->{ INCLUDE_PARENTS_SUBDIRS } =
+ !$params->{ INCLUDE_PARENTS_SUBDIRS } ? [] :
+ [ map m|^/| ? $_ : "/$_", @{$params->{ INCLUDE_PARENTS_SUBDIRS }}];
$self->{ TOLERANT } = $params->{ TOLERANT } || 0;
$self->{ PARSER } = $params->{ PARSER };
***************
*** 401,405 ****
sub _fetch_path {
! my ($self, $name) = @_;
my ($size, $compext, $compdir) =
@$self{ qw( SIZE COMPILE_EXT COMPILE_DIR ) };
--- 405,409 ----
sub _fetch_path {
! my ($self, $name, $parentfile) = @_;
my ($size, $compext, $compdir) =
@$self{ qw( SIZE COMPILE_EXT COMPILE_DIR ) };
***************
*** 424,429 ****
}
# search the INCLUDE_PATH for the file, in cache or on disk
! foreach $dir (@{ $self->{ INCLUDE_PATH } }) {
next unless $dir;
$path = "$dir/$name";
--- 428,442 ----
}
+ # Add parent's path to INCLUDE_PATH if INCLUDE_PARENTS_PATH is set
+ my @includepaths = @{ $self->{ INCLUDE_PATH } };
+ if ($self->{INCLUDE_PARENTS_PATH} && $parentfile) {
+ # File::Basename might be better, but this works fine for me
+ (my $parentpath = $parentfile) =~ s|/[^/]+$||;
+ unshift(@includepaths,"$parentpath$_")
+ for (reverse ('',@{$self->{INCLUDE_PARENTS_SUBDIRS}}));
+ }
+
# search the INCLUDE_PATH for the file, in cache or on disk
! foreach $dir (@includepaths) {
next unless $dir;
$path = "$dir/$name";
***************
*** 572,575 ****
--- 585,589 ----
time => (stat $name)[9],
load => $now,
+ file => $name,
};
}
***************
*** 756,759 ****
--- 770,774 ----
'name' => $data->{ name },
'modtime' => $data->{ time },
+ 'file' => $data->{ file },
%{ $parsedoc->{ METADATA } },
};