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