[TransWarp] A layout framework, and thoughts on DOMlet data specifiers

Phillip J. Eby pje at telecommunity.com
Fri Aug 8 17:14:04 EDT 2003


In order to enable applications to re-use page layout, peak.web needs 
DOMlets that support this, similar to Woven or ZPT macro facilities, or to 
Struts' "Tiles" framework, or OFBiz's "Regions" framework.

The basic idea is simple: have a way to specify a DOMlet that means "use 
such-and-such layout to format my contents".  The "define" attributes in 
the body of the DOMlet will provide values to fill the "regions" in the 
"layout" that is chosen.

On the layout side of the equation, we assume that any peak.web template 
may be used as a layout; it will simply contain "region" DOMlets that will 
render the content supplied by the caller, or else display their existing 
content as a default.

So far, so good, and it even seems reasonable to assume that the logical 
names for the DOMlets involved should be "layout" (used by the caller to 
"lay out" the defined content), and "region" (to identify a place in the 
layout where the caller's defined content to be inserted).

But the devil is in the details.  Do we perform the substitution at runtime 
or compile-time?  If at runtime, do we also select the layout at runtime, 
or do we select it at compile time?  In either case, how is a layout selected?

When I had conceived of resources as being properties of a skin, 
incorporating layout templates into the system seemed almost 
trivial.  Since DOMlet factories are properties, all that seemed needed was 
to specify a DOMlet name that mapped to another template.  This doesn't 
work any more, at least not directly.  Currently, you'd have to take the 
extra step of mapping a property to a resource lookup, in order to do this 
now.  Or, presumably, we could have our hypothetical 'layout' DOMlet accept 
a resource path as its parameter, and do the lookup.

There are some downsides to this approach.  First, resource paths have to 
start with '/++resources++/some.package', which means that layout specs are 
verbose.  Second, the '++resources++' part is in principle changeable, so 
it's potentially not "portable" between apps if a template uses it.  This 
goes directly against one of the goals for the layout system: that it 
should be easy to plug an "off-the-shelf" component into your application, 
preferably without having to rewrite all its templates to fit into your GUI 
design.

This same ++resources++ problem also exists when you want to refer to say, 
an image resource or other static file, so it's not exclusive to the layout 
issue.  I wonder if perhaps we should just permit '//' to be used as a 
shortcut for '/++resources++/'  (or whatever the local app's equivalent 
is).  This would fix the portability problem and at least part of the 
verbosity problem.  We still need '++resources++' or its equivalent in 
order to allow access via the web, but for internal use the shortcut isn't 
bad.  Interestingly, it echoes the '//' of URLs' "naming authority", and 
it's followed by a qualified name.  It might be that DOMlet specs would now 
look "too much" like URLs, but we'll just have to try it, I think.

Okay, so let's look at the next part of the URL: the package 
name.  Certainly, it's important for dealing with most static resources, so 
as to disambiguate similarly named resources (e.g 'editForm') existing in 
multiple packages.  But, for layouts, we seem to want to obtain these from 
the application.  If package A and B each request 'standard_page_layout' 
from their own package names, how can the creator of application C get them 
both to use C's template for the layout?

This seems to imply that there should be a standard for what packages 
layouts are considered to come from, or else there needs to be a way to 
access them that isn't dependent on package namespaces.

We could of course, forgo the use of standard paths for layout selection, 
use a property name, and add some way to conveniently map from a property 
name to a resource.  What concerns me about this approach, however, is that 
if every DOMlet ends up having its own rules for interpreting path 
arguments, the overall system becomes a little too context-dependent for 
easy understanding when reading template text.

Perhaps we need to support using PEAK URLs in DOMlet specs, although that 
almost seems even more chaotic, increasing the dynamic expressiveness of 
the language too much.  I mean, it definitely seems crazy to allow one to 
specify a database connection as the target of a DOMlet!

Of the current PEAK URL types, about the only one that seems to make any 
sense is the "config:" scheme, which would allow use of properties.  This 
would end up looking like:

<sometag pwt:domlet="layout:config:foo.bar.baz/">

Which is beginning to look an awful lot like code to me.  I don't see as 
much trouble with:

<sometag pwt:domlet="layout://foo.bar/baz">

Hm.  It almost seems to me as though we could use Zope 3's idea of path 
parameters, e.g.:

<sometag pwt:domlet="layout:foo.bar.baz;ns=property">

But that's pretty ugly too.  OTOH, some of the ideas for specifying such 
properties wouldn't work because they were intended for externally usable 
URLs, so

<sometag pwt:domlet="layout:(property)foo.bar.baz">

Might be workable too.

I should back up here for a moment, to expand on my goal a bit.  I think we 
want to have some set of standard namespaces that are used in templates, 
and accessible through an easy-to-use, easy-to-read syntax.  If at all 
possible, DOMlets should use only this mechanism to identify the subject of 
their operations, and there should be a small number (ideally a fixed 
number as well) of kinds of data specifications.

However, I'm not positive this is achievable.  For example, our proposed 
"region" DOMlets will look up a named definition, but unless we have a 
"parameter" namespace defined just for this one kind of DOMlet, this won't 
be consistent with our overall pattern.  Indeed, every time a new DOMlet 
comes up with special requirements, we'll be extending that set of 
namespaces, *and* we'll also be increasing needless verbosity, since it's 
likely that most of these DOMlets with "special" namespaces will only ever 
use the special namespace.  (E.g., region DOMlets would always use the 
parameter namespace, making it redundant to say so.)  So, it seems to make 
little sense to try to have a uniform addressing mechanism in the data 
specifier.

That having been said, it may still be useful to try to increase the 
expressiveness of DOMlets' default interpretation of data specifiers, as we 
will do by allowing // in place of /++resources++.  Perhaps we should make 
it possible to specify property names, somehow, or perhaps allow a property 
name to specify a path that should be used.  Perhaps this would look like:

<html pwt:domlet="layout:(layouts.standard)">

Meaning "use the path specified by the template's 'layouts.standard' 
property".  Since an application could define this on a global or per-skin 
basis, we now have a simple method to achieve indirection for anything that 
needs this kind of configuration.

I'm not sure what I think of this yet, as I don't know of any obvious uses 
for it besides layouts.  I definitely lean towards making it a compile-time 
substitution replacing the property name with the path found in that 
property name.  Anyway, it seems mainly useful as a way to allow templates 
to refer to "application-level" resources without having to know who the 
"application" is.  In principle, it could also be used for relative paths, 
but that doesn't seem very useful for anything.

For any given DOMlet, there are several interesting "contexts" from which 
things might be looked up.  At base, there are its 'data' and 'state' 
objects, and there is also the DOMlet itself, and its surrounding component 
hierarchy.  Currently, data specifiers just traverse the 'data', and there 
is no way to look up properties, utilities, or do other acquisition-like 
things, on the data, the state, or the DOMlet itself.

No matter which way I turn on this subject, I bump into something I don't 
like.  If I make it possible to specify different kinds of lookups, 
templates will become more code-like.  If I don't make it possible, then 
DOMlets that can reasonably support  more than one kind of lookup will 
spawn variants to handle them.  *sigh*.

Maybe I'm going about this entirely the wrong way.  Maybe the solution 
isn't to allow different interpretations in general, but rather to support 
DOMlets that do.  To put it another way...

<div pwt:domlet="property:layouts.standard">
     <div pwt:domlet="layout">
         <div pwt:define="region1" domlet=":..">
             ...
         </div>
     </div>
</div>

The idea here is that by using a DOMlet to "escape" into another namespace, 
you could still use '..' to "back up" to the previous traversal 
location.  Alas, as you can see, it requires *lots* of extraneous markup to 
get there and back again.  :(  OTOH, it's intended to be a way to 
supplement normal traversal in unusual situations.  On the *other* other 
hand, it's really not useful for normal situations.

I think we'll have to feel our way on these points for a while, as the best 
compromise isn't clear yet.  At this point, I'm leaning towards saying that 
the interpretation of a data specifier is strictly up to the DOMlet, though 
it's recommended that they be TraversalPaths if at all practical.

So, back to the layout framework for a minute.  We haven't said what the 
"data" context is when "define"s run.  Do they receive the "data" that the 
layout was accessing, or the data that the calling template was processing 
when the layout was invoked?  I.e., if I have:

<html pwt:domlet=":spam">
<div pwt:domlet="layout://foo.bar/standardLayout">
     <div pwt:define="region1" pwt:domlet="text:baz">
        ...

Then, do I know that in "region1" I am referring to 'spam/baz'?  Or could I 
be referring to 'spam/diddly/baz' if there is a reference to 'diddly' in 
the 'standardLayout' template, that encloses the "region" DOMlet for 
region1?  E.g., if the layout template looks like this:

<html pwt:domlet=":diddly">
     <div pwt:domlet="region:region1">
         ...

should we assume that the defined "region1" is then invoked with 
'spam/diddly' instead of 'spam'?  If we do this, it *might* avoid the need 
for the regions to repetitively traverse to subobjects that are naturally 
required by those regions.  On the other hand, it seems to "leak" 
information from the template to the caller, in a way that makes the caller 
dependent on easily broken details of the template's 
implementation.  That's probably worse than some possibly-redundant lookups.

Another benefit of keeping the current data context when inserting regions 
is that you can read a template and understand what data it uses, without 
having to refer to the layout template.  For this reason, it seems that 
execution state (the 'state' parameter of 'renderFor()') should similarly 
revert when using a region.  Thus, once again, the behavior of DOMlets 
appearing in a region definition will in no way be influenced by anything 
appearing in a layout template.  While this arrangement is less flexible, 
it is easier to reason about, and there is no way for a change in the 
layout template to negatively impact a working template that simply uses 
the layout.

Alright.  I think that we've now completely specified how the "layout" 
framework should operate, with the exception that we need a convenient way 
to provide some kind of indirection for selecting layouts, e.g. via a 
'(property.name)' syntax, or some other mechanism.

Thoughts, anyone?




More information about the PEAK mailing list