[PEAK] sitemaps: <content> in non-root <location>
Phillip J. Eby
pje at telecommunity.com
Fri Jan 7 16:26:57 EST 2005
At 07:25 PM 1/7/05 +0100, Radek Kanovsky wrote:
>On Fri, Jan 07, 2005 at 01:12:25PM -0500, Phillip J. Eby wrote:
>
> > >So sitemaps.addHelper should be fixed
> > >too:
> > >
> > > def addHelper(handler,helper):
> > > def helped_handler(ctx, ob, namespace, name, qname,
> > >default=NOT_GIVEN):
> > > ob = helper(ob)
> > > return handler(
> > > ctx.clone(current=ob, viewHandler=ctx.viewHandler),
> > > ob,namespace,name,qname,default
> > > )
> > > return helped_handler
> >
> > Right; I see what you mean. I'm wondering if maybe I should just add
> > 'viewHandler' to the list of attributes that are automatically
> cloned. But
> > I'm not sure if there are maybe clonings that change 'current', but where
> > the viewHandler should *not* be kept. I'll have to take a look at that.
>
>I have roughly checked all clone() calls in peak.web and there is
>probably another clone() call in templates.Expects class that need fix.
>Maybe, some global fix would be better.
Yeah, I'm now thinking that something more like your original clone_from
proposal would actually be better, although I think it should be eager
rather than lazy; i.e. it should be done at cloning time rather than
waiting until viewHandler is used, because I don't want to keep around the
reference to the cloned context.
I've just checked in a more global version of the change. I don't want to
dig too much into this for now, because I expect view handling to be
refactored at some point soon to use generic functions anyway.
In particular, I'm thinking that the traversal context constructor might
call some sort of "traversedTo(ctx)" generic function that will dispatch on
ctx.current to see if anything special needs to be done (like setting the
view handler or changing the policy object to implement different security
rules in context). (But, I am not yet sure if this is a good idea,
performance-wise.)
Actually, I'm also thinking that 'handle_http' will become a generic
function that will be hookable with before/after/around methods to do
things like require a login, start a session, etc. This will get rid of
the need for 'IWebTraversable.beforeHTTP', and indeed the other
IWebTraversable methods (getURL and traverseTo) are probably also better
served as generic functions, essentially leaving no reason to even have the
IWebTraversable or IHTTPHandler interfaces, just some useful classes that
define useful methods for those generic functions. IWebException might go
away as well, since 'handleException()' could easily also be a generic
function.
But making those interfaces into generic functions won't have significant
performance impact, since they are called only a few times (at most once
per URL level) per request. It's traversal contexts that might be created
hundreds or even thousands of times to generate a page. OTOH, probably
contexts spend most of their time right now in _setup, __init__, and most
especially all the functions called by traverseName, so these are
more ripe for future optimization anyway. So, one single-dispatch generic
call in __init__ probably won't hurt that much, especially if the
IViewService adaptation can be replaced by it. I.e., there'll still be
only one adaptation happening over the context's lifetime to deal with
location-specific stuff like possible changes of view service, interaction
policy, skin service, etc.
Hm. Actually, if traversal itself is a generic function, then all of those
issues might be embeddable as various methods, such that we can kill
multiple birds with one stone. IOW, currently there are lots of functions
in peak.web with the INamespaceHandler signature, and many of these might
be able to be methods of a generic function, rather than being looked up
and dispatched through the other means currently in use. For example:
@traverseTo.when("ns=='view'")
def traverseView(ctx, ob, ns, name, qname, default=NOT_GIVEN):
handler = ctx.viewHandler(name,ob)
if handler is not None:
return handler(ctx, ob, ns, name, qname, default)
if default is NOT_GIVEN:
raise errors.NotFound(ctx,qname,ob)
return default
Although, more to the point, if there's a single "traverseTo" generic
function, you would just encode views as when "ns=='view' and
name=='someView' and isinstance(ob,SomeType)", and you wouldn't need the
above code at all. (Indeed, you could possibly even include an "and
ctx.allows(ob,name)" or other security test as well, allowing different
views to be selected based on access rights.) For that matter, you could
include tests based on the policy instance and URL path in order to have
location-specific views, without having to mess with changing viewHandler
around.
The main downside to this approach is that if you have a global generic
function, then it will end up with references to all these things. It
would be better to be able to have the interaction policy have its own
generic function instance specific to the application. But, the default
conditions have to be loaded somehow, and any other "global" rules that get
imported later should still become active. This seems to me to mean that
generic functions (and Dispatchers) need some kind of ability to allow
others to register and get notified when methods are added to them. That
way, you could then clone predicate-dispatch generic functions in the same
way as you can currently clone single-dispatch generic functions. The hard
part is eliminating ambiguity between the "inherited" and "overridden"
rules between the base function and the derived (cloned) function. I guess
if there is an expression/test for what GF a method is added to, you can
include this in the signature, and therefore make it part of specificity
testing, such that a method implied in a derived GF is thereby "more specific".
OTOH, none of that is needed for prototyping a GF-based peak.web, and even
for using it in all but the most demanding situations. So we could test
out the ideas using a single global GF and see if we like it, before adding
GF cloning to the dispatch package.
Anyway, ISTM that a lot of stuff in peak.web could be boiled down to five
generic functions (not counting the two from peak.security):
* handle_http(ctx) (aka ctx.renderHTTP())
* traverseTo(ctx, ob, ns, name, qname, default=NOT_GIVEN) (which would be
invoked by ctx.traverseName() after doing parseName())
* onTraverse(ctx) (allow objects to do special things like change policy,
user, skin, environ contents, etc. upon traversal to them)
* handleException(ctx, exc_info, retry_allowed=True)
* getURL(ctx,ob) (allowing us to get rid of kludgy 'peak.web.url' view)
And this would eliminate at least five interfaces, and perhaps at least as
many adapters and other classes, while expanding flexibility to do things
like notice that the request is XML-RPC and marshal that differently than a
regular POST, etc.
Of course, there are probably some other generic functions that would be
useful, like a 'getUser' that would do what the old LoginManager would do,
i.e. extract credentials from a request, then look up users and attempt to
authenticate them. And maybe something like 'getMenu' to obtain registered
menu items for an object, and so on, and so on.
Also, I think the sitemap language needs to grow tags to allow defining
methods for some of the functions above. It already has a way to specify a
kind of method for 'getURL' for content items, but a more general facility
might also be helpful. Similarly, a way to specify onTraverse/handle_http
hooks would also be important.
Finally, there would be plenty of predicate functions needed as well, to
encapsulate ideas like "there are names left to traverse" or "this is an
XML-RPC request", in order to make it easier to write rules that depend on
these things.
But all this is still just in early stages of thought, and I'm not sure
if/how much all this will actually simplify peak.web, as opposed to
extending capability or flexibility. Or more precisely, I'm not sure it's
going to reduce the code size or complexity. I do think it will reduce the
complexity of *understanding* the framework, if we replace lots of nouns
(interfaces) with a handful of situational verbs (generic functions). Even
more specifically, it's easier to see which generic functions I should hook
into to accomplish something, than to see which interfaces my object should
implement, or whether I need a Decorator or MultiTraverser or some such thing.
More information about the PEAK
mailing list