[Checkins] SVN: traject/trunk/src/traject/traject.txt Edit documentation.

Martijn Faassen faassen at startifact.com
Mon Nov 16 06:38:35 EST 2009


Log message for revision 105697:
  Edit documentation.
  

Changed:
  U   traject/trunk/src/traject/traject.txt

-=-
Modified: traject/trunk/src/traject/traject.txt
===================================================================
--- traject/trunk/src/traject/traject.txt	2009-11-16 11:35:46 UTC (rev 105696)
+++ traject/trunk/src/traject/traject.txt	2009-11-16 11:38:34 UTC (rev 105697)
@@ -7,62 +7,68 @@
 In web application construction there are two main ways to publish
 objects to the web: routing and traversal. Both are a form of URL
 dispatch: in the end, a function or method is called as the result of
-the pattern of the URL. Both use very different methods to do so,
+a pattern in the URL. Both use very different methods to do so,
 however.
 
 In *routing* a mapping is made from URL patterns to controllers (or
-views). The URL pattern is used to pull information from the URLs used
-and this information is used to determine which particular callable to
-call in the end.
+views) that are called to generate the rendered web page. The URL
+pattern is also used to pull parameter information from the URLs which
+can then be passed on.
 
 Take for instance the URL ``departments/10/employees/17``. A URL
-pattern could declare that the arguments `10`` and ``17`` should be
-taken from this URL by the system. This information is then used by
-the controller to retrieve the correct models from the database. The
-controller then uses information in these models to contruct the
-content of the view, for instance by rendering it with a HTML
+pattern could exist that maps all ``departments/*/employees/*``
+patterns to a particular callable. In addition, the routing system can
+be used to declare that the parameters `10`` and ``17`` should be
+taken from this URL and passed along as arguments to the
+controller. The programmer then programs the controller to retrieve
+correct models from the database using this information. After this
+the controller uses the information in these models to construct the
+contents of the view, for instance by rendering it with a HTML
 template.
 
-In *traversal*, there is no explicit mapping of URLs to controller or
-views. Instead models are traversed step by step, guided by the URL.
-By analogy one can in Python traverse through nested dictionaries
-(``d['a']['b']['c']``), or attributes (``d.a.b.c``). In the end, a
-*view* is looked up for the final model that can be called. The view
-could be a special attribute on the model, or more sophisticated
-systems can be used separating the view from the model.
+In *traversal*, there is no explicit mapping of URLs to controllers or
+views. Instead a model structure is traversed step by step, guided by
+the URL.  By analogy one can in Python traverse through nested
+dictionaries (``d['a']['b']['c']``), or attributes (``d.a.b.c``). In
+the end, a *view* is looked up for the final model that can be
+called. The view could be a special attribute on the model. More
+sophisticated systems can be used to separate the view from the model.
 
-The URL ``departments/10/employees/17`` would be resolved to a
-callable because there is a ``departments`` container model that
-contains ``department`` model objects.  In turn from a ``department``
-model one can traverse to the ``employees`` container, which in turn
-allows traversal to individual employees, such as employee 17. In the
-end a view is looked up for employee 17, and called.
+The URL ``departments/10/employees/17`` would be resolved to a view
+because there is a ``departments`` container model that contains
+``department`` model objects.  In turn from a ``department`` model one
+can traverse to the ``employees`` container, which in turn allows
+traversal to individual employees, such as employee 17. In the end a
+view is looked up for employee 17, and called.
 
-Routing is often used in combination with relational databases
-(exposed by means of an object relational mapper,
-typically). Traversal is often more convenient with in-memory object
-structures or object databases.
+Routing is often used in combination with a relational database,
+typically exposed to objects by means of an object relation mapper.
+Traversal tends to be more convenient with in-memory object structures
+or object databases.
 
 Routing has advantages:
 
-* good way for exposing relational content that doesn't have natural
-  nesting.
+* it is a good way for exposing relational content that doesn't have
+  natural nesting.
 
-* explicit overview of the URL patterns in an application.
+* the pattern registry gives the developer an explicit overview of the
+  URL patterns in an application.
 
-* familiar as used by many frameworks.
+* the approach is familiar as it is used by many frameworks.
 
 Traversal has advantages as well:
 
-* good way for exposing object content that has arbitrary nesting.
+* it is a good way for exposing object content that has arbitrary
+  nesting.
 
-* model-driven: objects come equipped with their views, which allows
-  for easier composition of application from models.
+* model-driven: objects come equipped with their views. This allows
+  the developer to compose an application from models, supporting a
+  declarative approach.
 
-* location-aware: a nested object structure can be location
-  aware. Each model can know about its parent and its name in the
-  URL. This allows for easy construction of URLs for arbitrary models.
-  In addition, security can be declared on higher levels of the
+* location-aware: a nested object structure can easily be made
+  location aware. Each model can know about its parent and its name in
+  the URL. This allows for easy construction of URLs for arbitrary
+  models. In addition, permissions can be declared based on this
   structure.
 
 Traject tries to combine the properties of routing and traversal in a
