[Templates-cvs] cvs commit: TT3/lib/Template Component.pm
cvs@template-toolkit.org
cvs@template-toolkit.org
Wed, 10 Nov 2004 18:11:21 +0000
cvs 04/11/10 18:11:21
Modified: lib/Template Component.pm
Log:
* added lots of mess and half-finished code that I wrote over the summer.
don't trust anything in here
Revision Changes Path
1.4 +175 -138 TT3/lib/Template/Component.pm
Index: Component.pm
===================================================================
RCS file: /template-toolkit/TT3/lib/Template/Component.pm,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- Component.pm 2004/03/26 10:55:11 1.3
+++ Component.pm 2004/11/10 18:11:21 1.4
@@ -20,19 +20,15 @@
# * Add 'chain' item indicating which chains we want to follow.
# * Finalise how chained items can block the rest of the chain,
# decline, etc.
-# * figure out what to do with declines so that fetching a
-# variable doesn't return '0'
-# * fetch/store vs get/set - can't make stash and templates work
-# homgenously if their respective providers have different methods.
-# Perhaps fetch() gets called with the context, and get without?
# * I don't think the enter/leave thing is right. enter() should
# be more like clone().
# * Figure out where to cache locally fetched things like compilers,
# resources, etc. local/shared, directly in object, or something
# else
+# * use cheap clone method with bless rather than calling new()?
#
# REVISION
-# $Id: Component.pm,v 1.3 2004/03/26 10:55:11 abw Exp $
+# $Id: Component.pm,v 1.4 2004/11/10 18:11:21 abw Exp $
#
#========================================================================
@@ -43,7 +39,7 @@
use Template::Exception;
use Template::Stash;
use Template::Base;
-use vars qw( $VERSION $DEBUG $ERROR $MAX_DEPTH );
+use vars qw( $VERSION $DEBUG $ERROR $THROW $EXCEPTION $MAX_DEPTH );
use base qw( Template::Base );
# providers can define a fetch($context, $item) method which expects a
@@ -53,18 +49,25 @@
use constant FETCH => 'fetch';
use constant GET => 'get';
-$VERSION = sprintf("%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/);
+$VERSION = sprintf("%d.%02d", q$Revision: 1.4 $ =~ /(\d+)\.(\d+)/);
$DEBUG = 0 unless defined $DEBUG;
$ERROR = '';
+$THROW = 'component';
+$EXCEPTION = 'Template::Exception';
$MAX_DEPTH = 64;
-our $THROW = 'component';
+#========================================================================
+# constructor, initialisation, accessors and other housekeeping methods.
+#========================================================================
+
#------------------------------------------------------------------------
-# new()
+# init()
#
-# Create a new component. We need to consider the following options:
+# Initialisation method called by base class new() constructor method.
+# Takes some, or others of the following options, all of which is still
+# subject to minor change.
# id / name / path / time # general purpose identifiers
# scope / parent / caller # context links
# source / provider / load # source doc ref or prov link
@@ -90,9 +93,29 @@
return $self;
}
+
+# not sure if we need all these... how about an AUTOLOAD that walks
+# the right chains?
+sub id { $_[0]->{ id } }
+sub name { $_[0]->{ name } }
+sub path { $_[0]->{ path } }
+sub time { $_[0]->{ time } }
+
+
+sub caller {
+ # TODO: get current caller or nth caller
+}
+
+sub callers {
+ # TODO: return list of callers
+}
+
+sub parent {
+ # TODO: get current parent or nth parent
+}
-sub name {
- return $_[0]->{ name };
+sub parents {
+ # TODO: return list of parents
}
@@ -121,7 +144,6 @@
}
-
#------------------------------------------------------------------------
# fresh()
#
@@ -146,6 +168,12 @@
}
+#------------------------------------------------------------------------
+# cache($cache)
+#
+# Component! Cache thyself if it is written in the options that you may.
+#------------------------------------------------------------------------
+
sub cache {
my ($self, $cache) = @_;
@@ -160,6 +188,15 @@
}
+#------------------------------------------------------------------------
+# store($store)
+#
+# And it shall come to pass that if the options of the component declare
+# the will of the component creator to be such that it should be stored
+# persistantly, then the component shall store itself. And the word was
+# good, and good was the word, unless of course, an error occurred.
+#------------------------------------------------------------------------
+
sub store {
my ($self, $store) = @_;
my ($source, $code);
@@ -181,24 +218,23 @@
}
-sub visited {
- return $_[0]->{ visited }
- || $_[0]->decline('not recording components visited');
-}
-
+#========================================================================
+# runtime!
+#========================================================================
#------------------------------------------------------------------------
# run(\%options)
#
-# Run the component.
+# This method runs the current component. The component is effectively
+# cloned by a call to the enter() method and the resulting clone component
+# is passed as the first argument to the body subroutine, masquerading as
+# the object itself.
#------------------------------------------------------------------------
sub run {
my $self = shift;
my $opts = @_ && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ };
-
- # sets context to self, local vars to args, visited list
my $copy = $self->enter($opts);
eval {
@@ -220,6 +256,8 @@
$copy->finish();
+# push(@$visited, $copy) if $visited;
+
# TODO: not sure about this
die $self->catch($@) if $@;
@@ -227,6 +265,13 @@
}
+
+#------------------------------------------------------------------------
+# call($name, \%options)
+#
+# Call another component.
+#------------------------------------------------------------------------
+
sub call {
my $self = shift;
my $name = shift;
@@ -241,108 +286,20 @@
-#------------------------------------------------------------------------
-# execute(\%args, \%opts)
-#
-# Method to execute the current template, implemented as a wrapper around
-# the run() method. The execute() method accepts arguments in the style
-# of process()
-#------------------------------------------------------------------------
+# this is kinda redundant right now, but it's a hook in my head for later
-sub execute {
- my $self = shift;
- my ($args, $opts);
- foreach ($args, $opts) {
- $_ = { }, last unless @_;
- $_ = { }, shift, next unless defined $_[0];
- $_ = shift, next if UNIVERSAL::isa($_[0], 'HASH');
- $_ = { @_ };
- }
- local $opts->{ args } = $args;
- $self->run($opts);
+sub visited {
+ return $_[0]->{ visited }
+ || $_[0]->decline('not recording components visited');
}
-#------------------------------------------------------------------------
-# process($name, \%args, \%opts)
-#
-# Process another template named by the first argument, using the
-# variable values defined by the second argument, and any other
-# processing options provided by the third.
-#------------------------------------------------------------------------
-
-sub process {
- my ($self, $name) = (shift, shift);
- my ($args, $opts);
- foreach ($args, $opts) {
- $_ = { }, next unless @_;
- $_ = { }, shift, next unless defined $_[0];
- $_ = shift, next if UNIVERSAL::isa($_[0], 'HASH');
- $_ = { @_ };
- }
- my $component = $self->template($name) || return;
-
- # TODO: may not want to mess with opts?
- local $opts->{ args } = $args;
- local $opts->{ caller } = $self;
-
- # TODO: error, return value, etc.
- $component->run($opts);
-}
-
#========================================================================
# context management methods
#========================================================================
-
-# NOTE: I think the enter(), leave() and finish() methods below aren't
-# quite right so I'm reworking them around a new visit() method.
-
-#sub visit {
-# my $self = shift;
-# my $opts = @_ && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ };
-#
-# if ($parent = $self->{ parent }) {
-# $parent = $parent->visit(
-
-
#------------------------------------------------------------------------
-# prepare(\%options)
-#
-# Prepare the current component to run by cloning a new component object
-# with a 'context' reference back to $self
-#------------------------------------------------------------------------
-
-sub prepare {
- my $self = shift;
- my $opts = @_ && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ };
-
- $self->debug("prepare() $self->{ name }\n") if $self->{ DEBUG };
-
- # don't let the caller chain grow too long
- return $self->error("maximum context depth ($MAX_DEPTH) reached")
- unless (($opts->{ depth } = $self->{ depth } + 1) < $MAX_DEPTH);
-
- # add context link to current component
- local $opts->{ context } = $self;
-
- # TODO: this is a bit of a kludge
- local $opts->{ $_ } = $self->{ $_ }
- foreach (qw( name path time meta source ));
-
- # TODO: what if both local vars and args are provided?
- $opts->{ local }->{ variables } ||= $opts->{ args } || { };
-
- my $copy = $self->new($opts) || return;
-
- # if 'prepare' option/actions set, then run them on copy
-
- return $copy;
-}
-
-
-#------------------------------------------------------------------------
# enter(\%options)
#
# Enter the current component by cloning a new component object with
@@ -353,8 +310,6 @@
my $self = shift;
my $opts = @_ && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ };
- $self->debug("enter() $self->{ name }\n") if $self->{ DEBUG };
-
# don't let the caller chain grow too long
return $self->error("maximum context depth ($MAX_DEPTH) reached")
unless (($opts->{ depth } = $self->{ depth } + 1) < $MAX_DEPTH);
@@ -363,17 +318,20 @@
# visiting, then all the components that it visits should do the same
$opts->{ visited } ||= [ ] if $self->{ visited };
- # TODO: what about all the other options that components might
- # want to share? What about an 'options' hash? Also need to
- # copy name, path, time, source, etc., etc.,
-
# add context link to current component
$opts->{ context } = $self;
- # TODO: what if both local vars and args are provided?
- $opts->{ local }->{ variables } ||= $opts->{ args } || { };
+ # set local variables to args passed
+ $opts->{ variables } ||= $opts->{ args } || { };
- $self->new($opts);
+ $self->debug("enter() $self->{ name }\n") if $self->{ DEBUG };
+
+ # misc flags
+ $opts->{ DEBUG } = $self->{ DEBUG } if $self->{ DEBUG };
+ $opts->{ THROW } = $self->{ THROW } if $self->{ THROW };
+
+ # bless hash of options into new components
+ bless $opts, ref $self;
}
@@ -429,8 +387,7 @@
#------------------------------------------------------------------------
# locate($resource)
#
-# Walk up the component/parent/caller chains looking for any $type items
-# defined in their 'local' stashes.
+# Walk up the component/parent/caller chains looking for any $type items.
#------------------------------------------------------------------------
sub locate {
@@ -465,11 +422,6 @@
$component->{name} || '<anon>',
" (distance: $depth)\n" ) if $debug;
-# TODO: can't decide where to put things, either directly in
-# component (much easier/cleaner to instantiate) or in specific
-# local hash (better separation)
-# if ($items = $component->{ local }->{ $resource }) {
-
if ($items = $component->{ $resource }) {
$self->debug("located component $resource: $items\n")
if $debug;
@@ -499,8 +451,11 @@
# list in reverse order so that context comes off first
unshift(@components, caller => $component->{ caller })
if $component->{ caller };
+
unshift(@components, parent => $component->{ parent })
if $component->{ parent };
+
+ # TODO: think this should be 'component' not 'context'??
unshift(@components, context => $component->{ context })
if $component->{ context };
@@ -551,7 +506,9 @@
|| $self->locate($resource) || return;
my ($items, $method);
+ $self->{ DECLINED } = 0;
+ # TODO: not sure if we need eval??
eval {
# provide a useful exception type for errors thrown
local $self->{ THROW } .= "search.$resource.$name";
@@ -559,19 +516,25 @@
foreach $items (@$locations) {
if (ref($items) eq 'HASH') {
# look for the item in the hash
- last if defined ($item = $items->{ $name });
+ return $items->{ $name } if exists $items->{ $name };
+# last if defined ($item = $items->{ $name });
}
elsif ($method = UNIVERSAL::can($items, FETCH)) {
# call fetch() method on provider, passing $self
+ # TODO: same again, how do we indicate a returned value
+ # of undef rather than a decline, should we called the declined()
+ # method if $item is undef?
last if defined ($item = &$method($items, $self, $name, @opts));
}
elsif ($method = UNIVERSAL::can($items, GET)) {
# call get() method, without passing $self
+ # TODO: and again...
last if defined ($item = &$method($items, $name, @opts));
}
elsif (UNIVERSAL::isa($items, 'CODE')) {
# call subroutine to fetch it, passing $self as first
# argument so that subroutine can behave like a method
+ # TODO: not much we can do here...
last if defined ($item = &$items($self, $name, @opts));
}
else {
@@ -589,6 +552,59 @@
+#------------------------------------------------------------------------
+# execute(\%args, \%opts)
+#
+# Method to execute the current template, implemented as a wrapper around
+# the run() method. The execute() method accepts arguments in the style
+# of process()
+#------------------------------------------------------------------------
+
+sub execute {
+ my $self = shift;
+ my ($args, $opts);
+ foreach ($args, $opts) {
+ $_ = { }, last unless @_;
+ $_ = { }, shift, next unless defined $_[0];
+ $_ = shift, next if UNIVERSAL::isa($_[0], 'HASH');
+ $_ = { @_ };
+ }
+ local $opts->{ args } = $args;
+ $self->run($opts);
+}
+
+
+#------------------------------------------------------------------------
+# process($name, \%args, \%opts)
+#
+# Process another template named by the first argument, using the
+# variable values defined by the second argument, and any other
+# processing options provided by the third.
+#------------------------------------------------------------------------
+
+sub process {
+ my ($self, $name) = (shift, shift);
+ my ($args, $opts);
+ foreach ($args, $opts) {
+ $_ = { }, next unless @_;
+ $_ = { }, shift, next unless defined $_[0];
+ $_ = shift, next if UNIVERSAL::isa($_[0], 'HASH');
+ $_ = { @_ };
+ }
+ my $component = $self->template($name) || return;
+
+ # TODO: may not want to mess with opts?
+ local $opts->{ args } = $args;
+ local $opts->{ caller } = $self;
+
+ # TODO: error, return value, etc.
+ $component->run($opts);
+}
+
+
+
+
+
#========================================================================
# methods for locating specific resources
#========================================================================
@@ -598,15 +614,29 @@
#------------------------------------------------------------------------
sub resource {
- my ($self, $name) = @_;
-
- # cache resources - they get used often
+ my ($self, $name) = (shift, shift);
return $self->{ resources }->{ $name }
- ||= $self->search( resources => $name );
+ ||= $self->search( resources => $name, @_ );
}
+#------------------------------------------------------------------------
+# component($name, \%options)
+#------------------------------------------------------------------------
+sub component {
+ my ($self, $name) = (shift, shift);
+
+ $self->debug("component($name)\n") if $self->{ DEBUG };
+
+ # see if $name is already a component
+# return $name if UNIVERSAL::can($name, 'run');
+
+ return $self->{ cache }->{ components }->{ $name }
+ ||= $self->search( components => $name, @_ );
+}
+
+
#------------------------------------------------------------------------
# template($name, \%options)
#
@@ -662,6 +692,12 @@
}
+sub plugin {
+ my $self = shift;
+ local $self->{ THROW } = 'plugin';
+ $self->search( plugins => @_ );
+}
+
#------------------------------------------------------------------------
# compiler($name)
@@ -722,12 +758,13 @@
sub catch {
my ($self, $error, $output) = @_;
- if (UNIVERSAL::isa($error, 'Template::Exception')) {
+ if (UNIVERSAL::isa($error, $EXCEPTION)) {
$error->text($output) if $output;
return $error;
}
else {
- return Template::Exception->new('undef', $error, $output);
+ # TODO: call throw() instead
+ return $EXCEPTION->new('undef', $error, $output);
}
}
@@ -831,7 +868,7 @@
=head1 VERSION
-$Revision: 1.3 $
+$Revision: 1.4 $
=head1 COPYRIGHT