diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-06-11 03:15:15 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-06-11 03:15:15 +0000 |
| commit | dfc2b65c1b6d2f938f68b7868a14d8f9a4faab9e (patch) | |
| tree | f3241401b7175a401e00286b11e3efe3c21f5093 /docs | |
| parent | f8b0065b6ede54424d7a7b49f9f113e87634b5ab (diff) | |
| download | pyramid-dfc2b65c1b6d2f938f68b7868a14d8f9a4faab9e.tar.gz pyramid-dfc2b65c1b6d2f938f68b7868a14d8f9a4faab9e.tar.bz2 pyramid-dfc2b65c1b6d2f938f68b7868a14d8f9a4faab9e.zip | |
Merge unifyroutesandtraversal branch into trunk
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/narr/hooks.rst | 73 | ||||
| -rw-r--r-- | docs/narr/project.rst | 4 | ||||
| -rw-r--r-- | docs/narr/traversal.rst | 21 | ||||
| -rw-r--r-- | docs/narr/urldispatch.rst | 90 | ||||
| -rw-r--r-- | docs/narr/urlmapping.rst | 7 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki2/authorization.rst | 80 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml | 3 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki2/src/authorization/tutorial/models.py | 8 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki2/src/authorization/tutorial/run.py | 3 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki2/src/authorization/tutorial/utilities.py | 10 |
10 files changed, 99 insertions, 200 deletions
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 533024352..53d6c8c77 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -7,51 +7,6 @@ ZCML "hooks" can be used to influence the behavior of the :mod:`repoze.bfg` framework in various ways. This is an advanced topic; not many people will want or need to do any of these things. -Changing the request factory ----------------------------- - -You may change the class used as the "request factory" from within the -:mod:`repoze.bfg` ``Router`` class (the ``Router`` class turns the -WSGI environment into a "request" object which is used ubiquitously -throughout :mod:`repoze.bfg`). The default "request factory" is the -class ``webob.Request``. You may change it by placing the following -ZCML in your ``configure.zcml`` file. - -.. code-block:: xml - :linenos: - - <utility provides="repoze.bfg.interfaces.IRequestFactory" - component="helloworld.factories.request_factory"/> - -Replace ``helloworld.factories.request_factory`` with the Python -dotted name to the request factory you want to use. Here's some -sample code that implements a minimal request factory: - -.. code-block:: python - - from webob import Request - from repoze.bfg.interfaces import IRequest - - class MyRequest(Request): - implements(IRequest) - - def request_factory(): - return MyRequest - -.. warning:: If you register an ``IRequestFactory`` utility in this - way, you *must* be sure that the factory returns an object that - implements *at least* the ``repoze.bfg.interfaces.IRequest`` - interface. Otherwise all application view lookups will fail (they - will all return a 404 response code). Likewise, if you want to be - able to use method-related interfaces such as ``IGETRequest``, - ``IPOSTRequest``, etc. in your view declarations, the callable - returned by the factory must also do the same introspection of the - environ that the default request factory does and decorate the - returned object to implement one of these interfaces based on the - ``HTTP_METHOD`` present in the environ. Note that the above - example does not do this, so lookups for method-related interfaces - will fail. - Changing the response factory ----------------------------- @@ -164,31 +119,3 @@ code that implements a minimal forbidden view: an alterate forbidden view. For example, it would make sense to return a response with a ``403 Forbidden`` status code. -.. _changing_routes_context_factory: - -Changing the Default Routes Context Factory -------------------------------------------- - -The default Routes "context factory" (the object used to create -context objects when you use ``<route..>`` statements in your ZCML) is -``repoze.bfg.urldispatch.DefaultRoutesContext``. You may change the -class used as the Routes "context factory" by placing the following -ZCML in your ``configure.zcml`` file. - -.. code-block:: xml - :linenos: - - <utility provides="repoze.bfg.interfaces.IRoutesContextFactory" - component="helloworld.factories.routes_context_factory"/> - -Replace ``helloworld.factories.routes_context_factory`` with the -Python dotted name to the context factory you want to use. Here's -some sample code that implements a minimal context factory: - -.. code-block:: python - :linenos: - - class RoutesContextFactory(object): - def __init__(self, **kw): - self.__dict__.update(kw) - diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 7fb3604de..3de146e8e 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -92,10 +92,10 @@ application's Python code and templates. <http://www.sqlalchemy.org/>`_ also exist. Use ``paster create -t bfg_zodb`` to create a project that depends on ZODB. Use ``paster create -t bfg_routesalchemy`` to create a project that depends on - SQLAlchemy and Routes (uses :term:`URL dispatch` instead of + SQLAlchemy and Routes (uses only :term:`URL dispatch` and no :term:`traversal`). Use ``paster create -t bfg_alchemy`` to create a project that depends on SQLAlchemy but *not* Routes (uses - :term:`traversal` instead of :term:`URL dispatch`). + only :term:`traversal` and no :term:`URL dispatch`). Installing your Newly Created Project for Development ----------------------------------------------------- diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index 7177432de..4c032952b 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -29,15 +29,18 @@ root object is usually a *mapping* object (such as a Python dictionary). .. note:: If a :term:`root factory` is passed to the :mod:`repoze.bfg` - "make_app" function as the value ``None``, no traversal is - performed. Instead, it's assumed that all URLs will be mapped to - code via :term:`URL dispatch`. No root factory, no traversal. It - is also possible to mix-and-match traversal with URL dispatch. - When both a root factory (and therefore traversal) *and* "routes" - declarations (and therefore url dispatch) are used, the url - dispatch routes are checked first, and if none match, - :mod:`repoze.bfg` will fall back to using traversal to attempt to - map the request to a view. + "make_app" function as the value ``None``, a default root factory + is used. This is most useful when you're using :term:`URL + dispatch` and you don't care very much about traversing any + particular graph to resolve URLs to code. It is also possible to + use traversal and URL dispatch together. When both a root factory + (and therefore traversal) *and* "routes" declarations (and + therefore url dispatch) are used, the url dispatch routes are + checked first, and if none match, :mod:`repoze.bfg` will fall back + to using traversal to attempt to map the request to a view. If the + name ``*traverse`` is in a route's ``path`` pattern, when it is + matched, it is also possible to do traversal *after* a route has + been matched. See :ref:`urldispatch_chapter` for more information. Items contained within the object graph are analogous to the concept of :term:`model` objects used by many other frameworks (and diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index c7d1e2a38..91063fc26 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -16,8 +16,8 @@ which allows you to declaratively map URLs to code. neither concept (controller nor action) exists within :mod:`repoze.bfg`. Instead, when you map a URL pattern to code in bfg, you will map the URL patterm to a :term:`view`. - Once the context and view name are found, the view will be - called with a :term:`context` and a :term:`request`. + Once the context and view are found, the view will be called + with a :term:`context` and a :term:`request`. It often makes a lot of sense to use :term:`URL dispatch` instead of :term:`traversal` in an application that has no natural hierarchy. @@ -54,17 +54,15 @@ acts as a root factory, it is willing to check the requested URL against a *routes map* to find a :term:`context` and a :term:`view` before traversal has a chance to find it first. If a route matches, a :term:`context` is generated and :mod:`repoze.bfg` will call the -:term:`view` specified with the context and the request. - -If no route matches, :mod:`repoze.bfg` will fail over to calling the -root factory callable passed to the application in it's ``make_app`` -function (usually a traversal function). By configuring your ZCML -``route`` statements appropriately, you can mix and match URL dispatch -and traversal in this way. +:term:`view` specified with the context and the request. If no route +matches, :mod:`repoze.bfg` will fail over to calling the :term:`root +factory` callable passed to the application in it's ``make_app`` +function (usually a traversal function). A root factory is not required for purely URL-dispatch-based apps: if -the root factory callable is ``None``, :mod:`repoze.bfg` will return a -NotFound error to the user's browser when no routes match. +the root factory callable is passed as ``None`` to the ``make_app`` +function, :mod:`repoze.bfg` will return a NotFound error to the user's +browser when no routes match. .. note:: See :ref:`modelspy_project_section` for an example of a simple root factory callable that will use traversal. @@ -105,9 +103,8 @@ factory The Python dotted-path name to a function that will generate a :mod:`repoze.bfg` context object when this route matches. - e.g. ``mypackage.models.MyFactoryClass``. By default, a - ``repoze.bfg.urldispatch.DefaultRoutesContext`` object will be - constructed if a factory is not provided. + e.g. ``mypackage.models.MyFactoryClass``. If this argument is not + specified, a default root factory will be used. encoding @@ -267,12 +264,12 @@ Example 3 --------- The context object passed to a view found as the result of URL -dispatch will by default be an instance of the -``repoze.bfg.urldispatch.DefaultRoutesContext`` object. You can -override this behavior by passing in a ``factory`` argument to the -ZCML directive for a particular route. The ``factory`` should be a -callable that accepts arbitrary keyword arguments and returns an -instance of a class that will be the context used by the view. +dispatch will by default be an instance of the object returned by the +default :term:`root factory`. You can override this behavior by +passing in a ``factory`` argument to the ZCML directive for a +particular route. The ``factory`` should be a callable that accepts a +WSGI environment and returns an instance of a class that will be the +context used by the view. An example of using a route with a factory: @@ -288,13 +285,13 @@ An example of using a route with a factory: The above route will manufacture an ``Idea`` model as a context, assuming that ``mypackage.models.Idea`` resolves to a class that -accepts arbitrary key/value pair arguments. +accepts a WSGI environment in its ``__init__``. .. note:: Values prefixed with a period (``.``) for the ``factory`` - and ``provides`` attributes of a ``route`` (such as - ``.models.Idea`` and ``.views.idea_view``) above) mean "relative to - the Python package directory in which this :term:`ZCML` file is - stored". So if the above ``route`` declaration was made inside a + and ``view`` attributes of a ``route`` (such as ``.models.Idea`` + and ``.views.idea_view``) above) mean "relative to the Python + package directory in which this :term:`ZCML` file is stored". So + if the above ``route`` declaration was made inside a ``configure.zcml`` file that lived in the ``hello`` package, you could replace the relative ``.models.Idea`` with the absolute ``hello.models.Idea`` Either the relative or absolute form is @@ -302,14 +299,10 @@ accepts arbitrary key/value pair arguments. form, in case your package's name changes. It's also shorter to type. -All context objects manufactured via URL dispatch will be decorated by -default with the ``repoze.bfg.interfaces.IRoutesContext`` -:term:`interface`. - If no route matches in the above configuration, :mod:`repoze.bfg` will -call the "fallback" ``get_root`` callable provided to it during -``make_app`. If the "fallback" ``get_root`` is None, a ``NotFound`` -error will be raised when no route matches. +call the "fallback" :term:`root factory` callable provided to it +during ``make_app`. If the "fallback" root factory is None, a +``NotFound`` error will be raised when no route matches. .. note:: See :ref:`using_model_interfaces` for more information about how views are found when interfaces are attached to a @@ -338,7 +331,10 @@ The ``.models`` module referred to above might look like so: .. code-block:: python :linenos: - class Article(dict): + class Article(object): + def __init__(self, environ): + self.__dict__.update(environ['repoze.bfg.matchdict']) + def is_root(self): return self['article'] == 'root' @@ -393,8 +389,8 @@ request when a database connection is involved. When of the traversal :term:`root factory`. Often the root factory will insert an object into the WSGI environment that performs some cleanup when its ``__del__`` method is called. When URL dispatch is used, -however, no root factory is required, so sometimes that option is not -open to you. +however, no special root factory is required, so sometimes that option +is not open to you. Instead of putting this cleanup logic in the root factory, however, you can cause a subscriber to be fired when a new request is detected; @@ -439,32 +435,28 @@ Lists, see :ref:`security_chapter` for more information about the :mod:`repoze.bfg` authorization subsystem). A common thing to want to do is to attach an ``__acl__`` to the context object dynamically for declarative security purposes. You can use the ``factory`` argument -that points at a context factory which attaches a custom ``__acl__`` -to an object at its creation time. +that points at a factory which attaches a custom ``__acl__`` to an +object at its creation time. Such a ``factory`` might look like so: .. code-block:: python :linenos: - class Article(dict): - pass - - def article_factory(**kw): - model = Article(**kw) - article = kw.get('article', None) - if article == '1': - model.__acl__ = [ (Allow, 'editor', 'view') ] - return model + class Article(object): + def __init__(self, environ): + matchdict = environ['bfg.routes.matchdict'] + article = matchdict.get('article', None) + if article == '1': + self.__acl__ = [ (Allow, 'editor', 'view') ] If the route ``archives/:article`` is matched, and the article number is ``1``, :mod:`repoze.bfg` will generate an ``Article`` :term:`context` with an ACL on it that allows the ``editor`` principal the ``view`` permission. Obviously you can do more generic things that inspect the routes match dict to see if the ``article`` argument -matches a particular string; our sample ``article_factory`` function -is not very ambitious. Its job could have just as well been done in -the ``Article`` class' constructor, too. +matches a particular string; our sample ``Article`` factory class is +not very ambitious. .. note:: See :ref:`security_chapter` for more information about :mod:`repoze.bfg` security and ACLs. diff --git a/docs/narr/urlmapping.rst b/docs/narr/urlmapping.rst index 3c74d04d9..21f6235f8 100644 --- a/docs/narr/urlmapping.rst +++ b/docs/narr/urlmapping.rst @@ -90,10 +90,3 @@ URL-based dispatch. :mod:`repoze.bfg` provides support for both approaches. You can use either as you see fit. -.. note:: - - Most existng :mod:`repoze.bfg` applications use :term:`traversal` to - map URLs to code. This is mostly due to the :term:`Zope` heritage - of :mod:`repoze.bfg` and because it aids applications that require - highly granular declarative security assertions. - diff --git a/docs/tutorials/bfgwiki2/authorization.rst b/docs/tutorials/bfgwiki2/authorization.rst index 53d4cfb63..402e42f8d 100644 --- a/docs/tutorials/bfgwiki2/authorization.rst +++ b/docs/tutorials/bfgwiki2/authorization.rst @@ -11,54 +11,39 @@ allowing anyone with access to the server to view pages. *authentication*. We'll make use of both features to provide security to our application. -Adding A Context Factory ------------------------- +Adding A Root Factory +--------------------- -We're going to start to use a custom *context factory* within our -``configure.zcml`` file in order to be able to attach security -declarations to our :term:`context` object. When we do this, we can -begin to make use of the declarative security features of -:mod:`repoze.bfg`. +We're going to start to use a custom *root factory* within our +``run.py`` file in order to be able to attach security declarations to +our :term:`context` object. When we do this, we can begin to make use +of the declarative security features of :mod:`repoze.bfg`. -Let's modify our ``configure.zcml``, following the instructions in the -BFG documentation section named -:ref:`changing_routes_context_factory`. We'll point it at a function -in a new module we create named ``utilities.py``. +Let's modify our ``run.py``, passing in a :term:`root factory` as the +first argument to ``repoze.bfg.router.make_app``. We'll point it at a +new class we create inside our ``models.py`` file. Add the following +statements to your ``models.py`` file: -Add the following section to your application's -``configure.zcml`` file: - -.. code-block:: xml - :linenos: - - <utility provides="repoze.bfg.interfaces.IRoutesContextFactory" - component=".utilities.RoutesContextFactory"/> - -As a result, our ``configure.zcml`` file will now look like so: - -.. literalinclude:: src/authorization/tutorial/configure.zcml - :linenos: - :language: xml - -Once ``configure.zcml`` has been modified, create a file named -``utilities.py`` and give it the following contents: - -.. literalinclude:: src/authorization/tutorial/utilities.py - :linenos: - :language: python +.. code-block:: python -The result of our changing of the default routes context factory in -``configure.zcml`` and our addition of a new ``RoutesContextFactory`` -class to ``utilities.py`` allows us to use declarative security -features of :mod:`repoze.bfg`. The ``RoutesContextFactory`` class we -added will be used to construct each of the ``context`` objects passed -to our views. All of our ``context`` objects will possess an -``__acl__`` attribute that allows "Everyone" (a special principal) to -view all request, while allowing only a user named ``editor`` to edit -and add pages. The ``__acl__`` attribute attached to a context is -interpreted specially by :mod:`repoze.bfg` as an access control list -during view execution. See :ref:`assigning_acls` for more information -about what an :term:`ACL` represents. + from repoze.bfg.security import Allow + from repoze.bfg.security import Everyone + + class RootFactory(object): + __acl__ = [ (Allow, Everyone, 'view'), (Allow, 'editor', 'edit') ] + def __init__(self, environ): + self.__dict__.update(environ['bfg.routes.matchdict']) + +Defining a root factory allows us to use declarative security features +of :mod:`repoze.bfg`. The ``RootFactory`` class we added will be used +to construct each of the ``context`` objects passed to our views. All +of our ``context`` objects will possess an ``__acl__`` attribute that +allows "Everyone" (a special principal) to view all request, while +allowing only a user named ``editor`` to edit and add pages. The +``__acl__`` attribute attached to a context is interpreted specially +by :mod:`repoze.bfg` as an access control list during view execution. +See :ref:`assigning_acls` for more information about what an +:term:`ACL` represents. .. note: Although we don't use the functionality here, the ``factory`` used to create route contexts may differ per-route instead of @@ -87,8 +72,11 @@ accepts a userid. If the userid exists in the system, the callback should return a sequence of group identifiers (or an empty sequence if the user isn't a member of any groups). If the userid *does not* exist in the system, the callback should return ``None``. We'll use -"dummy" data to represent user and groups sources. When we're done, -your application's ``run.py`` will look like this. +"dummy" data to represent user and groups sources. + +We'll also use the opportunity to pass our ``RootFactory`` in as the +first argument to ``make_app``. When we're done, your application's +``run.py`` will look like this. .. literalinclude:: src/authorization/tutorial/run.py :linenos: diff --git a/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml b/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml index 8fd6140ab..ff0125f83 100644 --- a/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml +++ b/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml @@ -49,9 +49,6 @@ permission="edit" /> - <utility provides="repoze.bfg.interfaces.IRoutesContextFactory" - component=".utilities.RoutesContextFactory"/> - <utility provides="repoze.bfg.interfaces.IForbiddenView" component=".login.login"/> diff --git a/docs/tutorials/bfgwiki2/src/authorization/tutorial/models.py b/docs/tutorials/bfgwiki2/src/authorization/tutorial/models.py index 3e63c3734..283ddea74 100644 --- a/docs/tutorials/bfgwiki2/src/authorization/tutorial/models.py +++ b/docs/tutorials/bfgwiki2/src/authorization/tutorial/models.py @@ -14,6 +14,9 @@ from sqlalchemy.ext.declarative import declarative_base from zope.sqlalchemy import ZopeTransactionExtension +from repoze.bfg.security import Allow +from repoze.bfg.security import Everyone + DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) Base = declarative_base() @@ -28,6 +31,11 @@ class Page(Base): self.name = name self.data = data +class RootFactory(object): + __acl__ = [ (Allow, Everyone, 'view'), (Allow, 'editor', 'edit') ] + def __init__(self, environ): + self.__dict__.update(environ['bfg.routes.matchdict']) + def initialize_sql(db, echo=False): engine = create_engine(db, echo=echo) DBSession.configure(bind=engine) diff --git a/docs/tutorials/bfgwiki2/src/authorization/tutorial/run.py b/docs/tutorials/bfgwiki2/src/authorization/tutorial/run.py index 0f2068bba..698ba96b9 100644 --- a/docs/tutorials/bfgwiki2/src/authorization/tutorial/run.py +++ b/docs/tutorials/bfgwiki2/src/authorization/tutorial/run.py @@ -4,6 +4,7 @@ from repoze.bfg.authentication import AuthTktAuthenticationPolicy import tutorial from tutorial.models import DBSession from tutorial.models import initialize_sql +from tutorial.models import RootFactory class Cleanup: def __init__(self, cleaner): @@ -27,7 +28,7 @@ def app(global_config, **kw): authpolicy = AuthTktAuthenticationPolicy('seekr!t', callback=groupfinder) - return make_app(None, tutorial, authentication_policy=authpolicy, + return make_app(RootFactory, tutorial, authentication_policy=authpolicy, options=kw) USERS = {'editor':'editor', diff --git a/docs/tutorials/bfgwiki2/src/authorization/tutorial/utilities.py b/docs/tutorials/bfgwiki2/src/authorization/tutorial/utilities.py deleted file mode 100644 index cc1e0d515..000000000 --- a/docs/tutorials/bfgwiki2/src/authorization/tutorial/utilities.py +++ /dev/null @@ -1,10 +0,0 @@ -from repoze.bfg.security import Allow -from repoze.bfg.security import Everyone - -class RoutesContextFactory(object): - __acl__ = [ (Allow, Everyone, 'view'), (Allow, 'editor', 'edit') ] - def __init__(self, **kw): - self.__dict__.update(kw) - - - |