@@ -75,7 +81,8 @@
 
 * lets the developer explicitly declare URL mappings.
 
-* supports arbitrary nesting.
+* supports arbitrary nesting, as URL mappings can be nested and the
+  system is also easily combined with normal traversal.
 
 * is model-driven. Routing is to models, not to views or controllers.
 
@@ -94,7 +101,7 @@
   the route in order to construct a nested structure. This can mean
   more queries to the database per request. In practice this is often
   mitigated by the fact that the parent models in the structure are
-  frequently needed by the view logic anyway.
+  typically needed by the view logic anyway.
 
 * In Traject each model instance should have one and only one location
   in the URL structure. This allows not only URLs to be resolved to
@@ -128,7 +135,7 @@
   >>> traject.parse('foo/:a/baz/:b')
   ('foo', ':a', 'baz', ':b')
 
-Each variable in a pattern needs to have a different name, however::
+The variable names in a pattern need to be unique::
 
   >>> traject.parse('foo/:a/baz/:a')
   Traceback (innermost last):
@@ -140,16 +147,16 @@
 
 In Traject, the resolution of a URL path results in a model. This
 model can then in turn have views registered for it that allow this
-model to be displayed. How this works is up to the web framework's
-view system.
+model to be displayed. How the view lookup works is up to the web
+framework itself.
 
-How does Traject know which model to return for a path? You can
-register a factory function for a URL pattern. This factory function
-should create or retrieve the model object.
+You tell Traject which model is returned for which path by registering
+a factory function per URL pattern. This factory function should
+create or retrieve the model object.
 
 The factory function receives parameters for each of the matched
 variables in whichever pattern matched - the signature of the factory
-function should account for all the variables in the patterns that are
+function should include all the variables in the patterns that are
 matched.
 
 Let's look at an example.
