Paul and Tres recently taught a repoze.bfg tutorial at the Plone Symposium at Penn State. Tres mentioned to me that, by the reactions of the tutorial attendees, he thought having two separate-but-equal ways to do URL-to-code mapping (traversal vs. url dispatch/aka routes) was too confusing. He then suggested an alternative.
Currently, a configured repoze.bfg application is in one of three modes: - traversal-only, when a "root factory" is used but no routes are configured - routes-only, when routes are confgured but no root factory is used. - a hybrid model where if a route can't be matched, the system falls back to traversal from a single root. In this mode, both a root factory and routes are configured. Tres' suggestion was essentially to cause BFG to always operate in a hybrid mode, where a "root factory" was used to generate the context object for views even when they are found via a route match instead of via traversal. For example, let's say we had a root factory that looked like this: class Root: pass root = Root() def root_factory(environ): return root .. and we configure it in to our BFG application like so: from repoze.bfg.router import make_app from myapp.models import root_factory return make_app(root_factory) Currently the above "root_factory" callback is only used when the URL is matched as a result of traversal. But when any <route> matches, it is ignored. Instead, when a <route> matches: - If there's a "factory=" attribute on the route declaration, it names a "context factory". The context factory is called when this route matches. - If there's no "factory=" attribute on the route declaration, a default routes context factory is used. But BFG never calls a "root factory" for an object matched via a route. In a system that operated under Tres' model, we'd essentially take away the difference between a "root factory" and a "context factory". Instead: - Each route will match a URL pattern. - If a route's URL pattern is matched on ingress, if the route has a "factory" attribute, the factory will be assumed to be a "root factory", and it will return a context object appropriate for that route. If the route does *not* have a "factory" attribute, the "default root factory" would be used to compose the context. - There would be a "default root factory", used when no supplied route matches or when no "factory=" attribute was supplied along with a route statement. What this boils down to is that the syste will have a "default route" will match any URL, but will be last in the route check ordering. The default route will always use the "default root factory" as its factory. Benefits: - Makes the difference between an application that uses routes and one that doesn't far less pronounced. Essentially, this change unifies the two models. - Adds the ability to do traversal through some set of names *after* a route is matched. We'd allow some special signifier to be placed within a route path, ala "/foo/bar/*subpath"; we'd resolve the root related to "/foo/bar", then just traverse with the path info captured in "subpath". Risks: - If a "factory" is specified on a route, it will need to point at a function that had the same call/response convention as a traversal root factory. This will break code. "Context factories" accept key/value pairs assumed to be items that matched in the URL match. These would cease working, and would need to be rewritten as root factories, which accept a WSGI environment. - URL generation may become more difficult and costly. I'm apt to do this for 1.0, even at the risk of breaking code, because it does nicely unify the traversal vs. routes story, which is definitely the most up-in-the-air part of BFG today. Anybody have any objections? - C _______________________________________________ Repoze-dev mailing list Repoze-dev@lists.repoze.org http://lists.repoze.org/listinfo/repoze-dev