more / cleaner / in code documentation of vreg, selectors and appobject stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 06 Apr 2010 10:11:40 +0200
branchstable
changeset 5147 70181998897f
parent 5146 fe56baf63ecb
child 5148 ec0ea7366066
more / cleaner / in code documentation of vreg, selectors and appobject
appobject.py
cwvreg.py
doc/book/en/development/index.rst
doc/book/en/development/vreg.rst
doc/book/en/development/vreg/appobject.rst
doc/book/en/development/vreg/selectors.rst
doc/book/en/development/vreg/vreg.rst
selectors.py
--- a/appobject.py	Tue Apr 06 10:10:47 2010 +0200
+++ b/appobject.py	Tue Apr 06 10:11:40 2010 +0200
@@ -1,11 +1,18 @@
-"""Base class for dynamically loaded objects accessible through the vregistry.
-
-You'll also find some convenience classes to build selectors.
+# :organization: Logilab
+# :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+# :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+The `AppObject` class
+---------------------
 
-:organization: Logilab
-:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+The AppObject class is the base class for all dynamically loaded objects
+(application objects) accessible through the vregistry.
+
+We can find a certain number of attributes and methods defined in this class and
+common to all the application objects.
+
+.. autoclass:: AppObject
 """
 __docformat__ = "restructuredtext en"
 
@@ -21,13 +28,17 @@
 # selector base classes and operations ########################################
 
 def objectify_selector(selector_func):
-    """convenience decorator for simple selectors where a class definition
-    would be overkill::
+    """Most of the time, a simple score function is enough to build a selector.
+    The :func:`objectify_selector` decorator turn it into a proper selector
+    class::
 
         @objectify_selector
         def one(cls, *args, **kwargs):
             return 1
 
+        class MyView(View):
+            __select__ = View.__select__ & one()
+
     """
     return type(selector_func.__name__, (Selector,),
                 {'__doc__': selector_func.__doc__,
@@ -49,7 +60,7 @@
 
 class Selector(object):
     """base class for selector classes providing implementation
-    for operators ``&`` and ``|``
+    for operators ``&``, ``|`` and  ``~``
 
     This class is only here to give access to binary operators, the
     selector logic itself should be implemented in the __call__ method
@@ -205,42 +216,77 @@
     selected according to a context (usually at least a request and a result
     set).
 
-    Concrete application objects classes are designed to be loaded by the
-    vregistry and should be accessed through it, not by direct instantiation.
+    The following attributes should be set on concret appobject classes:
 
-    The following attributes should be set on concret appobject classes:
-    :__registry__:
+    :attr:`__registry__`
       name of the registry for this object (string like 'views',
       'templates'...)
-    :__regid__:
+
+    :attr:`__regid__`
       object's identifier in the registry (string like 'main',
       'primary', 'folder_box')
-    :__select__:
+
+    :attr:`__select__`
       class'selector
 
-    Moreover, the `__abstract__` attribute may be set to True to indicate
-    that a appobject is abstract and should not be registered.
+    Moreover, the `__abstract__` attribute may be set to True to indicate that a
+    class is abstract and should not be registered.
 
     At selection time, the following attributes are set on the instance:
 
-    :_cw:
+    :attr:`_cw`
       current request
-    :cw_extra_kwargs:
+    :attr:`cw_extra_kwargs`
       other received arguments
 
-    only if rset is found in arguments (in which case rset/row/col will be
-    removed from cwextra_kwargs):
+    And also the following, only if `rset` is found in arguments (in which case
+    rset/row/col will be removed from `cwextra_kwargs`):
 
-    :cw_rset:
+    :attr:`cw_rset`
       context result set or None
-    :cw_row:
+
+    :attr:`cw_row`
       if a result set is set and the context is about a particular cell in the
       result set, and not the result set as a whole, specify the row number we
       are interested in, else None
-    :cw_col:
+
+    :attr:`cw_col`
       if a result set is set and the context is about a particular cell in the
       result set, and not the result set as a whole, specify the col number we
       are interested in, else None
