[Templates-cvs] cvs commit: TT3/lib/Template Context.pm
cvs@template-toolkit.org
cvs@template-toolkit.org
Thu, 16 Dec 2004 19:17:54 +0000
cvs 04/12/16 19:17:54
Modified: lib/Template Context.pm
Log:
* merged in new versions of template() and component() methods from
Template::Resource::Template
Revision Changes Path
1.12 +319 -17 TT3/lib/Template/Context.pm
Index: Context.pm
===================================================================
RCS file: /template-toolkit/TT3/lib/Template/Context.pm,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- Context.pm 2004/12/15 17:19:56 1.11
+++ Context.pm 2004/12/16 19:17:53 1.12
@@ -16,7 +16,7 @@
# modify it under the same terms as Perl itself.
#
# REVISION
-# $Id: Context.pm,v 1.11 2004/12/15 17:19:56 abw Exp $
+# $Id: Context.pm,v 1.12 2004/12/16 19:17:53 abw Exp $
#
#========================================================================
@@ -26,9 +26,10 @@
use warnings;
use Template::Resources;
use Template::Provider;
-use Template::Resource::Template;
-use Template::Scope;
+use Template::Template;
use Template::Utils;
+use Template::Path;
+use Template::Scope;
use base qw( Template::Scope );
# providers can define a fetch($context, $item) method which expects a
@@ -38,11 +39,14 @@
use constant FETCH => 'fetch';
use constant GET => 'get';
-our $VERSION = sprintf("%d.%02d", q$Revision: 1.11 $ =~ /(\d+)\.(\d+)/);
+our $VERSION = sprintf("%d.%02d", q$Revision: 1.12 $ =~ /(\d+)\.(\d+)/);
our $DEBUG = 0 unless defined $DEBUG;
our $ERROR = '';
our $THROW = 'context';
our $UTILS = 'Template::Utils';
+our $PATH = 'Template::Path' unless defined $PATH;
+our $TEMPLATE = 'Template::Template' unless defined $TEMPLATE;
+our $COMPONENT = 'Template::Component' unless defined $COMPONENT;
our $RESOURCES = 'Template::Resources' unless defined $RESOURCES;
our $TEMPLATES = 'Template::Provider' unless defined $TEMPLATES;
our $EXCEPTION = 'Template::Exception' unless defined $EXCEPTION;
@@ -73,7 +77,7 @@
|| return $self->error('failed to create templates provider: ',
$templates->error());
};
-
+
return $self;
}
@@ -138,26 +142,324 @@
sub id { $_[0]->{ id } }
sub name { $_[0]->{ name } }
sub time { $_[0]->{ time } }
-sub path { $_[0]->{ path } }
+
+sub path {
+ my $self = shift;
+ my $path = $self->{ path };
+
+ # TODO: handle get/set path and delete path_obj cache
+
+ # look for a component path if the context path is undefined
+ $path = $self->{ path } = $self->{ component }->{ path }
+ unless defined $self->{ path };
-sub new_template {
- my ($self, $name, @args) = @_;
+ # failing that, set the path to an empty string
+ $path = $self->{ path } = ''
+ unless defined $path;
+
+ return $path;
+}
+
+
+sub paths {
+ my $self = shift;
+ my $path = $self->{ path_obj } ||= do {
+ # TODO: do this via $self->object( path => ... );
+ # TODO: also think about configuration options like path delim, etc.
+ # create a path object for manipulating the path and cache it
+ my $pathclass = $self->pkgvar( PATH => $PATH );
+ $pathclass->new($self->{ path } || $self->path())
+ || return $self->error($pathclass->error());
+ };
+ my $paths = $path->paths(@_)
+ || $self->error($path->error());
+
+ return wantarray ? @$paths : $paths;
+}
+
+
+sub global {
+ my $self = shift;
+ my $context = $self;
+ my ($global, $parent);
+
+ # first we have to find the global hash or create it in the
+ # uppermost parent if it doesn't already exist
+
+ while (! $global) {
+ # return existing global hash in current context
+ last if $global = $context->{ global };
+
+ if ($context->{ parent }) {
+ # move on up to parent if there is one
+ $context = $context->{ parent };
+ }
+ else {
+ # otherwise create global hash when we can't go any higher
+ $global = $context->{ global } = { };
+ }
+ }
+
+ $self->{ global } ||= $global;
+
+ return $global unless @_;
+
+ my $name = shift;
+ return $global->{ $name } unless @_;
+
+ my $value = shift;
+ return $global->{ $name } = $value;
+}
+
+
+
+sub component {
+ my ($self, $name) = @_;
+ my $cclass = $self->pkgvar( COMPONENT => $COMPONENT );
my $debug = $self->{ DEBUG };
- $self->debug("template($name, ", join(', ', @args), ")\n") if $debug;
+ # TODO: check local component cache
- # get the template resource manager
- my $resource = $self->{ resource }->{ template }
- || $self->resource('template') || return;
-
- # call the resource's fetch() method
- return $resource->fetch($self, $name, @args)
- || $self->decline($resource->error());
+ my $global = $self->{ global } || $self->global();
+ my $paths = $global->{ paths }; # id map for static paths
+ my $cache = $global->{ cache }; # in-memory component cache
+ my $store = $global->{ store }; # on-disk component store
+ my ($component, $template, $id, $path, $names, $static);
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # Identify
+ # First we need to figure out exactly which template is wanted,
+ # resolving ambiguous names (e.g. '.../foo') which can have
+ # multiple possible locations. We're looking for a template id,
+ # to uniquely identify the template.
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ if (ref $name) {
+ # $name may already be a component
+ return $name if UNIVERSAL::isa($name, $cclass);
+
+ # let template() handle a reference to something else,
+ $template = $self->template($name) || return;
+ $id = $template->id();
+ }
+ else {
+ # expand $name template name into full path name(s) computed
+ # relative to the current context path
+ my $names = $self->paths($name) || return;
+
+ foreach $path (@$names) {
+ $self->debug(" - path: $path\n") if $debug;
+
+ # If there is a global paths cache and the requested path
+ # is an absolute one then it is static. We can cache the
+ # id returned for it so we don't have to look for the
+ # template again next time we use it
+ $static = $paths && $path =~ m[^(\w+:)?/];
+
+ if ($static && ($id = $paths->{ $path })) {
+ $self->debug(" - mapped static path '$path' to id '$id'\n")
+ if $debug;
+ last;
+ }
+ elsif ($template = $self->template($path)) {
+ $id = $template->id();
+ $self->debug(" - sourced template '$path' with id '$id'\n")
+ if $debug;
+ # save static path for next time
+ $paths->{ $path } = $id if $static;
+ last;
+ }
+ }
+ # we should have a valid template id, or we can't find it
+ return $self->error("template not found: $name")
+ unless $id;
+ }
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # Cache
+ # now we've got a template id we can look in the memory cache to
+ # see if we already have a compiled component for this template,
+ # If we do we check that it's fresh compared to its source template
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ if ($cache && ($component = $cache->get($id))) {
+ if ($component->fresh()) {
+ $self->debug(" - found cached template\n") if $debug;
+ return $component;
+ }
+ else {
+ $self->debug(' - cached component is stale: ', $component->error())
+ if $debug;
+ }
+ }
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # Store
+ # If we didn't find the component in the cache then we look for
+ # it in the store. If we find it then we restore its link to a
+ # source template so that it can subsequently check its
+ # freshness. If we haven't got a source template (e.g. if we used
+ # an id fetched from the global cache for static paths) then we
+ # need to go and fetch the complete template first.
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ if ($store && ($component = $store->get($id))) {
+ # fetch source object if we need to
+ $template ||= $self->template($path) || return;
+
+ # notify component of its source object
+ $component->template($template);
+
+ if ($component->fresh()) {
+ $self->debug(" - found stored template\n") if $debug;
+ $component->cache($cache) if $cache;
+ return $component;
+ }
+ else {
+ $self->debug(' - stored component is stale: ', $component->error())
+ if $debug;
+ }
+ }
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # Compile
+ # If we haven't got a cached/stored component then we have to
+ # create one from the template. If we haven't got a complete
+ # template yet (e.g. we used a static path to get the template id)
+ # then we need to get fetch it now.
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ $template ||= $self->template($path) || do {
+ # this should probably never happen, but never say never....
+ # we could get an id for a template one minute, cache it in
+ # the static paths, but then some time later, the provider
+ # could withdraw it. But if that happens then there shouldn't
+ # be a global paths cache for static paths in the first place
+ delete $paths->{ $path } if $static;
+ return $self->error("template not found (cached id error): $path");
+ };
+
+ # cache the template id for a static path
+ $paths->{ $path } = $template->id() if $static;
+
+ # now we should have a valid source ready to be compiled
+ $component = $template->component(context => $self)
+ || return $self->error($template->error());
+
+ # give the component a chance to cache/store itself
+ $component->cache($cache) if $cache;
+ $component->store($store) if $store;
+
+ return $component;
+}
+
+
+sub template {
+ my ($self, $path) = @_;
+ my $debug = $self->{ DEBUG };
+ my $source;
+
+ $self->debug("template($path)\n") if $debug;
+
+ my $tclass = $self->pkgvar( TEMPLATE => $TEMPLATE );
+
+ if (ref $path) {
+ # $path can be a reference to something
+ $source = $path;
+ }
+ else {
+ # otherwise it's the name of a template which we look for
+ # in the templates for the context and any parents
+ $source = $self->find( templates => $path )
+ || return $self->decline("template not found: $path");
+ }
+
+ # $source should be a Template::Template object (or whatever
+ # the current value of $tclass is), a code reference, or a
+ # text string or reference to one, all of which get upgraded
+ # to Template::Template objects if necessary.
+
+ if (UNIVERSAL::isa($source, $tclass)) {
+ $self->debug(" - template source is already a template object\n")
+ if $debug;
+ return $source;
+ }
+ elsif (UNIVERSAL::isa($source, 'HASH')) {
+ # $source is a hash reference
+ $self->debug(" - constructing template object from hash reference\n")
+ if $debug;
+
+ $source->{ path } = $path
+ unless defined $source->{ path };
+
+ $source->{ id } = "hash:$path"
+ unless defined $source->{ id };
+
+ return $tclass->new($source)
+ || $self->error($tclass->error());
+ }
+ elsif (UNIVERSAL::isa($source, 'CODE')) {
+ # $source is an anonymous subroutine reference
+ $self->debug(" - constructing template object from code reference\n")
+ if $debug;
+
+ return $tclass->new({
+ id => "code:$path",
+ path => $path,
+ time => CORE::time(),
+ code => $source,
+ text => '',
+ fresh => 1,
+ cache => 0,
+ store => 0,
+ }) || $self->error($tclass->error());
+ }
+ elsif (UNIVERSAL::isa($source, 'SCALAR') || ! ref $source) {
+ # $source is plain text or a reference to a scalar
+ $self->debug(" - constructing template object from template text\n")
+ if $debug;
+
+ return $tclass->new({
+ id => "text:$path",
+ path => $path,
+ time => CORE::time(),
+ text => $source,
+ fresh => 1,
+ cache => 0,
+ store => 0,
+ }) || $self->error($tclass->error());
+ }
+ else {
+ return $self->error("invalid template source: $source");
+ }
}
+sub process {
+ my ($self, $paths, $params) = @_;
+ my $output = '';
+
+ $paths = ref $paths eq 'ARRAY' ? $paths : [ $paths ];
+
+ $self->debug("include(", join(', ', @$paths), ")\n") if $DEBUG;
+
+ local $self->{ THROW } = 'template';
+
+ foreach my $path (@$paths) {
+ my $component = $self->component($path)
+ || return $self->error($self->error());
+ my $result = $component->process($self, $params)
+ || return $self->error($component->error());
+ $self->debug("processed $path: $result") if $DEBUG;
+ $output .= $result;
+ }
+ return $output;
+}
+
+
+
sub catch {
my ($self, $error, $output) = @_;
@@ -232,7 +534,7 @@
=head1 VERSION
-$Revision: 1.11 $
+$Revision: 1.12 $
=head1 COPYRIGHT