Hi Paul,

This is a shorter reply to a much longer version that I started writing over the weekend. It quickly got long and rambling with many diversions so I decided it was better to write it up as a series of design documents. So at some point in the not-too-distant future I'll be addressing your points in more detail, but in the mean time, here are the salient points.

Starting at the end:

I know this email probably sounds really critical.

Not at all. Well, only in the constructively critical sense which I'm always happy to hear. I'm glad there is someone taking an acute interest in what's going on and asking questions that I need to make sure I've got good answers for.

Just as long as you appreciate (which I'm sure you do), that I might not have answers for you right here and now. If it's a question of me spending time writing code for you to look at/hack on or writing detailed explanations of what I'm going to do, then I'm sure you'll agree the former is preferable. Especially when it's generally easier and less ambiguous to write code than trying to write about it.

Anyway, nothing is set in stone in the architecture. Well, not much. But everything is certainly up for discussion, so keep it coming.

Template::Alloy internally uses _filename to store the "uri" of the file. Some have expressed concern that they don't want the full pathname of their file available to template users.

Yep, that's a good point.  You'll be able to configure the uri that the
template provider tags onto the local (to the INCLUDE_PATH) template name. This relates to the other point you made:

> I'm imagining you'll be wanting to have [% (file:my/file.tt).template %].

There won't be a file: or template: available from the template unless you
explicitly map a template provider/path to a namespace.

More on that to follow in a design document about providers, paths, filesystems, namespaces and how it all fits together. But in summary, yep, that'll be catered for by a translation/mapping layer between template and filesystem names.

I'm imagining part of the magic is a shared cache so that you don't have to load the filter list twice, or scan the plugin dirs twice, or load a template twice.

Yep. There will be a central hub, effectively an uber-context, rather similar to TT2's context. Most of the major TT components (template providers, filters, plugins, etc) will hang off here.

    print $t->process( thing => 'Badger' );

That is somewhat confusing. Template::process returns true if successful but Template::Template::process returns the processed data. You may want to change method names to keep things more clear.

Yes, it's confusing. And there's also the TT2 context process method which returns the output.

  $context->process('hello', name => 'Badger');

This is something that I'll address in detail in a design doc, but in summary, my plan is for all the process() methods to return the output by default.

  print $tt->process('hello', name =>'Badger');
  print $tt->template('hello')->process(name => 'Badger')

Errors will be throw as exceptions by default so there will be no need to explicitly check the return value (I've long since learnt the hard way why returning undef to indicate error is now considered bad practice).

Like TT2, you'll be able to specify the output:

  $tt->process('hello', { name => 'Badger' }, \*STDOUT);

Or use the print() method which will do what the TT2 process() method currently does.

  $tt->print('hello', name => 'Badger');

For backwards compatibility, there will be a host of ways to say that you want the default output to be STDOUT just like TT2. More on that in the "Configuration" design doc.

[% "Hello [% thing %]".template %] I can make this work - quite easily, but I think it is a security hole to expose the template objects to the language by default.

Yes, .template and .eval won't be enabled by default.  There's a big issue
relating to the homogenisation of virtual methods and filters.  Some virtual
methods and filters need access to the context, while other don't.  Then we
also have the matter of static vs dynamic filters.  But I'll save that for
yet another design doc.

This is nice and I'm sure it is infinitely flexible, but the common operation is that you want your nested templates to share the same configuration as your top level template/context.

Yes, that's right.  Most, if not all of that config will be stored in the
Template::Templates provider (hanging off the hub).

Part of the question is will Template users want and/or need to have the different get/setters for different templates. Again it is a neat feature, but I think that it will rarely be used.

It's more a question of allowing different template language authors the flexibility to implement their own strategies for getting/setting variables, templates, and other resources. The same goes for different implementations of a language. You might want to use a fast but somewhat feature-stripped version of TT3 for some of your templates, in order to eek out the best performance, for example, but use the regular version for the others. Or you might want to use some of your old TT2 templates alongside some shiny new TT3 templates, or some HTML::Template templates, or whatever.

In TT2 templates are limited to calling $context->process(), $stash->get(), etc. So they're effectively stuck with whatever implementations the T::Context and T::Stash objects provide them with.

In TT3, the template will be calling $self->process() and $self->get(), or $self->another_method_altogether(). Subclasses of T::Template will be free to re-implement/augment those methods however they see fit, or inherit the defaults from the base class.

However, there is the important issue of templates playing nicely with each
other.  So if they want to access each others variables, or pass variables
between them, then they will have to follow certain rules and stick to the
Playing Nicely Together API.

I think it would be interesting to do a straw poll to find how may people routinely use INCLUDE vs PROCESS. I don't ever use INCLUDE.

I do, often because I have no choice.

Part of the problem that requires me to turn to this particular solution is that there's no way for a template to declare local variables. If you use a temporary variable (even indirectly, like in a FOREACH loop) in a template that you PROCESS then it'll trample your current variable namespace.

So TT3 will have something like a MY directive to declare variables as properly local. I've got a prototype implementation which uses regular Perl 'my' variable in the generated code. It seems to work well and has the benefit of being more efficient (effectively allowing you to bypass the stash in simple cases).

So I think that will eliminate most of the problems that cause me to turn to INCLUDE. Can't speak for anyone else, though.

I think there's also a need for a version of PROCESS that only passes the
arguments to the template, and doesn't provide access to any of the other
variables currently defined. For those times when you want a template processed in "stand-alone" mode. This often goes hand-on-hand with the DEFAULT directive:

header:

  [% DEFAULT title='Hello World' %]
  ...blah blah [% title %]...

I can't think of a good name for it, so JFDI will have to do for now:

  [% JFDI header %]                      # vars => { }
  [% JFDI header title="My Example" %]   # vars => { title => ... }

In that first call, you really don't want the DEFAULT finding any other 'title' variable you happen to have lying around. So 'header' is processed with no variables defined whatsoever. In the second example, we explicitly pass a title, but that's all it gets.

some people will have to use INCLUDE because they'll be using templates they don't trust (sometimes I wonder about these people - who will admint to using templates they don't trust ?).

It's not so much that I'll use templates I don't trust.  But rather that I'll
use templates that may (or may not) use some variables that happen to clash.
This can happen, for example, when two different designers/developers wrote
the templates and happened to use the same 'x' variable.  Or when I'm re-using
some of my own templates from a previous project.  Or, in the pathological
case, when I'm trying to claw my way through a thousand templates written by 9
different people over 6 years of rather haphazard evolution of a web site/app.

Even adopting a naming convention doesn't really solve the problem.  You just
end up with variable clash over my_x instead.  Re-engineering the whole site
to organise variables properly is probably the more "correct" solution, but using INCLUDE is often the more appropriate one when a quick fix is required. Alas :-(

People in the know (like yourself) already know better than to use
INCLUDE, and take steps to avoid it except where strictly necessary.  It's
those people who don't know any better that we also have to account for. :-)

So I think some kind of local variable mechanism is a must and a way of
processing a template in an isolated environment (i.e. detached from the
current context) would be nice to have.

The part I find interesting about the TT3 get/set fallback proposal is that it takes complexity from one point (the localization of the variable stash) and it inserts it at another point -- the lookup of a variable.

Partly, yes.  One of the benefits (as I perceive them) are that you can reuse
the same (or similar) variable localisation mechanism for templates and other
resources if necessary.  There's no need for the current ugly BLOCKS hack in
TT2 if localisation is implicit.  Having said that, variables will always be a
bit special because they have to be fast.

Either way, there is similar overhead to the TT2 way of cloning the stash - but now it applies to PROCESS directives as well as INCLUDE directives.

No, definitely not. TT3 PROCESS will be as fast as TT2 PROCESS. It's an important feature of the underlying architecture that we can process a template in the current context (PROCESS), in which case all things are shared and there's no fallback cost, or in a child context (INCLUDE), in which case you get the safely blanket but take a performance hit.

The overhead hasn't been removed, it has only been relocated. And the overhead grows for variable access each additional nested scope.

First, let me reiterate that this only applies to INCLUDE.

The performance depends on a number of things:

  1) The implementation (I've got about 6 or 7 different variants which
     I'm using to explore the problem and benchmark different scenarios)

  2) How deep you nest.

  3) How many variables you have defined.

  4) What kind of variables you have defined.  Large chunks of text take
     significantly longer to copy than refs, for example.

  5) How many different variables you use in a template, what type
     they are and how many levels up they're defined.

  6) How many times you reference the same variable in a template.

  7) How many (and what kind) of INCLUDE parameters you use.

There's probably some other factors, too, but they seem to be the main ones. Different strategies win in different situations, so it'll be a question of picking the one that keeps most people happy most of the time. Which means we'll undoubtedly make some people unhappy.

But I'm not *too* worried about INCLUDE. I *am* worried about it, of course, but not half as much as I am about PROCESS because that's the one that claims to be fast. But given that I'm really not worried about PROCESS being fast (because it'll be doing no more than TT2 process or even less), then it follows that I'm not worried about INCLUDE at all because less than half of nothing is nothing.

So the undeniable logic is that there's nothing to worry about. Nevertheless, I have worried it enough about it to reach to the conclusion that allowing templates to define custom get/set/etc methods will be a Good Thing. I agree that most people won't use it most of the time (or not consciously at least), but I see it as an important "do your own thing" hook for people who want it.

The new TT3 way clones the entire context.

Nah. Just bits of it, if and only if you want them cloned, like when you use INCLUDE instead of PROCESS.

Do you think I'm made of memory?  :-)

Cheers
A



_______________________________________________
templates mailing list
[email protected]
http://lists.template-toolkit.org/mailman/listinfo/templates

Reply via email to