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

Reply via email to