On Tue, 27 Aug 2002, Andy Wardley wrote:

> the answer

Yesterday I wrote a whole set of examples of how to do this.  I was all 
set to post them to the list and then my temperature skyrocketed to 104F so 
I lay in bed moaning instead.

Anyway, here's the explanation, better late than never:

Though there's probably some way that you can easily count the number of 
regexps that occur in a string from within the Template Toolkit, you 
might want to consider if it's the right thing to do.  There's no explicit 
operator for this - do you really want to be shoehorning in something 
designed to do something else into the task?  I don't think that'll come 
up with clean templates - we're not trying to re-implement perl here ;-).

I'd try and extend the Template Toolkit to do the right thing, to add an
explicit operator for the job.

The easiest way to do this is to add a subroutine to the stash that can do 
the calling when you run the process method:

  use Template;

  my $template = Template->new();
  $template->process( "template", { occurrence => \&occurrence } )
    or die $template->error;

  sub occurrence
  {
    my ($string, $regex) = @_;
    return scalar(() = $string =~ m/$regex/g);
  }

And then in the template:

  [% occurrence("abaab","a") %]

The problem with doing this is that it requires you to know in advance 
that you'll need to do occurrence counting.  If this is part of the display 
logic then the script shouldn't really have to do anything about it.  What 
you need to do is provide some system where by you can load dynamically 
such routines as you need them.

The Template Toolkit uses a plugin architecture to achieve this.  A simple 
plugin might look something like this:

  package Template::Plugin::Matcher;
  use base qw(Template::Plugin);

  use strict;
  use warnings;

  sub occurrence
  {
    my $self = shift;
    my ($string, $regex) = @_;
    return scalar(() = $string =~ m/$regex/g);
  }

Then the plugin can be created as a object in TT like so:

  [% USE Matcher %]
  [% Matcher.occurrence("abaab","a") %]

Note how the plugin creates a Matcher class which methods, in this case a 
static method that is used to do occurrence matching, can be called 
against.  This might seem a little counter intuitive to people who expect 
to just have the functions made available to them...It's like the 
difference between a exporter class and an object orientated class.  We can 
emulate the code we had above fully by having the *plugin* populate the 
stash with the subroutine (rather than doing it manually in the process 
method)

  package Template::Plugin::Occurrences;
  use base qw(Template::Plugin);

  use strict;
  use warnings;

  sub new
  {
    my ($class, $context) = @_;

    # place the occurrences subroutine in the stash
    my $stash = $context->stash;
    $stash->set("occurrence",\&occurrence);

    # return a blessed object
    return bless {}, $class;
  }

  sub occurrence
  {
    my ($string, $regex) = @_;
    return scalar(() = $string =~ m/$regex/g);
  }

Which leads us back to something more simple like this in our template:

  [% USE Occurrences %]
  [% occurrence("abaab","a") %]

Finally, the most complicated (but arguable easiest solution to use) is to 
create vmethods.  These are essentially methods that exist for all items in 
the stash.  You can call them on any object and they should do the right 
thing.

  package Template::Plugin::OccurrencesV;
  use base qw(Template::Plugin);

  use strict;
  use warnings;

  sub load
  {
    my ($class, $context) = @_;

    # set the vmethod in the stash
    $Template::Stash::SCALAR_OPS->{occurrence} = \&occurrence;

    # and the same for a list op so that single element lists
    # can be used as scalars
    $Template::Stash::LIST_OPS->{occurrence} = \&occurrence_list;

    # return the same class
    return $class;
  }

  sub occurrence
  {
    my ($string, $regex) = @_;
    return scalar(() = $string =~ m/$regex/g);
  }

  sub occurrence_list
  {
    my ($stringlist, $regex) = @_;
    return occurrence($stringlist->[0], $regex)
  }

This can then be used like this:

  [% USE OccurrencesV %]

  [% foo = "abaab" %]
  [% bar = ["abaab"] %]

  scalar: [% foo.occurrence("a") %]
  list:   [% bar.occurrence("a") %]

Note how we create two vmethods.  One for scalar operations and one for 
list operations.  This is to fit in with the doing the right thing thing 
that Template Toolkit does when it has single element lists - essentially 
you should be able to treat them as scalars.

Mark.

-- 
s''  Mark Fowler                                     London.pm   Bath.pm
     http://www.twoshortplanks.com/              [EMAIL PROTECTED]
';use Term'Cap;$t=Tgetent Term'Cap{};print$t->Tputs(cl);for$w(split/  +/
){for(0..30){$|=print$t->Tgoto(cm,$_,$y)." $w";select$k,$k,$k,.03}$y+=2}


_______________________________________________
templates mailing list
[EMAIL PROTECTED]
http://www.template-toolkit.org/mailman/listinfo/templates

Reply via email to