[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 
interaction's getAbsoluteURL().

* 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 
interaction.request.getApplicationURL().

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 
lookup.

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 
root traversable.

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 
on traversal.

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 mailing list