I have an application I am working on in TT2 that is attempting to perform behavior similar to HTML::Mason (but not exactly) in locating templates. In order to do this, each template remembers where it was loaded from, and sets its working directory to that directory. Templates loaded without any leading path qualifiers, which normally causes INCLUDE_PATH to be searched in TT2 are searched for as if INCLUDE_PATH were the current directory and every directory above it (up to an APP_ROOT config setting that is. Also, templates loaded with an absolute path are treated as relative to APP_ROOT, making the whole tree relocatable. This requires a fairly grotesque hack, but it's reasonably trivial and not really a problem yet). So if a template /foo/bar/baz/blah.tmpl contains an [%INCLUDE thing.tmpl%], it will search for thing.tmpl in /foo/bar/baz, then /foo/bar, then /foo, then /, but won't actually change the working directory. This is fairly similar to HTML::Mason's autohandler or Zope's acquisition behavior.
Anyway, to manage this, I had to override Provider::_load and have it stick an extra member into Document object metadata (for which I had to create a subclass of Document), namely _epath (stands for "effective path"). When documents call Context::visit(), passing themselves in, the overridden Context pushes it onto an internal _DOCSTACK member, and the leave() method pops it. Finally I had to change the interface between Context and Provider to have the Context pass itself in as a parameter to Provider::fetch so the provider could call a working_directory() method on the context in order to implement the proper behavior in _fetch_path(). All this was reasonably hunky dory until the internal interface changed. Documents no longer pass themselves to the visit() method on their context, they only pass their defblocks in. No document stack now. I could use perl's 'caller' function, but that's a grotesque hack I have never liked, and it fails in the face of inheritance. Now since Document is a reasonably private item (only Provider ever instantiates them) and I'm subclassing Context, Provider, and Document anyway, I could simply revert the interface back to the way it was, but I'm getting less comfortable with it -- it's feeling more like an outright fork of TT2 than an enhancement, because I'm breaking the interface contract all over the place. In TT3, I could probably have the affected Provider subscribe to visit() and leave() events to manage the document stack directly (assuming this does cause an event notification?), but that's for a future design. I see quite a bit of value in maintaining a Document stack along with the block stack anyway. It would allow for tracebacks in error handling for one, as well as this mason-like behavior. I would also like to see Providers have some way of getting at their context (again without using the ugly hack of 'my $context = caller;'). I'll submit a patch to this effect if you're receptive to the idea. __________________________________________________ FREE voicemail, email, and fax...all in one place. Sign Up Now! http://www.onebox.com
