I made a mistake by adding the current generation of URL-dispatch behavior to
repoze.bfg.
Currently, repoze.bfg allows you to use Routes to invoke a bfg view with a
manufactured context based on a route match. To do so, you configure a
repoze.bfg.urldispatch.RoutesMapper as the bfg get_root callable (aka
IRootFactory, lately, at least), and pass it in to bfg's make_app at
application construction time. Before you pass it in, you are expected to call
the RoutesMapper's connect method with the same arguments you might pass to a
real Routes mapper object (see
http://routes.groovie.org/manual.html#setting-up-routes).
The BFG RoutesMapper also treats one argument to connect specially: a
context_factory. This argument, if it exists, allows you to tell BFG what
kind of class to create for the manufactured context. If it doesn't exist, BFG
creates a generic RoutesContext object. In either case, the object is decorated
with a single interface: IRoutesContext. Whether it's a custom context
manufactured by a context_factory or a default RoutesContext object, the
object obtains only that single interface. The attributes of an instance of the
class are the keys and values in the routes match_dict. You are expected to
make BFG view declarations *against IRoutesContext* with names that match the
controller name in the routes match_dict.
Some of this is wrong. Instead of working this way, URL dispatch should replace
traversal to find the context, the view_name, and the subpath based on elements
in a route path, and it should *allow the user to specify the context interface
types* instead of expecting a user to register views against IRoutesContext.
Here's how such a scheme might work: in the case that a context is found by a
route, the context will be manufactured instead of being returned as a result of
traversal through the root graph. Next-gen urldispatch will just find a context
and possibly inform BFG about the view name and the subpath based on
elements present the Route path (just like the normal ModelGraphTraverser does).
Each route should allow a context_factory to be specified as well as a
sequence of context_interfaces. When a route is matched, a context will be
created using the context factory; its attributes will be all the attributes in
the routes match_dict. It will also be decorated (via alsoProvides) with the
interfaces mentioned as context_interfaces. Users will make normal BFG view
declarations against such interfaces. This will allow you to mix and match
traversal and graph traversal using the same interfaces.
Additionally, instead of requiring a RoutesMapper as an IRootFactory, a next-gen
BFG that allows a context to be found as a result of a Routes route match will
just allow ZCML route declarations to be placed in an application's
configure.zcml. If any of these declarations exist, URL dispatch will be
given first crack at resolving a URL; in applications that *only* use URL
dispatch, the get_root callable passed to make_app at app startup can be a
do-nothing.
For convenience, if a Routes match_dict contains a :view_name key, it will be
passed back to BFG as the BFG view name. Likewise, if it contains a *subpath
wildcard match, this will be passed back to BFG as the subpath. These will be
attached by BFG to the request as subpath and view_name. They'll also be
available on the manufactured context, too; this is only a way to make contexts
found through traversal or url dispatch more similar.
Here's an example of some ZCML that would create a Routes route:
route
name=blog_entry
path=/blog/:id/:view_name
context_factory=.blog.BlogEntry
context_interfaces=.interfaces.IBlogEntry .interfaces.IContent
/route
For PATH_INFO '/blog/1/edit' The urldispatch traverser will pass back a
BlogEntry with the interfaces IBlogEntry and IContent attached to it (another
marker interface IRoutesContext will also be attached, but it's only for
framework consumption). The view_name will be passed back as edit. The
subpath will be empty.
Here's another example:
route
name=blog_entry
path=/blog/:id/:view_name/*subpath
context_factory=.blog.BlogEntry
context_interfaces=.interfaces.IBlogEntry .interfaces.IContent
/route
Here we'll get the same context and view_name for PATH_INFO '/blog/1/edit', but
we'll have a non-empty subpath.
Of course, users will be able to specify a view_name and subpath argument as
defaults in the route declaration as well, e.g.:
route
name=blog_entry
path=/blog/:id
view_name=edit
subpath=/foo/bar
context_factory=.blog.BlogEntry
context_interfaces=.interfaces.IBlogEntry .interfaces.IContent
/route
The repoze.bfg.model_url API will be changed to inspect if the context is an
IRoutesContext; if so, the Routes url_for API will be used to construct a URL
for Routes-generated contexts. I'm not sure how this will work out.
If I find time to do this, I won't unceremoniously rip out the