[Templates-cvs] cvs commit: TT3/lib/Template Component.pm
cvs@template-toolkit.org
cvs@template-toolkit.org
Fri, 10 Dec 2004 14:59:22 +0000
cvs 04/12/10 14:59:22
Modified: lib/Template Component.pm
Log:
* new component containing only the component related bits - other
stuff moved into Context and Scope
Revision Changes Path
1.7 +100 -437 TT3/lib/Template/Component.pm
Index: Component.pm
===================================================================
RCS file: /template-toolkit/TT3/lib/Template/Component.pm,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- Component.pm 2004/11/24 16:22:40 1.6
+++ Component.pm 2004/12/10 14:59:22 1.7
@@ -26,7 +26,7 @@
# * use cheap clone method with bless rather than calling new()?
#
# REVISION
-# $Id: Component.pm,v 1.6 2004/11/24 16:22:40 abw Exp $
+# $Id: Component.pm,v 1.7 2004/12/10 14:59:22 abw Exp $
#
#========================================================================
@@ -38,63 +38,96 @@
use Template::Base;
use base qw( Template::Base );
-our $VERSION = sprintf("%d.%02d", q$Revision: 1.6 $ =~ /(\d+)\.(\d+)/);
+our $VERSION = sprintf("%d.%02d", q$Revision: 1.7 $ =~ /(\d+)\.(\d+)/);
our $DEBUG = 0 unless defined $DEBUG;
our $ERROR = '';
our $THROW = 'component';
our $EXCEPTION = 'Template::Exception';
our $MAX_DEPTH = 64;
+our $CACHE = 1;
+our $STORE = 1;
+our $PARAMS = [ qw( metadata template templates ) ];
-
-#========================================================================
-# constructor, initialisation, accessors and other housekeeping methods.
-#========================================================================
-
#------------------------------------------------------------------------
# init()
#
# 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
-# local # local data
-# metadata # locally defined metadata
-# options # cache/store/compiler/etc
-# depth # internal depth counter
+# Accept the following named parameters:
+#
+# id # unique identifier generated by provider
+# path # full path to template relative to provider root
+# time # timestamp (defaults to current time)
+# metadata # hash of component metadata
+# template # reference to main template subroutine
+# templates # hash of sub-templates (e.g. BLOCK defs)
+# source # reference to Template::Source source template
+# cache # can/should this component be cached in memory?
+# store # can/should this component be stored persistantly?
#------------------------------------------------------------------------
sub init {
my ($self, $config) = @_;
- @$self{ keys %$config } = values %$config;
+
+ # path is a mandatory parameter
+ return $self->error("mandatory path parameter not defined")
+ unless defined ($self->{ path } = $config->{ path });
+
+ # name defaults to last part of path
+ ($self->{ name } = $self->{ path }) =~ s{^([^/]*/)*}{}
+ unless defined ($self->{ name } = $config->{ name });
+
+ # set default time to now, and cache/store options to
+ # relevant package vars if not explicitly set in config
+ $self->{ time } = $config->{ time } || time();
+ $self->{ cache } = $self->pkgvar( CACHE => $CACHE )
+ unless defined ($self->{ cache } = $config->{ cache });
+ $self->{ store } = $self->pkgvar( STORE => $STORE )
+ unless defined ($self->{ store } = $config->{ store });
+
+ # copy any other parameters defined in $PARAMS from config to self
+ my $params = $self->pkgvar( PARAMS => $PARAMS );
+ @$self{ @$params } = @$config{ @$params };
- $self->{ depth } ||= 0;
+ return $self;
+}
+
+# TODO: components don't have/need an id, but should they, or can
+# they ask source for it?
- # 'visited' flag should be upgraded to array ref
- $self->{ visited } = [ ]
- if $self->{ visited }
- && ! UNIVERSAL::isa($self->{ visited }, 'ARRAY');
+#sub id { $_[0]->{ id } }
+sub path { $_[0]->{ path } }
+sub time { $_[0]->{ time } }
- # TMP HACK
- $self->{ name } = '[anon]' unless defined $self->{ name };
- return $self;
+sub metadata {
+ my $self = shift;
+ my $meta = $self->{ metadata } ||= { };
+
+ # when called with an argument, we return the metadata item
+ # e.g. $comp->metadata('foo') ==> $comp->{ metadata }->{ foo }
+
+ if (@_) {
+ my $item = shift;
+ return $meta->{ $item };
+ }
+
+ # when called without any argument, we return the complete
+ # metatdata hash. note that updating this hash will affect
+ # the metadata for cached components and effects will persist
+ # for all future invocations
+ return $meta
}
-# TODO: rename this to template(), and change Template::Source to
-# Template::Template? (and also Template::Provider to
-# Template::Templates?)
#------------------------------------------------------------------------
# source($source)
#
# Method called to register a Template::Source object with the component
-# that identifies the source of the template document and provides a
+# that identifies the source of the template document. This provides a
# mechanism by which we can check to see if the template source has
-# changed since the template component was compiled.
+# changed since the template component was compiled (fresh()).
#------------------------------------------------------------------------
sub source {
@@ -112,6 +145,8 @@
# TODO: check paths, modification times, etc, to ensure we haven't
# got a mismatch between template source and compiled template code
+ # TODO: what about setting cache/store options from source?
+
return $self;
}
@@ -143,14 +178,19 @@
#------------------------------------------------------------------------
# cache($cache)
#
-# Component! Cache thyself if it is written in the options that you may.
+# Component! Cache thyself within the cache object passed to you as the
+# $cache argument, but only if a true value is declared in your internal
+# 'cache' option suggesting that you may.
#------------------------------------------------------------------------
sub cache {
my ($self, $cache) = @_;
+ # return internal cache flag if $cache object not passed
+ return $self->{ cache } unless $cache;
+
return $self->decline("cache option not set for $self->{ name } component")
- unless $self->{ options }->{ cache };
+ unless $self->{ cache };
defined $cache->set($self->{ id }, $self)
|| return $self->error( "failed to cache $self->{ name } component: ",
@@ -163,16 +203,21 @@
#------------------------------------------------------------------------
# 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.
+# And so it came to pass that the component checked upon its own
+# internal 'store' option and found it to have a true value, thereby
+# indicating the will of the component creator to be that it should
+# store itself persistantly in compiled form, using the store object
+# passed unto it as the $store argument. The word was good, and good
+# was the word, unless of course, an error occurred.
#------------------------------------------------------------------------
sub store {
my ($self, $store) = @_;
my ($source, $code);
+ # return state of internal store flag if $store object not passed
+ return $self->{ store } unless $store;
+
return $self->decline("store option not set for component: $self->{ name }")
unless $self->{ options }->{ store };
@@ -191,419 +236,37 @@
-# 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 parents {
- # TODO: return list of parents
-}
-
-
-
-
-#========================================================================
-# runtime!
-#========================================================================
-
-#------------------------------------------------------------------------
-# run(\%options)
-#
-# 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 : { @_ };
- my $copy = $self->enter($opts);
-
- eval {
- # maybe we should call this 'run' or have a separate
- # body method which we call if 'body' isn't defined?
-
- my $body = $self->{ body };
-
- # TODO: check body exists, accept other values
- # TODO: what about $copy->result(&$copy($copy))?
-
- if (UNIVERSAL::isa($body, 'CODE')) {
- $opts->{ result } = &$body($copy);
- }
- else {
- $opts->{ result } = $self->{ body };
- }
- };
-
- $copy->finish();
-
-# push(@$visited, $copy) if $visited;
-
- # TODO: not sure about this
- die $self->catch($@) if $@;
-
- return $opts->{ result };
-}
-
-
-
-#------------------------------------------------------------------------
-# call($name, \%options)
-#
-# Call another component.
-#------------------------------------------------------------------------
-
-sub call {
- my $self = shift;
- my $name = shift;
- my $opts = @_ && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ };
-
- local $opts->{ caller } = $self;
-
- my $component = $self->template($name) || return;
- $component->run($opts); # TODO: error, return value, etc.
-
-}
-
-
-
-# this is kinda redundant right now, but it's a hook in my head for later
-
-sub visited {
- return $_[0]->{ visited }
- || $_[0]->decline('not recording components visited');
-}
-
-
-
-#========================================================================
-# context management methods
-#========================================================================
-
-#------------------------------------------------------------------------
-# enter(\%options)
-#
-# Enter the current component by cloning a new component object with
-# a 'context' reference back to $self
-#------------------------------------------------------------------------
-
-sub enter {
- my $self = shift;
- my $opts = @_ && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ };
-
- # 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);
-
- # if the current component is tracking the components that it's
- # visiting, then all the components that it visits should do the same
- $opts->{ visited } ||= [ ] if $self->{ visited };
-
- # add context link to current component
- $opts->{ context } = $self;
-
- # set local variables to args passed
- $opts->{ variables } ||= $opts->{ args } || { };
-
- $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;
-}
-
-
-#------------------------------------------------------------------------
-# leave($component)
-#
-# Leave the component passed as the argument, previously created by
-# calling the enter() method.
-#------------------------------------------------------------------------
-
-sub leave {
- my ($self, $component) = @_;
-
- $self->debug("leave() $self->{ name }\n") if $self->{ DEBUG };
-
- # if $component->{ import }
- # $self->import($component->export());
-
- my $visits = $self->{ visited };
- push(@$visits, $component) if $visits;
-
- return $self;
-}
-
-
-#------------------------------------------------------------------------
-# finish()
-#
-# Notify the component that we've finished using it, allowing it to
-# notify its caller via the leave() method.
-#------------------------------------------------------------------------
-
-sub finish {
- my $self = shift;
- my $caller = delete($self->{ caller }) || return 1;
-
- $self->debug("finish()\n") if $self->{ DEBUG };
-
- # delete any other stuff we don't need to keep around
-
- $caller->leave($self)
- || return $self->error($caller->error());
-
- return $caller;
-}
-
-
-
-
-
-
-#------------------------------------------------------------------------
-# 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
-#========================================================================
-
-
-#------------------------------------------------------------------------
-# 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');
+ my ($self, $context) = @_;
+ my $template = $self->{ template }
+ || return $self->error('no template defined');
- return $self->{ cache }->{ components }->{ $name }
- ||= $self->search( components => $name, @_ );
-}
-
-
-#------------------------------------------------------------------------
-# template($name, \%options)
-#
-# Fetches a template matching the name provided as a first argument.
-# Delegates to the templates resource manager for the current context
-# and caches locally the template returned for subsequent use within
-# this context.
-#------------------------------------------------------------------------
-
-sub template {
- my $self = shift;
- my $name = shift;
- my $opts = @_ && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ };
- my ($templates, $template);
-
- $self->debug("template($name)\n") if $self->{ DEBUG };
-
- # see if $name is already a component
- return $name if UNIVERSAL::can($name, 'run');
-
- # first look in the local templates cache to see if the
- # requested template has already been used in this context,
- # otherwise fetch the template from the templates resource
- # manager. this isn't a real Template::Cache, just a hash
-
- my $cache = $self->{ cache }->{ templates } ||= { };
+ # barf if HOT
- # TODO: check for refs
-
- if ($template = $cache->{ $name }) {
- $self->debug("found $name in used templates cache\n")
- if $self->{ DEBUG };
- }
- else {
- if ($templates = $self->resource('templates')) {
- if ($template = $templates->fetch($self, $name, $opts)) {
- $cache->{ $name } = $template;
- $self->debug("stored $name in used templates cache\n")
- if $self->{ DEBUG };
- }
- elsif (! defined $template) {
- # report error
- return $self->error($templates->error());
- }
- }
- elsif (! defined $templates) {
- return; # error already set by resource() method
- }
- }
-
- return $template
- || $self->decline("$name template not found");
-}
-
-
-sub plugin {
- my $self = shift;
- local $self->{ THROW } = 'plugin';
- $self->search( plugins => @_ );
-}
-
-
-#------------------------------------------------------------------------
-# compiler($name)
-#
-# Search the compilers for one matching the name provided as an argument.
-# If $name is undefined then instead search for a 'default' compiler
-# which may return a compiler object or reference another by name
-# (e.g. default => 'tt3').
-#------------------------------------------------------------------------
+ # context vist()
-sub compiler {
- my $self = shift;
- my $name = shift || 'default';
-
- # check to see if $name is already a compiler
- if (ref $name) {
- return UNIVERSAL::can($name, 'compile')
- ? $name
- : $self->error("invalid compiler reference: $name");
- }
+ $context = $context->child({
+ templates => $self->{ templates },
+ variables => { },
+ });
- $self->debug("looking for $name compiler\n") if $self->{ DEBUG };
+ $self->{ _HOT } = 1;
- return $self->{ cache }->{ compilers }->{ $name } ||= do {
- my $compiler = $self->search( compilers => $name )
- || return $self->decline("$name compiler not found");
-
- # if $name is 'default' then the compiler returned may be the name
- # of another compiler, e.g. { tt3 => $blah, default => 'tt3' }
- if (ref $compiler) {
- $self->debug("got $name $compiler\n") if $self->{ DEBUG };
- }
- elsif ($name eq 'default') {
- $name = $compiler;
- $self->debug("looking for default compiler: $name\n")
- if $self->{ DEBUG };
- $compiler = $self->search( compilers => $name );
- return unless defined $compiler;
- return $self->decline("$name default compiler not found")
- unless $compiler;
- }
- else {
- return $self->error("invalid compiler returned for $name: $compiler");
- }
- $compiler;
+ my $result = eval {
+ &$template($context);
};
-}
-
-
-sub stash {
- my $self = shift;
- my $vars = $self->{ cache }->{ variables } ||= { };
- return Template::Stash->new( variables => $vars,
- component => $self );
-}
-
-sub catch {
- my ($self, $error, $output) = @_;
+ $self->{ _HOT } = 0;
- if (UNIVERSAL::isa($error, $EXCEPTION)) {
- $error->text($output) if $output;
- return $error;
- }
- else {
- # TODO: call throw() instead
- return $EXCEPTION->new('undef', $error, $output);
- }
-}
-
-
-
-#------------------------------------------------------------------------
-# DESTROY
-#
-# Call the finish() method.
-#------------------------------------------------------------------------
+ # context leave()
-sub DESTROY {
- my $self = shift;
- $self->debug("DESTROY $self->{ name }\n") if $self->{ DEBUG };
- $self->finish();
-# $self->debug("DESTROY\n") if $self->{ DEBUG };
+ die $context->catch($@)
+ if $@;
+
+ return $result;
}
-
1;
__END__
@@ -688,7 +351,7 @@
=head1 VERSION
-$Revision: 1.6 $
+$Revision: 1.7 $
=head1 COPYRIGHT