+
+
+    .. Note::
+
+      * do not inherit directly from this class but from a more specific class
+        such as `AnyEntity`, `EntityView`, `AnyRsetView`, `Action`...
+
+      * to be recordable, a subclass has to define its registry (attribute
+        `__registry__`) and its identifier (attribute `__regid__`). Usually
+        you don't have to take care of the registry since it's set by the base
+        class, only the identifier `id`
+
+      * application objects are designed to be loaded by the vregistry and
+        should be accessed through it, not by direct instantiation, besides
+        to use it as base classe.
+
+
+      * When we inherit from `AppObject` (even not directly), you *always* have
+        to use **super()** to get the methods and attributes of the superclasses,
+        and not use the class identifier.
+
+        For example, instead of writting::
+
+          class Truc(PrimaryView):
+              def f(self, arg1):
+                  PrimaryView.f(self, arg1)
+
+        You must write::
+
+          class Truc(PrimaryView):
+              def f(self, arg1):
+                  super(Truc, self).f(arg1)
+
     """
     __registry__ = None
     __regid__ = None
--- a/cwvreg.py	Tue Apr 06 10:10:47 2010 +0200
+++ b/cwvreg.py	Tue Apr 06 10:11:40 2010 +0200
@@ -1,9 +1,178 @@
-"""extend the generic VRegistry with some cubicweb specific stuff
+# :organization: Logilab
+# :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+# :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+""".. VRegistry:
+
+The `VRegistry`
+---------------
+
+The `VRegistry` can be seen as a two level dictionary. It contains all objects
+loaded dynamically to build a |cubicweb| application. Basically:
+
+* first level key return a *registry*. This key corresponds to the `__registry__`
+  attribute of application object classes
+
+* second level key return a list of application objects which share the same
+  identifier. This key corresponds to the `__regid__` attribute of application
+  object classes.
+
+A *registry* hold a specific kind of application objects. You've for instance
+a registry for entity classes, another for views, etc...
+
+The `VRegistry` has two main responsibilities:
+
+- being the access point to all registries
+
+- handling the registration process at startup time, and during automatic
+  reloading in debug mode.
+
+
+.. _AppObjectRecording:
+
+Details of the recording process
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. index::
+   vregistry: registration_callback
+
+On startup, |cubicweb| have to load application objects defined in its library
+and in cubes used by the instance. Application objects from the library are
+loaded first, then those provided by cubes are loaded in an ordered way (e.g. if
+your cube depends on an other, objects from the dependancy will be loaded
+first). Cube's modules or packages where appobject are looked at is explained in
+:ref:`cubelayout`.
+
+For each module:
+
+* by default all objects are registered automatically
+
+* if some objects have to replace other objects, or be included only if some
+  condition is true, you'll have to define a `registration_callback(vreg)`
+  function in your module and explicitly register **all objects** in this module,
+  using the api defined below.
+
+.. Note::
+    Once the function `registration_callback(vreg)` is implemented in a module,
+    all the objects from this module have to be explicitly registered as it
+    disables the automatic objects registration.
+
+
+API for objects registration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here are the registration methods that you can use in the `registration_callback`
+to register your objects to the `VRegistry` instance given as argument (usually
+named `vreg`):
+
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_all
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_and_replace
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_if_interface_found
+.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.unregister
+
+
+Examples:
+
+.. sourcecode:: python
+
+   # web/views/basecomponents.py
+   def registration_callback(vreg):
+      # register everything in the module except SeeAlsoComponent
+      vreg.register_all(globals().values(), __name__, (SeeAlsoVComponent,))
+      # conditionally register SeeAlsoVComponent
+      if 'see_also' in vreg.schema:
+          vreg.register(SeeAlsoVComponent)
 
