From d6798e7775cc31e7507bccf8373fe5f77381ba51 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 17 Jul 2008 15:54:13 +0000 Subject: Add info about views. --- docs/api/index.rst | 20 ------ docs/index.rst | 1 + docs/narr/introduction.rst | 14 ++--- docs/narr/views.rst | 147 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 155 insertions(+), 27 deletions(-) delete mode 100644 docs/api/index.rst create mode 100644 docs/narr/views.rst (limited to 'docs') diff --git a/docs/api/index.rst b/docs/api/index.rst deleted file mode 100644 index 496afc4c6..000000000 --- a/docs/api/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _modules: - -API documentation -================= - -:mod:`repoze.bfg` ------------------ - -The :mod:`repoze.bfg` package contains the code nececessary to create -and run a web application. - -.. toctree:: - :maxdepth: 2 - - push - router - security - template - view - diff --git a/docs/index.rst b/docs/index.rst index bc6dbb303..3e27cccde 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,6 +13,7 @@ Narrative documentation in chapter form explaining how to use :maxdepth: 2 narr/introduction + narr/views API documentation ----------------- diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst index 91cafd503..768b9b905 100644 --- a/docs/narr/introduction.rst +++ b/docs/narr/introduction.rst @@ -149,15 +149,15 @@ mapply code which dynamically ("magically") determines which arguments to pass to a view based on environment and request parameters. -view constructor and view +view factory and view - A "view constructor" is a callable which returns a view object. It + A "view factory" is a callable which returns a view object. It should accept two values: context and request. A "view" is a callable that accepts arbitrary values (mapped into it by "mapply") and which returns a response object. - A view constructor may *be* a view in a repoze.bfg application + A view factory may *be* a view in a repoze.bfg application (e.g. it may accept "context" and "request" and return a response object directly instead of returning a view object). This makes it possible to support views as simple functions. @@ -180,7 +180,7 @@ context view registry - A registry which maps a context and view name to a view constructor + A registry which maps a context and view name to a view factory and optionally a permission. template @@ -278,12 +278,12 @@ code to execute: 8. Armed with the context, the view name, and the subpath, the router performs a view lookup. It attemtps to look up a view - constructor from the ``repoze.bfg`` view registry using the view - name and the context. If a view constructor is found, it is + factory from the ``repoze.bfg`` view registry using the view + name and the context. If a view factory is found, it is converted into a WSGI application: it is "wrapped in" ( aka "adapted to") a WSGI application using mapply. The WSGI adapter uses mapply to map request and environment variables into the - view when it is called. If a view constructor is not found, a + view when it is called. If a view factory is not found, a generic WSGI ``NotFound`` application is constructed. In either case, the resulting WSGI application is called. The WSGI diff --git a/docs/narr/views.rst b/docs/narr/views.rst new file mode 100644 index 000000000..3331794e3 --- /dev/null +++ b/docs/narr/views.rst @@ -0,0 +1,147 @@ +Views +===== + +A view is a callable which is called when a a request enters your +application. ``repoze.bfg's`` primary job is to find and call a view +when a request reaches it. The view's return value must implement the +Response object interface. + +Defining a View as a Function +----------------------------- + +The easiest way to define a view is to create a function that accepts +two arguments: *context*, and *request*. For example, this is a hello +world view implemented as a function:: + + def hello_world(context, request): + from webob import Response + return Response('Hello world!') + +Defining View Factories +----------------------- + +Declarations in your view registry to point at a *view factory* +rather than pointing it at a view implemented as a function. This +provides an additional level of convenience for some applications. + +A view factory, like a view implemented as a function, accepts the +*context* and *request* arguments. But unlike a view implemented as a +function it returns *another* callable instead of a response. The +returned callable is then called by ``repoze.bfg``, with its arguments +filled in "magically". + +The easiest way to implement a view factory is to imlement it as a +class. Here's a hello world view factory that is implemented as a +class:: + + class HelloWorld(object): + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + from webob import Response + return Response('Hello world!') + +You can also implement a view factory as a function:: + + def HelloWorld(context, request): + def hello_world(): + from webob import Response + return Response('Hello world!') + return hello_world + +Using View Factories for Convenience +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +View factories aren't just makework. They exist for convenience, +because, unlike views as functions, views that are returned via a view +factory will have arguments "magically" mapped into them when they are +called by the router. You can choose the arguments you'd like the +constructed view to receive simply by mentioning each in the signature +of the callable you return from the view factory (or the __call__ of +the class you use as the view factory). + +The arguments that are available to be magically mapped into +constructed view calls are as follows. + +context + + The current context + +request + + The current request + +environ + + The current WSGI environment + +start_response + + The current WSGI start_response callable + +XXX We need to decide which other elements will be mapped in from the +request and map them in: e.g. query string/form elements, etc. + +This means that the ``__call__`` of the following view factory +will have its *environ* and *start_response* arguments filled in +magically during view call time:: + + def ViewFactory(object): + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self, environ, start_response): + msg = 'Called via %s ' % environ['PATH_INFO'] + start_response('200 OK', ('Content-Length', len(msg)) + return msg + +.. note:: If you're familiar with WSGI, you'll notice above that the + view factory returns a valid WSGI application. View + factories in ``repoze.bfg`` can return any WSGI application. + +View Functions Revisited +------------------------ + +Above we provided an example of a "view" imlemented as a function:: + + def hello_world(context, request): + from webob import Response + return Response('Hello world!') + +When ``repoze.bfg`` finds and calls this callable, has no a-priori +knowledge that would allow it to believe that this function would +return a response directly. It assumes that what it's calling will +return a *view* instead of a *response*. In other words, it expects +that everything configured in its view registry points at a view +factory. + +However, there is a special case in the logic implemented in the +``repoze.bfg`` router that says if the return value of a view +factory is an object implementing the Response interface, use that +object as the response, and don't try to call the object or magically +map any arguments into it. Instead, it just passes it along to the +upstream WSGI server. + +This is purely for convenience: it's useful to be able to define +simple functions as "views" without the overhead of defining a view +factory. + +View Factory Arguments +---------------------- + +Now that we know what view factories are, what are these *context* +and *request* arguments that are mapped in to it? + +context + + An instance of a model found via graph traversal. + +request + + A WebOb request object representing the current request. + + + -- cgit v1.2.3