Hello,

This patch implements an experimental feature similar to Perl
AUTOLOAD sub. It adds new AUTOLOAD configuration option to TT,
which is used to specify a subroutine that will be called when
non-existent template variable is accessed the first time.
Return value from this subroutine will then be assigned to the
variable.

Example:

     sub autoload {
         my ($stash, $root, $item, $lvalue) = @_;
         # copy value from global hash
         return $stash->get('global')->{ $item };
     }

     my $template = Template->new({
         AUTOLOAD => \ &autoload,
     });


The patch is against 2.06; only implemented in Perl stash so far.
Documentation is included.

Please let me know if you're interested in this.

-- 
Vladimir Pastukhov <[EMAIL PROTECTED]>
--- ./lib/Template/Manual/Config.pod.orig       Wed Nov  7 19:47:53 2001
+++ ./lib/Template/Manual/Config.pod    Mon Jul 15 08:17:07 2002
@@ -536,6 +536,26 @@
 
 
 
+=item AUTOLOAD
+
+This option is used to specify a subroutine that will be called
+when non-existent template variable is accessed the first time.
+Return value from this subroutine will then be assigned to the
+variable.
+
+    sub autoload {
+       my ($stash, $root, $item, $lvalue) = @_;
+       # copy value from global hash
+       # (which must exist to avoid recursion)
+       return $stash->get('global')->{ $item };
+    }
+
+    my $template = Template->new({
+       AUTOLOAD => \ &autoload,
+    });
+
+
+
 =back
 
 =head2 Runtime Processing Options
--- ./lib/Template/Context.pm.orig      Wed Nov  7 19:47:52 2001
+++ ./lib/Template/Context.pm   Mon Jul 15 06:05:37 2002
@@ -749,6 +749,7 @@
 
        # hack to get stash to know about debug mode
        $predefs->{ _DEBUG } = $config->{ DEBUG } || 0;
+       $predefs->{ _AUTOLOAD } = $config->{ AUTOLOAD };
        Template::Config->stash($predefs)
            || return $self->error($Template::Config::ERROR);
     };
--- ./lib/Template/Stash.pm.orig        Wed Nov  7 19:47:52 2001
+++ ./lib/Template/Stash.pm     Mon Jul 15 06:58:31 2002
@@ -460,27 +460,38 @@
 
     if ($rootref eq __PACKAGE__ || $rootref eq 'HASH') {
 
-       # if $root is a regular HASH or a Template::Stash kinda HASH (the 
-       # *real* root of everything).  We first lookup the named key 
-       # in the hash, or create an empty hash in its place if undefined
-       # and the $lvalue flag is set.  Otherwise, we check the HASH_OPS
-       # pseudo-methods table, calling the code if found, or return undef.
+       # if $root is a regular HASH or a Template::Stash kinda HASH (the
+       # *real* root of everything).  We first lookup the named key
+       # in the hash, or try to call pseudo-method from HASH_OPS table
+       # if corresponding item is undefined and the $lvalue flag is not set.
+       # Otherwise, if we have an array ref then return hash slice,
+       # or if the item does not exist in the hash and AUTOLOAD hook
+       # is present, store its return value under the named key,
+       # so the hook is called only once per variable.  If no hook
+       # is set and this is an lvalue, create an empty hash in place of
+       # the undefined item, or return undef.
 
        if (defined($value = $root->{ $item })) {
            return $value unless ref $value eq 'CODE';      ## RETURN
            @result = &$value(@$args);                      ## @result
        }
+       elsif (($value = $HASH_OPS->{ $item }) && ! $lvalue) {
+           @result = &$value($root, @$args);               ## @result
+       }
+       elsif (ref $item eq 'ARRAY') {
+           # hash slice
+           return [ @$root{ @$item } ];                    ## RETURN
+       }
+       elsif (! exists $root->{ $item } && ($value = $self->{ _AUTOLOAD })) {
+           # call autoload hook and create new hash item
+           @result = &$value($self, $root, $item, $lvalue);   ## @result
+           $value  = $root->{ $item } = $result[0];
+           @result = &$value(@$args) if ref $value eq 'CODE'; ## @result
+       }
        elsif ($lvalue) {
            # we create an intermediate hash if this is an lvalue
            return $root->{ $item } = { };                  ## RETURN
        }
-       elsif ($value = $HASH_OPS->{ $item }) {
-           @result = &$value($root, @$args);               ## @result
-       }
-       elsif ( ref $item eq 'ARRAY' ) {
-             # hash slice
-             return [@$root{@$item}];                       ## RETURN
-       }
     }
     elsif ($rootref eq 'ARRAY') {
 
--- ./lib/Template/Manual/Variables.pod.orig    Wed Nov  7 19:47:53 2001
+++ ./lib/Template/Manual/Variables.pod Mon Jul 15 08:40:33 2002
@@ -410,6 +410,58 @@
        ...
     [% END %]
 
+=head2 Autoloading
+
+If user-defined subroutine is specified with AUTOLOAD configuration
+option, it will be called when non-existent template variable is
+accessed the first time. Return value (which may be a code or an
+object reference) from this subroutine will then be assigned to the
+variable.
+
+Parameters are passed in the following order: references to the
+stash object and a variable container (which is either stash itself
+or a hash), variable name, and a flag to indicate if this variable
+is being evaluated on the left side of an assignment.
+
+    sub autoload {
+       my ($stash, $root, $item, $lvalue) = @_;
+       # automatically create intermediate hash in assignment
+       return { auto => 1 } if $lvalue;
+       # copy value from global hash or return default
+       return $stash->get('global')->{ $item } || 'nothing';
+    }
+
+    my $template = Template->new({
+       AUTOLOAD => \ &autoload,
+    });
+
+    my $vars = {
+       global => {
+           item1 => 'foo',
+           item2 => sub { return int(rand(10)) },
+       },
+    };
+
+template:
+
+    item1 = [% item1 %]
+    item2 = [% item2 %]
+    item3 = [% item3 %]
+    hash.item1 = [% hash.item1 %]
+    hash.item2 = [% hash.item2 %]
+    hash.auto  = [% hash.auto  %]
+
+output:
+
+    item1 = foo
+    item2 = 3
+    item3 = nothing
+    hash.item1 = foo
+    hash.item2 = 7
+    hash.auto  = 1
+
+Be careful when using autoload subroutine to avoid unlimited recursion.
+
 =head2 Error Handling
 
 Errors can be reported from user code by calling die().  Errors raised

Reply via email to