-:organization: Logilab
-:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+In this example, we register all application object classes defined in the module
+except `SeeAlsoVComponent`. This class is then registered only if the 'see_also'
+relation type is defined in the instance'schema.
+
+.. sourcecode:: python
+
+   # goa/appobjects/sessions.py
+   def registration_callback(vreg):
+      vreg.register(SessionsCleaner)
+      # replace AuthenticationManager by GAEAuthenticationManager
+      vreg.register_and_replace(GAEAuthenticationManager, AuthenticationManager)
+      # replace PersistentSessionManager by GAEPersistentSessionManager
+      vreg.register_and_replace(GAEPersistentSessionManager, PersistentSessionManager)
+
+In this example, we explicitly register classes one by one:
+
+* the `SessionCleaner` class
+* the `GAEAuthenticationManager` to replace the `AuthenticationManager`
+* the `GAEPersistentSessionManager` to replace the `PersistentSessionManager`
+
+If at some point we register a new appobject class in this module, it won't be
+registered at all without modification to the `registration_callback`
+implementation. The previous example will register it though, thanks to the call
+to the `register_all` method.
+
+
+.. _Selection:
+
+Runtime objects selection
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that we've all application objects loaded, the question is : when I want some
+specific object, for instance the primary view for a given entity, how do I get
+the proper object ? This is what we call the **selection mechanism**.
+
+As explained in the :ref:`Concepts` section:
+
+* each application object has a **selector**, defined by its `__select__` class attribute
+
+* this selector is responsible to return a **score** for a given context
+
+  - 0 score means the object doesn't apply to this context
+
+  - else, the higher the score, the better the object suits the context
+
+* the object with the higher score is selected.
+
+.. Note::
+
+  When no score is higher than the others, an exception is raised in development
+  mode to let you know that the engine was not able to identify the view to
+  apply. This error is silenced in production mode and one of the objects with
+  the higher score is picked.
+
+  In such cases you would need to review your design and make sure your selectors
+  or appobjects are properly defined.
+
+For instance, if you are selecting the primary (eg `__regid__ = 'primary'`) view (eg
+`__registry__ = 'views'`) for a result set containing a `Card` entity, 2 objects
+will probably be selectable:
+
+* the default primary view (`__select__ = implements('Any')`), meaning
+  that the object is selectable for any kind of entity type
+
+* the specific `Card` primary view (`__select__ = implements('Card')`,
+  meaning that the object is selectable for Card entities
+
+Other primary views specific to other entity types won't be selectable in this
+case. Among selectable objects, the implements selector will return a higher
+score than the second view since it's more specific, so it will be selected as
+expected.
+
+.. _SelectionAPI:
+
+API for objects selections
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here is the selection API you'll get on every registry. Some of them, as the
+'etypes' registry, containing entity classes, extend it. In those methods,
+`*args, **kwargs` is what we call the **context**. Those arguments are given to
+selectors that will inspect there content and return a score accordingly.
+
+.. automethod:: cubicweb.vregistry.Registry.select
+
+.. automethod:: cubicweb.vregistry.Registry.select_or_none
+
+.. automethod:: cubicweb.vregistry.Registry.possible_objects
+
+.. automethod:: cubicweb.vregistry.Registry.object_by_id
 """
 __docformat__ = "restructuredtext en"
 _ = unicode
--- a/doc/book/en/development/index.rst	Tue Apr 06 10:10:47 2010 +0200
+++ b/doc/book/en/development/index.rst	Tue Apr 06 10:11:40 2010 +0200
@@ -11,7 +11,7 @@
    :numbered:
 
    cubes/index
-   vreg/index
+   vreg.rst
    datamodel/index
    entityclasses/index
    devcore/index
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/development/vreg.rst	Tue Apr 06 10:11:40 2010 +0200
@@ -0,0 +1,110 @@
+The VRegistry, selectors and application objects
+================================================
+
+This chapter talk about core concepts of the |cubicweb| framework, that make it
+different from other framework (and probably not easy to grasp at a first
+glance). You won't be able to do advanced development with |cubicweb| without
+a good understanding of what's explain below.
+
+This chapter goes deep into details. You don't have to remember them all but keep
+it in mind so you can go back there later...
+
+.. toctree::
+   :maxdepth: 1
+
+.. autodocstring:: cubicweb.cwvreg
+.. autodocstring:: cubicweb.selectors
+.. automodule:: cubicweb.appobject
+
+Base selectors
+--------------
+
+Selectors are scoring functions that are called by the registry to tell whenever
+an appobject can be selected in a given context. Selector sets are for instance
+the glue that tie views to the data model. Using them appropriately is an
+essential part of the construction of well behaved cubes.
+
+Of course you may have to write your own set of selectors as your needs grows and
+you get familiar with the framework (see :ref:`CustomSelectors`).
+
+Here is a description of generic selectors provided by CubicWeb that should suit
+most of your needs.
+
+Bare selectors
+~~~~~~~~~~~~~~
+Those selectors are somewhat dumb, which doesn't mean they're not (very) useful.
+
+.. autoclass:: cubicweb.appobject.yes
+.. autoclass:: cubicweb.selectors.match_kwargs
+.. autoclass:: cubicweb.selectors.appobject_selectable
+
+
+Result set selectors
+~~~~~~~~~~~~~~~~~~~~~
+Those selectors are looking for a result set in the context ('rset' argument or
+the input context) and match or not according to its shape. Some of these
+selectors have different behaviour if a particular cell of the result set is
+specified using 'row' and 'col' arguments of the input context or not.
+
+.. autoclass:: cubicweb.selectors.none_rset
+.. autoclass:: cubicweb.selectors.any_rset
+.. autoclass:: cubicweb.selectors.nonempty_rset
+.. autoclass:: cubicweb.selectors.empty_rset
+.. autoclass:: cubicweb.selectors.one_line_rset
+.. autoclass:: cubicweb.selectors.multi_lines_rset
+.. autoclass:: cubicweb.selectors.multi_columns_rset
+.. autoclass:: cubicweb.selectors.paginated_rset
+.. autoclass:: cubicweb.selectors.sorted_rset
+.. autoclass:: cubicweb.selectors.one_etype_rset
+.. autoclass:: cubicweb.selectors.multi_etypes_rset
+
+
+Entity selectors
+~~~~~~~~~~~~~~~~
+Those selectors are looking for either an `entity` argument in the input context,
+or entity found in the result set ('rset' argument or the input context) and
+match or not according to entity's (instance or class) properties.
+
+.. autoclass:: cubicweb.selectors.non_final_entity
+.. autoclass:: cubicweb.selectors.implements
+.. autoclass:: cubicweb.selectors.score_entity
+.. autoclass:: cubicweb.selectors.rql_condition
+.. autoclass:: cubicweb.selectors.relation_possible
+.. autoclass:: cubicweb.selectors.partial_relation_possible
+.. autoclass:: cubicweb.selectors.has_related_entities
+.. autoclass:: cubicweb.selectors.partial_has_related_entities
+.. autoclass:: cubicweb.selectors.has_permission
+.. autoclass:: cubicweb.selectors.has_add_permission
+
+
+Logged user selectors
+~~~~~~~~~~~~~~~~~~~~~
+Those selectors are looking for properties of the user issuing the request.
+
+.. autoclass:: cubicweb.selectors.anonymous_user
+.. autoclass:: cubicweb.selectors.authenticated_user
+.. autoclass:: cubicweb.selectors.match_user_groups
+
+
+Web request selectors
+~~~~~~~~~~~~~~~~~~~~~
+Those selectors are looking for properties of *web* request, they can not be
+used on the data repository side.
+
+.. autoclass:: cubicweb.selectors.match_form_params
+.. autoclass:: cubicweb.selectors.match_search_state
+.. autoclass:: cubicweb.selectors.match_context_prop
+.. autoclass:: cubicweb.selectors.match_view
+.. autoclass:: cubicweb.selectors.primary_view
+.. autoclass:: cubicweb.selectors.specified_etype_implements
+
+
+Other selectors
+~~~~~~~~~~~~~~~
+.. autoclass:: cubicweb.selectors.match_transition
+
+You'll also find some other (very) specific selectors hidden in other modules
+than :mod:`cubicweb.selectors`.
+
+
+.. |cubicweb| replace:: *CubicWeb*
--- a/doc/book/en/development/vreg/appobject.rst	Tue Apr 06 10:10:47 2010 +0200
+++ b/doc/book/en/development/vreg/appobject.rst	Tue Apr 06 10:11:40 2010 +0200
@@ -1,32 +1,7 @@
-
-
-The `AppObject` class
-~~~~~~~~~~~~~~~~~~~~~
-
-In general:
-
-* we do not inherit directly from this class but from a more specific
-  class such as `AnyEntity`, `EntityView`, `AnyRsetView`,
-  `Action`...
-
-* to be recordable, a subclass has to define its own register (attribute
-  `__registry__`) and its identifier (attribute `id`). Usually we do not have
-  to take care of the register, only the identifier `id`.
-
-We can find a certain number of attributes and methods defined in this class
-and common to all the application objects.
-
-At recording time, the following attributes are dynamically added to
-the *subclasses*:
-
-* `vreg`, the `vregistry` of the instance
-* `schema`, the instance schema
-* `config`, the instance configuration
-
-We also find on instances, the following attributes:
-
-* ._cw`, `Request` instance
-* `rset`, the *result set* associated to the object if necessary
+XXX todo:
+* configure members for doc generated for appojbect class,
+* configure module's member into the module
+* put doc below somewhere else
 
 :URL handling:
   * `build_url(*args, **kwargs)`, returns an absolute URL based on the
@@ -52,20 +27,3 @@
 
   * `tal_render(template, variables)`, renders a precompiled page template with
     variables in the given dictionary as context
-
-.. note::
-  When we inherit from `AppObject` (even not directly), you *always* have to use
-  **super()** to get the methods and attributes of the superclasses, and not
-  use the class identifier.
-
-  For example, instead of writting: ::
-
-      class Truc(PrimaryView):
-          def f(self, arg1):
-              PrimaryView.f(self, arg1)
-
-  You must write: ::
-
-      class Truc(PrimaryView):
-          def f(self, arg1):
-              super(Truc, self).f(arg1)
--- a/doc/book/en/development/vreg/selectors.rst	Tue Apr 06 10:10:47 2010 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +0,0 @@
-Base selectors
---------------
-
-Selectors are scoring functions that are called by the registry to tell whenever
-an appobject can be selected in a given context. Selector sets are for instance
-the glue that tie views to the data model. Using them appropriately is an
-essential part of the construction of well behaved cubes.
-
-Of course you may have to write your own set of selectors as your needs grows and
-you get familiar with the framework (see :ref:`CustomSelectors`).
-
-Here is a description of generic selectors provided by CubicWeb that should suit
-most of your needs.
-
-Bare selectors
-~~~~~~~~~~~~~~
-Those selectors are somewhat dumb, which doesn't mean they're not (very) useful.
-
-.. autoclass:: cubicweb.appobject.yes
-.. autoclass:: cubicweb.selectors.match_kwargs
-.. autoclass:: cubicweb.selectors.appobject_selectable
-
-
-Result set selectors
-~~~~~~~~~~~~~~~~~~~~~
-Those selectors are looking for a result set in the context ('rset' argument or
-the input context) and match or not according to its shape. Some of these
-selectors have different behaviour if a particular cell of the result set is
-specified using 'row' and 'col' arguments of the input context or not.
-
-.. autoclass:: cubicweb.selectors.none_rset
-.. autoclass:: cubicweb.selectors.any_rset
-.. autoclass:: cubicweb.selectors.nonempty_rset
-.. autoclass:: cubicweb.selectors.empty_rset
-.. autoclass:: cubicweb.selectors.one_line_rset
-.. autoclass:: cubicweb.selectors.multi_lines_rset
-.. autoclass:: cubicweb.selectors.multi_columns_rset
-.. autoclass:: cubicweb.selectors.paginated_rset
-.. autoclass:: cubicweb.selectors.sorted_rset
-.. autoclass:: cubicweb.selectors.one_etype_rset
-.. autoclass:: cubicweb.selectors.multi_etypes_rset
-
-
-Entity selectors
-~~~~~~~~~~~~~~~~
-Those selectors are looking for either an `entity` argument in the input context,
-or entity found in the result set ('rset' argument or the input context) and
-match or not according to entity's (instance or class) properties.
-
-.. autoclass:: cubicweb.selectors.non_final_entity
-.. autoclass:: cubicweb.selectors.implements
-.. autoclass:: cubicweb.selectors.score_entity
-.. autoclass:: cubicweb.selectors.rql_condition
-.. autoclass:: cubicweb.selectors.relation_possible
-.. autoclass:: cubicweb.selectors.partial_relation_possible
-.. autoclass:: cubicweb.selectors.has_related_entities
-.. autoclass:: cubicweb.selectors.partial_has_related_entities
-.. autoclass:: cubicweb.selectors.has_permission
-.. autoclass:: cubicweb.selectors.has_add_permission
-
-
-Logged user selectors
-~~~~~~~~~~~~~~~~~~~~~
-Those selectors are looking for properties of the user issuing the request.
-
-.. autoclass:: cubicweb.selectors.anonymous_user
-.. autoclass:: cubicweb.selectors.authenticated_user
-.. autoclass:: cubicweb.selectors.match_user_groups
-
-
-Web request selectors
-~~~~~~~~~~~~~~~~~~~~~
-Those selectors are looking for properties of *web* request, they can not be
-used on the data repository side.
-
-.. autoclass:: cubicweb.selectors.match_form_params
-.. autoclass:: cubicweb.selectors.match_search_state
-.. autoclass:: cubicweb.selectors.match_context_prop
-.. autoclass:: cubicweb.selectors.match_view
-.. autoclass:: cubicweb.selectors.primary_view
-.. autoclass:: cubicweb.selectors.specified_etype_implements
-
-
-Other selectors
-~~~~~~~~~~~~~~~
-.. autoclass:: cubicweb.selectors.match_transition
-
-You'll also find some other (very) specific selectors hidden in other modules
-than :mod:`cubicweb.selectors`.
--- a/doc/book/en/development/vreg/vreg.rst	Tue Apr 06 10:10:47 2010 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,323 +0,0 @@
-.. VRegistry:
-
-The `VRegistry`
----------------
-
-The `VRegistry` can be seen as a two level dictionary. It contains all objects
-loaded dynamically to build a |cubicweb| application. Basically:
-
-* first level key return a *registry*. This key corresponds to the `__registry__`
-  attribute of application object classes
-
-* second level key return a list of application objects which share the same
-  identifier. This key corresponds to the `__regid__` attribute of application
-  object classes.
-
-A *registry* hold a specific kind of application objects. You've for instance
-a registry for entity classes, another for views, etc...
-
-The `VRegistry` has two main responsibilities:
-
-- being the access point to all registries
-
-- handling the registration process at startup time, and during automatic
-  reloading in debug mode.
-
-
-.. _AppObjectRecording:
-
-Managing the recording process
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Details of the recording process
-````````````````````````````````
-
-.. index::
-   vregistry: registration_callback
-
-On startup, |cubicweb| have to load application objects defined in its library
-and in cubes used by the instance. Application objects from the library are
-loaded first, then those provided by cubes are loaded in an ordered way (e.g. if
-your cube depends on an other, objects from the dependancy will be loaded
-first). Cube's modules or packages where appobject are looked at is explained in
-:ref:`cubelayout`.
-
-For each module:
-
-* by default all objects are registered automatically
-
-* if some objects have to replace other objects, or be included only if some
-  condition is true, you'll have to define a `registration_callback(vreg)`
-  function in your module and explicitly register **all objects** in this module,
-  using the api defined below.
-
-.. Note::
-    Once the function `registration_callback(vreg)` is implemented in a module,
-    all the objects from this module have to be explicitly registered as it
-    disables the automatic objects registration.
-
-
-API for objects registration
-````````````````````````````
-
-Here are the registration methods that you can use in the `registration_callback`
-to register your objects to the `VRegistry` instance given as argument (usually
-named `vreg`):
-
-.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_all
-.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_and_replace
-.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register
-.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_if_interface_found
-.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.unregister
-
-
-Examples
-````````
-.. sourcecode:: python
-
-   # web/views/basecomponents.py
-   def registration_callback(vreg):
-      # register everything in the module except SeeAlsoComponent
-      vreg.register_all(globals().values(), __name__, (SeeAlsoVComponent,))
-      # conditionally register SeeAlsoVComponent
-      if 'see_also' in vreg.schema:
-          vreg.register(SeeAlsoVComponent)
-
-In this example, we register all application object classes defined in the module
-except `SeeAlsoVComponent`. This class is then registered only if the 'see_also'
-relation type is defined in the instance'schema.
-
-.. sourcecode:: python
-
-   # goa/appobjects/sessions.py
-   def registration_callback(vreg):
-      vreg.register(SessionsCleaner)
-      # replace AuthenticationManager by GAEAuthenticationManager
-      vreg.register_and_replace(GAEAuthenticationManager, AuthenticationManager)
-      # replace PersistentSessionManager by GAEPersistentSessionManager
-      vreg.register_and_replace(GAEPersistentSessionManager, PersistentSessionManager)
-
-In this example, we explicitly register classes one by one:
-
-* the `SessionCleaner` class
-* the `GAEAuthenticationManager` to replace the `AuthenticationManager`
-* the `GAEPersistentSessionManager` to replace the `PersistentSessionManager`
-
-If at some point we register a new appobject class in this module, it won't be
-registered at all without modification to the `registration_callback`
-implementation. The previous example will register it though, thanks to the call
-to the `register_all` method.
-
-.. _Selection:
-
-Runtime objects selection
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Now that we've all application objects loaded, the question is : when I want some
-specific object, for instance the primary view for a given entity, how do I get
-the proper object ? This is what we call the **selection mechanism**.
-
-As explained in the :ref:`Concepts` section:
-
-* each application object has a **selector**, defined by its `__select__` class attribute
-
-* this selector is responsible to return a **score** for a given context
-
-  - 0 score means the object doesn't apply to this context
-
-  - else, the higher the score, the better the object suits the context
-
-* the object with the higher score is selected.
-
-.. Note::
-
-  When no score is higher than the others, an exception is raised in development
-  mode to let you know that the engine was not able to identify the view to
-  apply. This error is silenced in production mode and one of the objects with
-  the higher score is picked.
-
-  In such cases you would need to review your design and make sure your selectors
-  or appobjects are properly defined.
-
-For instance, if you are selecting the primary (eg `__regid__ = 'primary'`) view (eg
-`__registry__ = 'views'`) for a result set containing a `Card` entity, 2 objects
-will probably be selectable:
-
-* the default primary view (`__select__ = implements('Any')`), meaning
-  that the object is selectable for any kind of entity type
-
-* the specific `Card` primary view (`__select__ = implements('Card')`,
-  meaning that the object is selectable for Card entities
-
-Other primary views specific to other entity types won't be selectable in this
-case. Among selectable objects, the implements selector will return a higher
-score than the second view since it's more specific, so it will be selected as
-expected.
-
-.. _SelectionAPI:
-
-API for objects selections
-``````````````````````````
-
-Here is the selection API you'll get on every registry. Some of them, as the
-'etypes' registry, containing entity classes, extend it. In those methods,
-`*args, **kwargs` is what we call the **context**. Those arguments are given to
-selectors that will inspect there content and return a score accordingly.
-
-.. automethod:: cubicweb.vregistry.Registry.select
-
-.. automethod:: cubicweb.vregistry.Registry.select_or_none
-
-.. automethod:: cubicweb.vregistry.Registry.possible_objects
-
-.. automethod:: cubicweb.vregistry.Registry.object_by_id
-
-
-.. _Selectors:
-
-Selectors
----------
-
-Using and combining existant selectors
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can combine selectors using the `&`, `|` and `~` operators.
-
-When two selectors are combined using the `&` operator (formerly `chainall`), it
-means that both should return a positive score. On success, the sum of scores is returned.
-
-When two selectors are combined using the `|` operator (former `chainfirst`), it
-means that one of them should return a positive score. On success, the first
-positive score is returned.
-
-You can also "negate" a selector by precedeing it by the `~` unary operator.
-
-Of course you can use parens to balance expressions.
-
-.. Note:
-  When one chains selectors, the final score is the sum of the score of each
-  individual selector (unless one of them returns 0, in which case the object is
-  non selectable)
-
-
-Example
-~~~~~~~
-
-The goal: when on a Blog, one wants the RSS link to refer to blog entries, not to
-the blog entity itself.
-
-To do that, one defines a method on entity classes that returns the RSS stream
-url for a given entity. The default implementation on
-:class:`~cubicweb.entities.AnyEntity` (the generic entity class used as base for
-all others) and a specific implementation on Blog will do what we want.
-
-But when we have a result set containing several Blog entities (or different
-entities), we don't know on which entity to call the aforementioned method. In
-this case, we keep the generic behaviour.
-
-Hence we have two cases here, one for a single-entity rsets, the other for
-multi-entities rsets.
-
-In web/views/boxes.py lies the RSSIconBox class. Look at its selector ::
-
-  class RSSIconBox(ExtResourcesBoxTemplate):
-    """just display the RSS icon on uniform result set"""
-    __select__ = ExtResourcesBoxTemplate.__select__ & non_final_entity()
-
-It takes into account:
-
-* the inherited selection criteria (one has to look them up in the class
-  hierarchy to know the details)
-
-* :class:`~cubicweb.selectors.non_final_entity`, which filters on result sets
-  containing non final entities (a 'final entity' being synonym for entity
-  attributes type, eg `String`, `Int`, etc)
-
-This matches our second case. Hence we have to provide a specific component for
-the first case::
-
-  class EntityRSSIconBox(RSSIconBox):
-    """just display the RSS icon on uniform result set for a single entity"""
-    __select__ = RSSIconBox.__select__ & one_line_rset()
-
-Here, one adds the :class:`~cubicweb.selectors.one_line_rset` selector, which
-filters result sets of size 1. Thus, on a result set containing multiple
-entities, :class:`one_line_rset` makes the EntityRSSIconBox class non
-selectable. However for a result set with one entity, the `EntityRSSIconBox`
-class will have a higher score than `RSSIconBox`, which is what we wanted.
-
-Of course, once this is done, you have to:
-
-* fill in the call method of `EntityRSSIconBox`
-
-* provide the default implementation of the method returning the RSS stream url
-  on :class:`~cubicweb.entities.AnyEntity`
-
-* redefine this method on `Blog`.
-
-
-When to use selectors?
-~~~~~~~~~~~~~~~~~~~~~~
-
-Selectors are to be used whenever arises the need of dispatching on the shape or
-content of a result set or whatever else context (value in request form params,
-authenticated user groups, etc...). That is, almost all the time.
-
-.. XXX add and example of a single view w/ big "if" inside splitted into two views with appropriate selectors.
-
-
-.. _CustomSelectors:
-
-Defining your own selectors
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. autoclass:: cubicweb.appobject.Selector
-   :members: __call__
-
-.. autofunction:: cubicweb.appobject.objectify_selector
-
-Selectors __call__ should *always* return a positive integer, and shall never
-return `None`.
-
-Useful abstract base classes for 'entity' selectors:
-
-.. autoclass:: cubicweb.selectors.EClassSelector
-.. autoclass:: cubicweb.selectors.EntitySelector
-
-Also, think to use the `lltrace` decorator on your selector class' :meth:`__call__` method
-or below the :func:`objectify_selector` decorator of your selector function so it gets
-traceable when :class:`traced_selection` is activated (see :ref:DebuggingSelectors).
-
-.. autofunction:: cubicweb.selectors.lltrace
-
-
-.. _DebuggingSelectors:
-
-Debugging selection
-~~~~~~~~~~~~~~~~~~~
-
-Once in a while, one needs to understand why a view (or any AppObject) is, or is
-not selected appropriately. Looking at which selectors fired (or did not) is the
-way. There exists a traced_selection context manager to help with that, *if
-you're running your instance in debug mode*.
-
-Here is an example:
-
-.. sourcecode:: python
-
-     from cubicweb.selectors import traced_selection
-     with traced_selection():
-         mycomp = self._cw.vreg['views'].select('wfhistory', self._cw, rset=rset)
-
-Don't forget the 'from __future__ import with_statement' at the module
-top-level if you're using python 2.5.
-
-This will yield additional WARNINGs in the logs, like this::
-
-    2009-01-09 16:43:52 - (cubicweb.selectors) WARNING: selector one_line_rset returned 0 for <class 'cubicweb.web.views.basecomponents.WFHistoryVComponent'>
-
-You can also give to traced_selection the registry ids of objects on which to debug
-you want to debug selection ('wfhistory' in the example above).
-
-
-
-.. |cubicweb| replace:: *CubicWeb*
--- a/selectors.py	Tue Apr 06 10:10:47 2010 +0200
+++ b/selectors.py	Tue Apr 06 10:11:40 2010 +0200
@@ -1,42 +1,184 @@
-"""This file contains some basic selectors required by application objects.
+# :organization: Logilab
+# :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+# :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+# :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+""".. _Selectors:
+
+Selectors
+---------
+
+Using and combining existant selectors
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can combine selectors using the `&`, `|` and `~` operators.
+
+When two selectors are combined using the `&` operator (formerly `chainall`), it
+means that both should return a positive score. On success, the sum of scores is returned.
+
+When two selectors are combined using the `|` operator (former `chainfirst`), it
+means that one of them should return a positive score. On success, the first
+positive score is returned.
+
+You can also "negate" a selector by precedeing it by the `~` unary operator.
+
+Of course you can use parens to balance expressions.
+
+.. Note:
+  When one chains selectors, the final score is the sum of the score of each
+  individual selector (unless one of them returns 0, in which case the object is
+  non selectable)
+
+
+Example
+~~~~~~~
+
+The goal: when on a Blog, one wants the RSS link to refer to blog entries, not to
+the blog entity itself.
+
+To do that, one defines a method on entity classes that returns the RSS stream
+url for a given entity. The default implementation on
+:class:`~cubicweb.entities.AnyEntity` (the generic entity class used as base for
+all others) and a specific implementation on Blog will do what we want.
 
-A selector is responsible to score how well an object may be used with a
-given context by returning a score.
+But when we have a result set containing several Blog entities (or different
+entities), we don't know on which entity to call the aforementioned method. In
+this case, we keep the generic behaviour.
+
+Hence we have two cases here, one for a single-entity rsets, the other for
+multi-entities rsets.
+
+In web/views/boxes.py lies the RSSIconBox class. Look at its selector:
+
+.. sourcecode:: python
+
+  class RSSIconBox(ExtResourcesBoxTemplate):
+    '''just display the RSS icon on uniform result set'''
+    __select__ = ExtResourcesBoxTemplate.__select__ & non_final_entity()
+
+It takes into account:
+
+* the inherited selection criteria (one has to look them up in the class
+  hierarchy to know the details)
+
+* :class:`~cubicweb.selectors.non_final_entity`, which filters on result sets
+  containing non final entities (a 'final entity' being synonym for entity
+  attributes type, eg `String`, `Int`, etc)
 
-In CubicWeb Usually the context consists for a request object, a result set
-or None, a specific row/col in the result set, etc...
+This matches our second case. Hence we have to provide a specific component for
+the first case:
+
+.. sourcecode:: python
+
+  class EntityRSSIconBox(RSSIconBox):
+    '''just display the RSS icon on uniform result set for a single entity'''
+    __select__ = RSSIconBox.__select__ & one_line_rset()
+
+Here, one adds the :class:`~cubicweb.selectors.one_line_rset` selector, which
+filters result sets of size 1. Thus, on a result set containing multiple
+entities, :class:`one_line_rset` makes the EntityRSSIconBox class non
+selectable. However for a result set with one entity, the `EntityRSSIconBox`
+class will have a higher score than `RSSIconBox`, which is what we wanted.
+
+Of course, once this is done, you have to:
+
+* fill in the call method of `EntityRSSIconBox`
+
+* provide the default implementation of the method returning the RSS stream url
+  on :class:`~cubicweb.entities.AnyEntity`
+
+* redefine this method on `Blog`.
 
 
-If you have trouble with selectors, especially if the objet (typically
-a view or a component) you want to use is not selected and you want to
-know which one(s) of its selectors fail (e.g. returns 0), you can use
-`traced_selection` or even direclty `TRACED_OIDS`.
+When to use selectors?
+~~~~~~~~~~~~~~~~~~~~~~
+
+Selectors are to be used whenever arises the need of dispatching on the shape or
+content of a result set or whatever else context (value in request form params,
+authenticated user groups, etc...). That is, almost all the time.
+
+Here is a quick example:
+
+.. sourcecode:: python
+
+    class UserLink(component.Component):
+	'''if the user is the anonymous user, build a link to login else a link
+	to the connected user object with a loggout link
+	'''
+	__regid__ = 'loggeduserlink'
 
-`TRACED_OIDS` is a tuple of traced object ids. The special value
-'all' may be used to log selectors for all objects.
+	def call(self):
+	    if self._cw.cnx.anonymous_connection:
+		# display login link
+		...
+	    else:
+		# display a link to the connected user object with a loggout link
+		...
+
+The proper way to implement this with |cubicweb| is two have two different
+classes sharing the same identifier but with different selectors so you'll get
+the correct one according to the context:
+
 
-For instance, say that the following code yields a `NoSelectableObject`
-exception::
+    class UserLink(component.Component):
+	'''display a link to the connected user object with a loggout link'''
+	__regid__ = 'loggeduserlink'
+	__select__ = component.Component.__select__ & authenticated_user()
 
-    self.view('calendar', myrset)
+	def call(self):
+            # display useractions and siteactions
+	    ...
+
+    class AnonUserLink(component.Component):
+	'''build a link to login'''
+	__regid__ = 'loggeduserlink'
+	__select__ = component.Component.__select__ & anonymous_user()
 
-You can log the selectors involved for *calendar* by replacing the line
-above by::
+	def call(self):
+	    # display login link
+            ...
+
+The big advantage, aside readibily once you're familiar with the system, is that
+your cube becomes much more easily customizable by improving componentization.
+
+
+.. _CustomSelectors:
 
-    from cubicweb.selectors import traced_selection
-    with traced_selection():
-        self.view('calendar', myrset)
+Defining your own selectors
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autodocstring:: cubicweb.appobject::objectify_selector
 
-With python 2.5, think to add:
+In other case, you can take a look at the following abstract base classes:
+
+.. autoclass:: cubicweb.selectors.ExpectedValueSelector
+.. autoclass:: cubicweb.selectors.EClassSelector
+.. autoclass:: cubicweb.selectors.EntitySelector
 
-    from __future__ import with_statement
+Also, think to use the :func:`lltrace` decorator on your selector class' :meth:`__call__` method
+or below the :func:`objectify_selector` decorator of your selector function so it gets
+traceable when :class:`traced_selection` is activated (see :ref:`DebuggingSelectors`).
+
+.. autofunction:: cubicweb.selectors.lltrace
 
-at the top of your module.
+.. Note::
+  Selectors __call__ should *always* return a positive integer, and shall never
+  return `None`.
+
+
+.. _DebuggingSelectors:
 
-:organization: Logilab
-:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+Debugging selection
+~~~~~~~~~~~~~~~~~~~
+
+Once in a while, one needs to understand why a view (or any application object)
+is, or is not selected appropriately. Looking at which selectors fired (or did
+not) is the way. The :class:`cubicweb.selectors.traced_selection` context
+manager to help with that, *if you're running your instance in debug mode*.
+
+.. autoclass:: cubicweb.selectors.traced_selection
+
+
+.. |cubicweb| replace:: *CubicWeb*
 """
 __docformat__ = "restructuredtext en"
 
@@ -90,19 +232,31 @@
     return traced
 
 class traced_selection(object):
-    """selector debugging helper.
-
+    """
     Typical usage is :
 
-    >>> with traced_selection():
-    ...     # some code in which you want to debug selectors
-    ...     # for all objects
+    .. sourcecode:: python
+
+        >>> from cubicweb.selectors import traced_selection
+        >>> with traced_selection():
+        ...     # some code in which you want to debug selectors
+        ...     # for all objects
+
+    Don't forget the 'from __future__ import with_statement' at the module top-level
+    if you're using python prior to 2.6.
 
-    or
+    This will yield lines like this in the logs::
+
+        selector one_line_rset returned 0 for <class 'cubicweb.web.views.basecomponents.WFHistoryVComponent'>
 
-    >>> with traced_selection( ('oid1', 'oid2') ):
-    ...     # some code in which you want to debug selectors
-    ...     # for objects with id 'oid1' and 'oid2'
+    You can also give to :class:`traced_selection` the identifiers of objects on
+    which you want to debug selection ('oid1' and 'oid2' in the example above).
+
+    .. sourcecode:: python
+
+        >>> with traced_selection( ('oid1', 'oid2') ):
+        ...     # some code in which you want to debug selectors
+        ...     # for objects with id 'oid1' and 'oid2'
 
     """
     def __init__(self, traced='all'):
@@ -264,12 +418,12 @@
       - `accept_none` is False and some cell in the column has a None value
         (this may occurs with outer join)
 
-    .. note::
-       using EntitySelector or EClassSelector as base selector class impacts
-       performance, since when no entity or row is specified the later works on
-       every different *entity class* found in the result set, while the former
-       works on each *entity* (eg each row of the result set), which may be much
-       more costly.
+    .. Note::
+       using :class:`EntitySelector` or :class:`EClassSelector` as base selector
+       class impacts performance, since when no entity or row is specified the
+       later works on every different *entity class* found in the result set,
+       while the former works on each *entity* (eg each row of the result set),
+       which may be much more costly.
     """
 
     @lltrace
@@ -310,8 +464,12 @@
 
 
 class ExpectedValueSelector(Selector):
-    """Take a list of expected values as initializer argument, check
-    _get_value method return one of these expected values.
+    """Take a list of expected values as initializer argument and store them
+    into the :attr:`expected` set attribute.
+
+    You should implements the :meth:`_get_value(cls, req, **kwargs)` method
+    which should return the value for the given context. The selector will then
+    return 1 if the value is expected, else 0.
     """
     def __init__(self, *expected):
         assert expected, self
@@ -349,10 +507,12 @@
 
 
 class appobject_selectable(Selector):
-    """return 1 if another appobject is selectable using the same input context.
+    """Return 1 if another appobject is selectable using the same input context.
 
     Initializer arguments:
+
     * `registry`, a registry name
+
     * `regid`, an object identifier in this registry
     """
     def __init__(self, registry, regid):