[TransWarp] Stream of Skin consciousness, continued...
Phillip J. Eby
pje at telecommunity.com
Wed Jul 30 17:53:18 EDT 2003
"Previously, on PJ's Design Rants..."
* web.ITraversable needs a getAbsoluteURL(), with the default
implementation adding the traversable's name to its parent's
getAbsoluteURL(), unless the name is None, in which case it adds the
* There will be a web.IResourceManager interface, with a way to obtain
static and dynamic (active and passive?) resources, by combining a path and
a package. Skins (and probably layers) will implement this
interface. Skins will likely also be ITraversables, and will probably
manage caching of resources retrieved from them.
* There needs to be a way to access the skin itself via URL traversal, e.g.
/++resources (++skin++?). This "way" must somehow be woven into the system
as a whole, preferably without just hacking it into the traversal algorithm.
* A security issue is presented by access to package-supplied data
resources, if packages to be published are not explicitly specified.
"...and now, on PJ's Design Rants..."
So we need to get the skin into the URL traversal. One way in which this
is tricky, is that the skin designation *itself* might be in the URL, e.g.
/thisApp/handheld vs. /thisApp/fullSize.
To accommodate these things, I think we'll need to change from using an
adapted application object bound to the skin as a traversal root. Instead,
we'll have to use the skin object itself as a traversal root. In this way,
a skin service that wanted skins to be based on the URL could return a
skin-selector as the root object. And, a skin can recognize the special
URL start path (/++resources++) when traversing, and return an adapter that
looks up resources. All other names would be delegated to an adapter for
the Skin's application object. This would simplify getAbsoluteURL() in the
default case, since traversables wouldn't need to check for their name
being None. The traversal start object (the skin) would of course have a
different getAbsoluteURL() implementation: one that simply returns
There's a new problem, though. We need to distinguish between the object
used for traversal (interaction.root) and the object that is in fact the
skin (interaction.skin), if we're going to use traversal to separate them.
OTOH, for HTTP-based requests, a skin service based on path can extract
those names from the traversal path (using get/setTraversalStack) and add
them to the URL base (using setApplicationNames). So, in that case, the
skin would again be the traversal root, from the POV of zope.publisher and
peak.web. With the skin URL being part of the request's base URL, there
isn't any need for the skin to have special getAbsoluteURL() handling; it
still just returns the request's idea of the base URL.
That's nice: skins' traversal logic won't be affected by how you get at the
skin, even if it's via a URL path. They still have to support the
/++resources++ path though, as we can't really push that bit into the
request's base URL. Presumably, the InteractionPolicy will define the name
used for ++resources++, and it will be available in the interaction. So a
skin's traverseTo() will just check if the name is equal to
interaction.resourcePrefix (or some such) and return its traverser for
that, or else delegate to a traverer wrapped around the app object.
Ironically, it appears that our skin service is misnamed, in some
sense. It will be returning a traversal root, that just happens to also be
a skin. I guess we should drop the root/skin distinction and just stick
with the skin. So Interaction.getApplication() should return self.skin
instead of self.root.
Okay, so interaction.root is dead; a quick check revealed it was only ever
used by the interaction anyway (apart from one test, easily fixed), and it
wasn't documented in the interface, either. I've moved the default root
creation code from interaction.root to the NullSkinService, so everything's
nice and tidy.
So how can we get from bindResource/whatever to the skin/resource
manager? If resources only ever implement the IWebPage interface, we could
have bindResource return a proxy that waits until render() time to actually
retrieve and invoke the resource. That's not a great way to do it,
though. It'd probably be best for skins to declare themselves a provider
of the IResourceManager interface and for the bindings to do a utility
That really sucks, because the interaction already has a 'skin' attribute
we could use. We just can't access it during the binding's
computation. If we change ITraversable.getObject() to pass in the
interaction, I guess we could manage a proxy mechanism. I'm not sure what
the performance trade-off for that is. getObject() will be called a *lot*
by DOMlets, whereas resource lookups will likely be infrequent. I guess
we'll live with the lookups.
So, Skins need to detect /++resources++ and fan that off to another
traversable. Everything else, it'll delegate to a surrogate application
I've been alternating writing this, and coding what I write about, so the
above is all basically done. What's not done, and still needs to be
defined, is IResourceManager: do we have one or two getResource()
methods? Do we ask for "resource package" objects and then traverse the
rest of the way? Maybe we should split this interface into ISkin, which
allows cached/aggregated paths, and IResourceManager, which is based purely
I'm going to leave these questions open for the moment, in order to think
them through a bit further. Meanwhile, I'm going to go ahead with checking
in what I've got so far.
More information about the PEAK