[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 } },
  	};