@@ -161,7 +168,7 @@
 We can see two parameters in this URL pattern: `department_id`` and
 ``customer_id``.
 
-We now define a model object as it might be stored in a database::
+We now define a model as it might be stored in a database::
 
   >>> class Employee(object):
   ...   def __init__(self, department_id, employee_id):
@@ -170,24 +177,27 @@
   ...   def __repr__(self):
   ...     return '<Employee %s %s>' % (self.department_id, self.employee_id)
 
-Now we define the factory function for this URL pattern. The
-parameters in this case would be ``department_id`` and
-``employee_id``::
+We define the factory function for this URL pattern so that an
+instance of this model is returned. The parameters in this case would
+be ``department_id`` and ``employee_id``::
 
   >>> def factory(department_id, employee_id): 
   ...   return Employee(department_id, employee_id)
 
 The factory function in this case just creates a ``Employee`` object
-on the fly. Instead, it could do a database query based on its parents
-and the parameters supplied.
+on the fly. In the context of a relation database it could instead
+perform a database query based on the parameters supplied.
 
-In order to register this factory function, we need a registry of patterns, so
-we'll create one::
+In order to register this factory function, we need a registry of
+patterns, so we'll create one::
 
   >>> patterns = traject.Patterns()
 
-A pattern needs to be registered for a class or an interface. In this
-case we'll register the patterns for a class ``Root``::
+Patterns need to be registered for particular classes or
+(``zope.interface``) interfaces. This is so that multiple pattern
+registries can be supported, each associated with a particular root
+object. In this case we'll register the patterns for a class
+``Root``::
 
   >>> class Root(object):
   ...    pass
@@ -199,14 +209,17 @@
 Resolving a path
 ----------------
 
-Now we are ready to resolve paths. A path is part of a URL such as
+We are ready to resolve paths. A path is part of a URL such as
 ``foo/bar/baz``. It looks very much like a pattern, but all the
 variables will have been filled in.
 
 The models retrieved by resolving paths will be *located*. Ultimately
 their ancestor will be a particular root model from which all paths
-are resolved. Let's create one here for demonstration purposes::
+are resolved. The root model itself is not resolved by a pattern: it
+is the root from which all patterns are resolved.
 
+We create a root model first::
+
   >>> root = Root()
 
 When a path is resolved, a complete chain of ancestors from model to
@@ -216,23 +229,27 @@
 and ``departments/:department_id/employees`` all have no factory
 registered.
 
+These steps will have a *default* model registered for them instead.
 When resolving the pattern we need to supply a special default factory
-which will generate an object in such cases. Let's make one here. The
-factory function needs to be able to deal with arbitrary keyword
-arguments as any number of parameters might be supplied::
+which will generate the default models when needed.
 
+Let's make a default factory here. The factory function needs to be
+able to deal with arbitrary keyword arguments as any number of
+parameters might be supplied::
+
   >>> class Default(object):
   ...     def __init__(self, **kw):
   ...         pass
 
-We can now resolve a path::
+Now that we have a Default factory, we can try to resolve a path::
   
   >>> obj = patterns.resolve(root, u'departments/1/employees/2', Default)
   >>> obj
   <Employee 1 2>
 
-We can also resolve a stack of names (where the first name to resolve
-is on the top of the stack)::
+An alternative ``resolve_stack`` method allows us to resolve a stack
+of names instead (where the first name to resolve is on the top of the
+stack)::
 
   >>> l = [u'departments', u'1', u'employees', u'2']
   >>> l.reverse()
@@ -243,12 +260,12 @@
 ---------
 
 Traject supports the notion of locations. After we find a model, the
-model will have received two special attributes::
+model receive two special attributes::
 
-*  ``__name__``: the name we addressed this object with in the path
+*  ``__name__``: the name we addressed this object with in the path.
 
 * ``__parent__``: the parent of the model. This is an model that
-  matches the path without the last step.
+  matches the parent path (the path without the last step).
 
 The parent will in turn have a parent as well, all the way up to the
 ultimate ancestor, the root.
@@ -287,18 +304,32 @@
 In a mixed traject/traversal environment, for instance where view
 lookup is done by traversal, it can be useful to be able to resolve a
 path according to the patterns registered until no longer
-possible. The rest of the the steps are not followed.
+possible. The rest of the the steps are not followed, and are assumed
+to be consumed in some other way using the traversal system.
 
-The method consume will consume steps as far as possible, return the
-steps that weren't consumed yet, those steps that were consumed, and
-the object it managed to find::
+The ``consume`` method will consume steps as far as possible, return
+the steps that weren't consumed yet, those steps that were consumed,
+and the object it managed to find::
 
   >>> unconsumed, consumed, last_obj = patterns.consume(root, 
   ...       'departments/1/some_view', Default)
+
+``departments/1/some_view`` cannot be consumed fully by the pattern
+``departments/:department_id/employees/:employee_id``, as
+``some_view`` does not match the expected ``employees``.
+
+We can see which parts of the path could not be consumed::
+
   >>> unconsumed
   ['some_view']
+
+And which parts of the path were consumed as part of a pattern::
+
   >>> consumed
   ['departments', '1']
+
+The last object we managed to consume stands for ``1``::
+
   >>> isinstance(last_obj, Default)
   True
   >>> last_obj.__name__
@@ -331,19 +362,33 @@
 Giving a model its location
 ---------------------------
 
-When we retrieve a model directly by means of a query or construction
-in our application code (such as in our view logic) it will not have a
-location. This is inconvenient if we want to ask the model questions
-that need such location information (such as its URL or its
-location-dependent permissions).
+Models are automatically given their location after traversal. There
+is however another case where giving an object a location can be
+useful. We may for instance retrieve an object from a query and then
+wish to construct a URL to it, or check whether it has
+location-dependent permissions. Traject therefore also offers
+functionality to reconstruct an object's location.
 
-We can register a special function per model class that is the inverse
-of the factory. Given a model instance, it needs to return the
-arguments used in the pattern towards it::
+In order to do this, we need to register a special function per model
+class that is the inverse of the factory. Given a model instance, it
+needs to return the arguments used in the pattern. Thus, for the
+following pattern::
 
+  >>> pattern_str = u'departments/:department_id/employees/:employee_id'
+
+and a given model, we would need to reconstruct the arguments
+``department_id`` and ``employee_id`` from that model.
+
+This is a function that does this for ``Employee``::
+
   >>> def employee_arguments(obj):
   ...     return {'employee_id': obj.employee_id, 
   ...             'department_id': obj.department_id} 
+
+
+When we register it, we also need to supply the class for which it can
+reconstruct the arguments, in this case, ``Employee``::
+
   >>> patterns.register_inverse(Root, Employee, pattern_str, employee_arguments)
 
 Let's construct some employee now::
@@ -362,7 +407,7 @@
     ...
   AttributeError: ...
 
-We can now locate it::
+We can now use the ``locate`` method to locate it::
 
   >>> patterns.locate(root, m, Default)
 
@@ -381,3 +426,19 @@
   u'departments'
   >>> p3.__parent__ is root
   True
+
+A global patterns registry
+--------------------------
+
+Since the patterns registry is clever enough to distinguish between
+roots, in many scenarios only a single, global ``Patterns`` registry
+is needed. Top-level functions have been made available in the
+``traject`` namespace to manipulate and use this patterns registry::
+
+  traject.register
+  traject.register_inverse
+  traject.resolve
+  traject.resolve_stack
+  traject.consume
+  traject.consume_stack
+  traject.locate



More information about the checkins mailing list