[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