[TransWarp] Binding templates and resources
Phillip J. Eby
pje at telecommunity.com
Tue Jul 29 22:41:39 EDT 2003
At 07:02 PM 7/29/03 -0400, Phillip J. Eby wrote:
>Resources must be referenceable in application URLs. So we're going to
>have to reserve a URL name (tentatively '++resources++') to get at
>them. A resource will be accessed via
>'/++resources++/some.package/resourceName'. There will probably be a
>configuration property or properties to control this.
This is slightly more complex than it first appeared. For one thing, we
don't want to allow traversing to arbitrary package names, if this results
in module imports. That's a security problem, since it opens up anything
on PYTHONPATH to execution. So it's going to be necessary to declare
specific properties to enable access to package resources. :(
It also has occurred to me that there may be circumstances where a package
needs to assert other global items than "resources". And, our proposed
mechanism hasn't been thought through for non-filesystem resources, e.g.
skins stored in a database. Also, as I've been trying to move to more
detailed design, I've been frustrated by the practical difficulties of
layering heterogeneous location types - i.e., layering a skin-style
organization atop the "included-in-the-package" style.
Part of the problem is that the desired namespace mixes a number of
"co-ordinates": skin, package, and resource/template name. Skins need to
be composable from multiple layers, and property definitions aren't well
suited to this.
Perhaps if I created a "multiPropertyMap" type that aggregated multiple
PropertyMaps, I could address that. Perhaps specifically what I should do
is create a Skin class that did such aggregation by delegating
configuration lookups to a sequence of "layers". Downside: layer instances
wouldn't be sharable, since they'd effectively be doing
caching. Ick. Maybe we need to drop the idea of caching at the property
If there's an explicit Skin object, though -- even for "unskinned"
applications -- we can leave the caching to it. This simplifies
configuration to a large extent, since we don't need to account for caching
in every property definition. Now, any "special" handling (e.g. loading
resources from a DB) can be delegated to a "layer" component, which need
only offer properties, in order to override an individual resource or template.
The only caveat here that I see is that resource/template loaders may need
to be careful how they set parent components for the objects they create,
or else bindResource/bindTemplate will need to explicitly delegate lookups
to the skin. That isn't a bad idea in principle, since it's quickly
accessible as 'interaction.skin'. In practice, alas, the 'bind*'
operations don't necessarily have access to the current interaction,
without doing a utility lookup that would hit the skin first anyway! Ugh.
But, I think the benefits of having Skin.getResource() and
Skin.getTemplate() methods would be very, very nice. First, we encapsulate
the lookups behind an interface that can be independently re-implemented,
without forcing the specific mechanism on everybody. Second, the default
Skin implementation can delegate to Layers, with a default Layer to
implement the package-based rules, and it can cache the return values -- or
not. (A DB-backed skin processor implementing a large-scale
private-labelling scheme may need to cache things differently than a small
set of filesystem skins.)
Downsides: pushing caching into the skin means that the lower-level
components can't update a resource's "freshness". (OTOH, the Skin could
perhaps adapt resources to a "caching-hints" interface, and we could add
So far, discussing "resources and templates" is also rather tedious; it may
be that we should talk about "static and dynamic" resources, or maybe even
"data and code" (data and executable?) resources. The difference in each
case is that the first kind of thing (resource, static resource, data
resource) is effectively a singleton - it has a unique URL (or at least
path from the application root). Some examples of these "data resources"
The second kind of thing (template, code resource, dynamic resource) is
method-like, in that it doesn't have a unique URL, and it can be accessed
only via objects that bind it. Currently, the only such things we have are
PWT templates, and I don't currently have anything else in mind to build,
but I dislike locking Skin into this with a 'getTemplate()' method.
Anyway, having a 'web.IResourceManager' interface for Skin to implement is
probably a really good idea. It should also make it easy to hook up the
/++resource++ namespace, by relatively simple delegation to the skin.
Still left open, is the question of URL management for data resources. In
part, this is because absolute URL management in general is still open.
Probably, ITraversable needs a 'getAbsoluteURL(interaction)' method. Skins
and data resources should be ITraversables themselves. The default
implementation of getAbsoluteURL() would be to get the parent traversable's
absolute URL and concatenate the local traversable's name. (If the name is
None, this indicates that traversal is at the root, so the interaction
should be asked for a base URL.) Data resource's parents would be the
directory, package, and skin components that live above them, with the skin
supplying a base absolute URL based on /++resource++.
This will deal with all our absolute URL needs for singletons such as data
resources and application service objects (e.g. Specialists). For
individual model.Element objects, we'll need to be able to find their
specialists, which is still an open issue, but at least we can define the
It appears that Skins will work by way of path traversals as well, with the
getXResource() methods simply translating to path traversals based on the
input parameters, with caching of fully-traversed paths. It looks, too,
like we'll finally need that multi-traversing decorator that simultaneously
traverses more than one path.
This concludes another rambling PEAK design stream-of-consciousness. :)
More information about the PEAK