Now, the idea of a view predicate was not mine, it was Malthe Borch's. At the time, I was still wrapped up in the Zope-inspired worldview that a view invocation is (by god) an invocation of a ZCA multiadapter, QED, full-stop. Malthe figured out that it is useful to look outside the idea of adaptation to resolve a request to a view, and as a result I implemented view lookup in terms of view predicates in 1.1; 1.0 lacked this feature. The epiphany is somewhat obvious in hindsight, but at the time, it was not. I was well and truly wrapped around the adaptation axle.
For those who don't know, an "adapter" is a bit of code that converts
a set of input objects
[I] to an output object
O. The simplest
physical example of an adapter is the one we know and love: a power
plug adapter. Maybe one that converts a US-style two-pronged power
plug to a "proper" mains plug like they use in the UK. Other stilted
and trivial adapter analogies exist, I'll skip them here. Google for
In Zope (and BFG, and Django) terms, a "view" is a bit of code invoked as the result of a particular request. It's called a "controller action" in other framework religions.
In terms of view dispatch, Zope uses a combination of graph traversal
and an adapter lookup to find a view. Rather than using some ordered
set of URL match tests like many other web frameworks, Zope encodes
all knowledge about which view should be invoked in all particular
circumstances within a given application as a set of adapter
registrations. When a request enters the system, traversal is
responsible for finding a "context" object and a "view name". Using
this context object, this view name, and the request object it does a
"multiadapter" lookup something like this: please find me a view
adapter for this kind of context and this kind of request with this
((context, request), view name) -> view object.
While I'm a big fan of this style of dispatch, real-world requirements stretch its applicability. It was always pretty odd to need to attach an interface object to a request in order to convince the view machinery to do something different in some circumstance; it got truly weird when Zope started to support persistent registries that allowed you to make different view registrations in different contexts. Entire subframeworks of various Zope subsystems and offshoots exist just to manage the results for adapter and utility lookups related to requests and contexts. I fear it may all be for naught.
What Malthe figured out is that view dispatch is not just an adapter lookup. While adapter lookup can help speed things up, there's just not enough value in the adapter lookup machinery to spell all the query axes in terms of interfaces, as required by zope.component. For example, it makes a hell of a lot more sense to register a view callable that will be invoked in a circumstance like so:
request.method == 'POST' 'application/json' in request.accept
Than it ever would to register something in terms of interfaces, like Zope makes you do, ala:
providedBy(request) -> IPostRequest, IAcceptApplicationJSON
I mean, literally, it's just not really possible to anticipate all the interfaces you might need to attach to a request in order to provide a vocabulary that allowed you to compose interfaces in enough combinations for such a system based on interface lookup to work. Even if you could, who would understand it after you created it?
With view predicates in BFG 1.1+, it becomes quite easy to register
view callables for very specific circumstances that would be extremely
difficult (or maybe impossible) to spell if you treated view lookup as
only an adaptation problem. For example, using the
predicate in BFG, you can do this:
<view for=".models.Entry" name="edit.html" containment=".models.Blog" />
containment=".models.Blog" attribute is the important bit. It
says "only invoke this view when the context object or any of its
parents is a Blog object". This solves a whole raft of problems for
UI, where you want to use the same kind of object in two places (an
"Entry" object) but you want its edit view to be slightly different in
two different "sections" of a site: maybe one view when we're in the
Blog section, and another when we're in a Calendar section. This sort
of registration is not possible in bare Zope, and adaptation alone,
at least in the context of using a single adapter registry, cannot do it.
I don't think we can continue to treat view dispatch in the Zope world as a bare adapter lookup; it's just too limiting. In BFG 1.1+ we do not. Using a pattern much like Zope's we use a multiadapter lookup to find a "view" object. However, the view object that is found is often a "multiview". A multiview is a collection of views with associated predicates. Once a multiview is found and called, it evaluates the predicates associated with each of its constituent view callables (in a reasonably easy to understand order) to find the most specific view callable; then it invokes that to obtain a response.
Essentially we use the adapter lookup machinery only as a speed enhancement in this circumstance. The adapter lookup gets us "in a ballpark" where we can perform fewer predicate evaluations to find "the best" view callable. It would be possible to implement it without doing any adaptation at all, it would just be a lot slower.
Other predicate attributes exist for use in this way: route_name, request_method, request_param, xhr, accept, header, path_info. In the very latest release (1.2a9), there is even a "custom predicate" predicate, which allows you to provide a callable that returns true or false for an arbitrary circumstance so you can create your own predicate logic. These can be combined arbitrarily to specify an extremely granular set of circumstances without any interface foolery.
Personally, I think view predicates are a pure win, and I think their existence demonstrates why bare adaptation is not ideal for view lookup. I might even take that a little further: it would also be useful to be able to look up other things (such as utilities, and other code) using the same set of predicates. If this becomes true, I think the adaptation worldview as "from interface X to interface Y" as a fundamental assumption begins to look pretty anemic.