[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