diff options
| author | Chris McDonough <chrism@agendaless.com> | 2008-07-16 22:30:10 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2008-07-16 22:30:10 +0000 |
| commit | e2abb26847f7740448b823decc97ba52f44c9a66 (patch) | |
| tree | 43695ba9e8f6dcc8be16bf58fbc43a0d42f3caa3 /README.txt | |
| parent | 9e3bdbce1063f67b2e927fec0677cf74095ea9ad (diff) | |
| download | pyramid-e2abb26847f7740448b823decc97ba52f44c9a66.tar.gz pyramid-e2abb26847f7740448b823decc97ba52f44c9a66.tar.bz2 pyramid-e2abb26847f7740448b823decc97ba52f44c9a66.zip | |
Moved README docs into docs.
Diffstat (limited to 'README.txt')
| -rw-r--r-- | README.txt | 447 |
1 files changed, 12 insertions, 435 deletions
diff --git a/README.txt b/README.txt index 17a183576..f71e64cb5 100644 --- a/README.txt +++ b/README.txt @@ -7,444 +7,21 @@ uses Zope libraries to do much of its work. However, it is less ambitious and less featureful than any released version of Zope's publisher. -``repoze.bfg`` uses the WSGI protocol to handle requests and -responses, and integrates Zope, Paste, and WebOb libraries to form the -basis for a simple web object publishing framework. +Installation +------------ -Graph Traversal ---------------- +Install using setuptools, e.g. (within a virtualenv):: -In many popular web frameworks, a "URL dispatcher" is used to -associate a particular URL with a bit of code (known somewhat -ambiguously as a "controller" or "view" depending upon the particular -vocabulary religion to which you subscribe). These systems allow the -developer to create "urlconfs" or "routes" to controller/view Python -code using pattern matching against URL components. Examples: -`Django's URL dispatcher -<http://www.djangoproject.com/documentation/url_dispatch/>`_ and the -`Routes URL mapping system <http://routes.groovie.org/>`_ . + $ easy_install -i http://dist.repoze.org/lemonade/dev/simple repoze.bfg -It is however possible to map URLs to code differently, using object -graph traversal. The venerable Zope and CherryPy web frameworks offer -graph-traversal-based URL dispatch. ``repoze.bfg`` also provides -graph-traversal-based dispatch of URLs to code. Graph-traversal based -dispatching is useful if you like the URL to be representative of an -arbitrary hierarchy of potentially heterogeneous items. +Further Documentation +--------------------- -Non-graph traversal based URL dispatch can easily handle URLs such as -``http://example.com/members/Chris``, where it's assumed that each -item "below" ``members`` in the URL represents a member in the system. -You just match everything "below" ``members`` to a particular view. -They are not very good, however, at inferring the difference between -sets of URLs such as ``http://example.com/members/Chris/document`` vs. -``http://example.com/members/Chris/stuff/page`` wherein you'd like the -``document`` in the first URL to represent, e.g. a PDF document, and -``/stuff/page`` in the second to represent, e.g. an OpenOffice -document in a "stuff" folder. It takes more pattern matching -assertions to be able to make URLs like these work in URL-dispatch -based systems, and some assertions just aren't possible. For example, -URL-dispatch based systems don't deal very well with URLs that -represent arbitrary-depth hierarchies. +See the ``docs`` directory of the repoze.bfg distribution package +(also available via http://svn.repoze.org/repoze.bfg/trunk/docs). -Graph traversal works well if you need to divine meaning out of these -types of "ambiguous" URLs and URLs that represent arbitrary-depth -hierarchies. Each URL segment represents a single traversal through -an edge of the graph. So a URL like ``http://example.com/a/b/c`` can -be thought of as a graph traversal on the example.com site through the -edges "a", "b", and "c". +Reporting Bugs / Development Versions +------------------------------------- -Finally, if you're willing to treat your application models as a graph -that can be traversed, it also becomes trivial to provide "row-level -security" (in common relational parlance): you just attach a security -declaration to each instance in the graph. This is not as easy in -frameworks that use URL-based dispatch. - -Graph traversal is materially more complex than URL-based dispatch, -however, if only because it requires the construction and maintenance -of a graph, and it requires the developer to think about mapping URLs -to code in terms of traversing the graph. (How's *that* for -self-referential! ;-) That said, for developers comfortable with Zope, -in particular, and comfortable with hierarchical data stores like -ZODB, mapping a URL to a graph traversal it's a natural way to think -about creating a web application. - -In essence, the choice to use graph traversal vs. URL dispatch is -largely religious in some sense. Graph traversal dispatch probably -just doesn't make any sense when you possess completely "square" data -stored in a relational database. However, when you have a -hierarchical data store, it can provide advantages over using -URL-based dispatch. - -Similarities with Other Frameworks ----------------------------------- - -The Django docs state that Django is an "MTV" framework in their `FAQ -<http://www.djangoproject.com/documentation/faq/>`_. This also -happens to be true for ``repoze.bfg``:: - - Django appears to be a MVC framework, but you call the Controller - the "view", and the View the "template". How come you don’t use the - standard names? - - Well, the standard names are debatable. - - In our interpretation of MVC, the "view" describes the data that - gets presented to the user. It’s not necessarily how the data looks, - but which data is presented. The view describes which data you see, - not how you see it. It’s a subtle distinction. - - So, in our case, a "view" is the Python callback function for a - particular URL, because that callback function describes which data - is presented. - - Furthermore, it’s sensible to separate content from presentation — - which is where templates come in. In Django, a "view" describes - which data is presented, but a view normally delegates to a - template, which describes how the data is presented. - - Where does the "controller" fit in, then? In Django’s case, it’s - probably the framework itself: the machinery that sends a request to - the appropriate view, according to the Django URL configuration. - - If you’re hungry for acronyms, you might say that Django is a "MTV" - framework — that is, "model", "template", and "view." That breakdown - makes much more sense. - -How ``repoze.bfg`` is Configured --------------------------------- - -Users interact with your ``repoze.bfg``-based application via a -"router", which is itself a WSGI application. At system startup time, -the router must be configured with a root object from which all -traversal will begin. The root object is a mapping object, such as a -Python dictionary. In fact, all items contained in the graph are -either leaf nodes (these have no __getitem__) or container nodes -(these do have a __getitem__). - -Items contained within the graph are analogous to the concept of -``model`` objects used by many other frameworks. They are typically -instances of classes. Each containerish instance is willing to return -a child or raise a KeyError based on a name passed to its __getitem__. -No leaf-level instance is required to have a __getitem__. - -Jargon ------- - -The following jargon is used casually in descriptions of -``repoze.bfg`` operations. - -request - - A ``WebOb`` request object. - -response - - An object that has three attributes: app_iter (representing an - iterable body), headerlist (representing the http headers sent - upstream), and status (representing the http status string). This - is the interface defined for ``WebOb`` response objects. - -mapply - - code which dynamically ("magically") determines which arguments to - pass to a view based on environment and request parameters. - -view constructor and view - - A "view constructor" 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 - (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. - -view name - - The "URL name" of a view, e.g "index.html". If a view is configured - without a name, its name is considered to be the empty string (which - implies the "default view"). - -model - - An object representing data in the system. A model is part of the - object graph traversed by the system. Models are traversed to - determine a context. - -context - - A model in the system that is the subject of a view. - -view registry - - A registry which maps a context and view name to a view constructor - and optionally a permission. - -template - - A file that is capable of representing some text when rendered. - -interface - - An attribute of a model object that determines its type. - -security policy - - An object that provides a mechanism to check authorization using - authentication data and a permission associated with a model. It - essentially returns "true" if the combination of the authorization - information in the model (e.g. an ACL) and the authentication data - in the request (e.g. the REMOTE_USER) allow the action implied by - the permission associated with the view (e.g. "add"). - -principal - - A user id or group id. - -permission - - A permission is a string token that is associated with a view name - and a model type by the developer. Models are decorated with - security declarations (e.g. ACLs), which reference these tokens - also. A security policy attempts to match the view permission - against the model's statements about which permissions are granted - to which principal to answer the question "is this user allowed to - do this". - -How ``repoze.bfg`` Processes a Request --------------------------------------- - -When a user requests a page from your ``repoze.bfg`` -powered -application, the system uses this algorithm to determine which Python -code to execute: - - 1. The request for the page is presented to ``repoze.bfg``'s - "router" in terms of a standard WSGI request, which is - represented by a WSGI environment and a start_response callable. - - 2. The router creates a `WebOb <http://pythonpaste.org/webob/>`_ - request object based on the WSGI environment. - - 3. The router uses the WSGI environment's ``PATH_INFO`` variable to - determine the path segments to traverse. The leading slash is - stripped off `PATH_INFO``, and the remaining path segments are - split on the slash character to form a traversal sequence, so a - request with a ``PATH_INFO`` variable of ``/a/b/c`` maps to the - traversal sequence ``['a', 'b', 'c']``. - - 4. Traversal begins at the root object. For the traversal sequence - ``['a', 'b', 'c']``, the root object's __getitem__ is called with - the name ``a``. Traversal continues through the sequence. In - our example, if the root object's __getitem__ called with the - name ``a`` returns an object (aka "object A"), that object's - __getitem__ is called with the name ``b``. If object A returns - an object when asked for ``b``, object B's __getitem__ is then - asked for the name ``c``, and may return object C. - - 5. Traversal ends when a) the entire path is exhausted or b) when - any graph element raises a KeyError from its __getitem__ or c) - when any non-final path element traversal does not have a - __getitem__ method (resulting in a NameError) or d) when any path - element is prefixed with the set of characters ``@@`` (indicating - that the characters following the ``@@`` token should be treated - as a "view name"). - - 6. When traversal ends for any of the reasons in the previous step, - the the last object found during traversal is deemed to be the - "context". If the path has been exhausted when traversal ends, - the "view name" is deemed to be the empty string (``''``). - However, if the path was not exhausted before traversal - terminated, the first remaining path element is treated as the - view name. Any subseqent path elements after the view name are - deemed the "subpath". For instance, if ``PATH_INFO`` was - ``/a/b`` and the root returned an "A" object, and the "A" object - returned a "B" object, the router deems that the context is - "object B", the view name is the empty string, and the subpath is - the empty sequence. On the other hand, if ``PATH_INFO`` was - ``/a/b/c`` and "object A" was found but raised a KeyError for the - name ``b``, the router deems that the context is object A, the - view name is ``b`` and the subpath is ``['c']``. - - 7. If a security policy is configured, the router performs a - permission lookup. If a permission declaration is found for the - view name and context implied by the current request, the - security policy is consulted to see if the "current user" (also - determined by the security policy) can perform the action. If he - can, processing continues. If he cannot, an HTTPUnauthorized - error is raised. - - 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 - 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 - generic WSGI ``NotFound`` application is constructed. - -In either case, the resulting WSGI application is called. The WSGI -application's return value is an iterable. This is returned upstream -to the WSGI server. The WSGI application also calls start_response -with a status code and a header list. - -A Sample Application --------------------- - -A typical simple ``repoze.bfg`` application consists of four things: - - 1. A ``views.py`` module, which contains view code. - - 2. A ``models.py`` module, which contains model code. - - 3. A ``configure.zcml`` file which maps view names to model types. - This is also known as the "view registry", although it also - often contains non-view-related declarations. - - 4. A "templates" directory, which is full of zc3.pt templates. - -An application must be a Python package (meaning it must have an -__init__.py and it must be findable on the PYTHONPATH). - -We don't describe any security in our very simple sample application. -Security is optional in a repoze.bfg application; it needn't be used -until necessary. - -views.py -~~~~~~~~ - -A views.py module might look like so:: - - from webob import Response - from repoze.bfg.template import render_template - - def my_hello_view(context, request): - response = Response('Hello from %s @ %s' % ( - context.__name__, - request.environ['PATH_INFO'])) - return response - - def my_template_view(context, request): - return render_template('templates/my.pt', name=context.__name__) - -models.py -~~~~~~~~~ - -A models.py might look like so:: - - from UserDict import UserDict - - from zope.interface import implements - from zope.interface import Attribute - from zope.interface import Interface - - class IMyModel(Interface): - __name__ = Attribute('Name of the model instance') - - class MyModel(UserDict): - implements(IMyModel) - def __init__(self, name): - self.__name__ = name - UserDict.__init__(self, {}) - - # model instance info would typically be stored in a database of some - # kind; here we put it at module scope for demonstration purposes. - - root = MyModel('root') - root['a'] = MyModel('a') - root['b'] = MyModel('b') - - def get_root(environ): - return root - -configure.zcml -~~~~~~~~~~~~~~ - -A view registry might look like so:: - - <configure xmlns="http://namespaces.zope.org/zope" - xmlns:bfg="http://namespaces.repoze.org/bfg" - i18n_domain="repoze.bfg"> - - <!-- this must be included for the view declarations to work --> - <include package="repoze.bfg" /> - - <!-- the default view for a MyModel --> - <bfg:view - for=".models.IMyModel" - factory=".views.my_hello_view" - permission="repoze.view" - /> - - <!-- the templated.html view for a MyModel --> - <bfg:view - for=".models.IMyModel" - factory=".views.my_template_view" - name="templated.html" - permission="repoze.view" - /> - - </configure> - -templates/my.pt -~~~~~~~~~~~~~~~ - -A template that is used by a view might look like so:: - - <html xmlns="http://www.w3.org/1999/xhtml" - xmlns:tal="http://xml.zope.org/namespaces/tal"> - <head></head> - <body> - <h1>My template viewing ${name}</h1> - </body> - </html> - -Running the Application ------------------------ - -To run the application above, the simplest method is to run it -directly from a starter script (although you might also use Paste to -perform this task):: - - from paste import httpserver - - from repoze.bfg import make_app - from myapp.models import get_root - import myapp - - app = make_app(get_root, myapp) - httpserver.serve(app, host='0.0.0.0', port='5432') - -Viewing the Application ------------------------ - -Visit http://localhost:5432/ in your browser. You will see:: - - Hello from root @ / - -Visit http://localhost:5432/a in your browser. You will see:: - - Hello from a @ /a - -Visit http://localhost:5432/b in your browser. You will see:: - - Hello from b @ /b - -Visit http://localhost:5432/templated.html in your browser. You will -see:: - - My template viewing root - - -Visit http://localhost:5432/a/templated.html in your browser. You -will see:: - - My template viewing a - -Visit http://localhost:5432/b/templated.html in your browser. You -will see:: - - My template viewing b +Visit http://bugs.repoze.org to report bugs. Visit +http://svn.repoze.org to download development or tagged versions. |
