On 2011-01-14 22:53:03 -0800, Rob Miller said:
Object dispatch is a TurboGears concept, not Pylons.
I've retrofitted it to almost every web framework I've ever used from
WebPy and CherryPy through to Pylons. Routes are an inherently
limiting thing to me (one app I worked on actually exceeded the ~55KiB
limit on regular expressions!) and I think the Pyramid folks generally
agree with me. ;)
While I think a "Pyramid for TG users" guide would be a valuable
resource, I think it'd be a bit confusing to put such a reference in a
"Pyramid for Pylons users" document.
I guess so; Pyramid's dispatch seems to be one of the most
misunderstood parts of how it works, and as an up-front intelligence
cost some may give up before fully grasping it. Don't get me wrong, I
like it. I also like the separation of view and the powerful
cross-package collaboration it gives.
The rest of this e-mail assumes that second-stage view lookup doesn't
exist; I find this to be an interesting discussion and don't mean to
imply or give the impression that I think "Pyramid sucks" or "traversal
sucks" or "view lookup sucks" and would like to concentrate on just
traversal for discussion purposes.
From what I've seen in blog posts—overriding the dictionary lookup
method—it seems a little too close to object dispatch
too close? too close for what? this sounds a bit pejorative, but i'm
not quite understanding what you're getting at...
Read on. :)
where you have straight-up attribute lookup—allowing override using
__getattr__ and usually some combination of __default__ (effectively
__getattr__ with extra arguments) and __lookup__ (redirecting the
dispatch by returning a new object to inspect).
I'm not intimately familiar w/ TG's object dispatch, so I can't really
comprehensively address the differences btween object dispatch and
resource traversal.
https://github.com/GothAlice/WebCore/blob/master/web/core/dialects/common.py#L14-50
The
above is the "simplest possible" (I'm sure it can be made simpler, but
not by much) object dispatch function. It is simple enough to be
easily groked by almost anyone. It is used in WebCore for XML-RPC and
AMF dispatch. The following is the core object dispatch used for
standard controllers in WebCore and is well commented:
https://github.com/GothAlice/WebCore/blob/master/web/core/dialects/core.py#L34-108
It
adds redirection via __lookup__ and __default__ and correct use of HTTP
status codes, but is two and a half times larger than the former
version.
In my mind, though, there's a pretty big difference between URLs being
constructed based on object attributes vs. URLs being constructed by
traversing nested dictionaries.
An attribute is a value in the __dict__ of the object, either added by
superclasses (standard or mix-in) or at runtime. Considering that,
they're very similar. :)
I think of object attributes as primarily a code construct; I would
expect two instances of the same class to have the same attributes, for
instance.
Correct. The result is that you get a declarative-style definition of
path elements to callables a la the difference between SA and
Declarative SA.
Generating URL paths via object attributes makes me wary that the URL
structure of my application is going to be tightly coupled with my code
choices, such that refactoring my application would change the URLs in
use.
Not necessarily true; there is already de-coupling in most frameworks
that use object dispatch between the 'controller' and 'view', where the
object dispatch 'controller' determines the 'view' to utilize. Within
the context of the mismatched-for-HTTP MVC, the controller shouldn't
really be doing much work at all, just passing relevant model back and
forth with the view.
I could refactor the functionality of those objects [dicts] to my
heart's content, but as long as the dictionary lookup API still worked,
I wouldn't need to worry about the impact it had on my URL paths.
In terms of raw lookup, I'm not entirely sure how dicts and base
objects differ; each provides a mechanism for determining a callable
based on (in a frictionless universe, recursive) lookups.
I can see how the two might seem more similar when you look at some of
the traversal examples in the Pyramid docs, since they show trivial
implementations where the dictionary keys and values are hard-coded in
Python.
No, I was thinking about the complicated / dynamic aspect of this as
well. Dicts provide __getitem__, objects provide __getattr__; same
basic idea. My core dispatch provides two additional methods for
reasons I describe below.
In practice, however, this is rarely the case; traversal resources
usually provide dynamic __getitem__ implementations that fetch their
contents from some data source. Changing the URL structure of your
site might involve a database migration, but it usually wouldn't
involve any code changes.
As above, __getattr__ provides the same dynamism as __getitem__. Both
of these suffer one problem: the need to use superglobals (thread-local
proxies) or functional thread locals (function calls to look up objects
from a registry) to have truly dynamic behaviour.
The dispatch used in WebCore includes two additional methods, one which
replicates the behaviour of the end-methods, the other being passed
remaining path elements as *args and GET+POST as **kw:
:: __default__ returns like any other WebCore method call, returning a
view and data for the view; this is a "catch-all".
:: __lookup__ returns another object and a list of remaining path
elements to evaluate via dispatch.
The latter I use /everywhere/ to affect dynamic behaviour based on
remaining path elements. Recursive (or non-recursive, whatever)
querying of __getattr__ will get you a single level deeper each loop,
but I have one application (a MongoDB-based CMS) that optimizes using
__lookup__:
https://github.com/GothAlice/Contentment/blob/master/web/extras/contentment/components/core.py#L91-139
It's
hard to describe. :/
It takes the remaining path elements (on the first call) and computes
all intermediary paths. E.g. it creates a list of ['/', '/foo',
'/foo/bar'] from the path "/foo/bar", then looks up all of those paths
in the DB. If the data structure is correct, that will give the
deepest element. (If not, that's OK, too, instead of returning the
asset controller and view, it returns the nearest asset controller and
the unhandled path elements.)
On subsequent calls (e.g. deep lookup fails) it reverts to lookups
based on the names of child nodes from the DB.
Anyway, that's an _extreme_ use case… in 48 lines of code. ;)
Using __lookup__ you can model the following quite easily:
:: /
:: /action
:: /users/
:: /users/27
:: /users/27/method (e.g. delete)
The above is done like this:
https://gist.github.com/780774
As you would expect (and as demonstrated in the CMS), this can be based
entirely on a database-modelled path structure.
It comes down to traversal being dict-based, and object dispatch being
dict-based with benefits. ;)
In Pyramid /is/ it possible to optimize traversal for the consumption
of multiple path elements?
of course, URL matching is more flexible in this regard than either
other options. change the matching pattern, and the URLs have changed.
no muss, no fuss.
It's more flexible in terms of centralized path reconfiguration, I'll
give it that. Flexible in other terms is a more difficult claim to
back up. ;) (Performance-wise, executing a 54KiB regular expression
on each request is pure insanity… and what Routes actually does on one
project.)
— Alice.
--
You received this message because you are subscribed to the Google Groups
"pylons-devel" group.
To post to this group, send email to pylons-devel@googlegroups.com.
To unsubscribe from this group, send email to
pylons-devel+unsubscr...@googlegroups.com.
For more options, visit this group at
http://groups.google.com/group/pylons-devel?hl=en.