--- a/cwconfig.py Fri Apr 02 16:10:17 2010 +0200
+++ b/cwconfig.py Fri Apr 02 16:10:35 2010 +0200
@@ -119,9 +119,6 @@
.. envvar:: CW_RUNTIME_DIR
Directory where pid files will be written
-
-
-.. |cubicweb| replace:: *CubicWeb*
"""
__docformat__ = "restructuredtext en"
_ = unicode
--- a/cwvreg.py Fri Apr 02 16:10:17 2010 +0200
+++ b/cwvreg.py Fri Apr 02 16:10:35 2010 +0200
@@ -347,8 +347,11 @@
obj.schema = schema
def register_if_interface_found(self, obj, ifaces, **kwargs):
- """register an object but remove it if no entity class implements one of
- the given interfaces at the end of the registration process
+ """register `obj` but remove it if no entity class implements one of
+ the given `ifaces` interfaces at the end of the registration process.
+
+ Extra keyword arguments are given to the
+ :meth:`~cubicweb.cwvreg.CubicWebVRegistry.register` function.
"""
self.register(obj, **kwargs)
if not isinstance(ifaces, (tuple, list)):
@@ -357,6 +360,13 @@
self._needs_iface[obj] = ifaces
def register(self, obj, *args, **kwargs):
+ """register `obj` application object into `registryname` or
+ `obj.__registry__` if not specified, with identifier `oid` or
+ `obj.__regid__` if not specified.
+
+ If `clear` is true, all objects with the same identifier will be
+ previously unregistered.
+ """
super(CubicWebVRegistry, self).register(obj, *args, **kwargs)
# XXX bw compat
ifaces = use_interfaces(obj)
--- a/doc/book/en/development/devcore/appobject.rst Fri Apr 02 16:10:17 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-
-
-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
-
-:URL handling:
- * `build_url(*args, **kwargs)`, returns an absolute URL based on the
- given arguments. The *controller* supposed to handle the response,
- can be specified through the first positional parameter (the
- connection is theoretically done automatically :).
-
-:Data manipulation:
-
- * `entity(row, col=0)`, returns the entity corresponding to the data position
- in the *result set* associated to the object
-
- * `complete_entity(row, col=0, skip_bytes=True)`, is equivalent to `entity` but
- also call the method `complete()` on the entity before returning it
-
-:Data formatting:
- * `format_date(date, date_format=None, time=False)` returns a string for a
- date time according to instance's configuration
- * `format_time(time)` returns a string for a date time according to
- instance's configuration
-
-:And more...:
-
- * `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/devcore/cwconfig.rst Fri Apr 02 16:10:17 2010 +0200
+++ b/doc/book/en/development/devcore/cwconfig.rst Fri Apr 02 16:10:35 2010 +0200
@@ -1,5 +1,5 @@
:mod:`Configuration <cubicweb.cwconfig>`
----------------------------------------
-.. automodule:: cubicweb.cwconfig
- :members:
+.. .. automodule:: cubicweb.cwconfig
+.. :members:
--- a/doc/book/en/development/devcore/index.rst Fri Apr 02 16:10:17 2010 +0200
+++ b/doc/book/en/development/devcore/index.rst Fri Apr 02 16:10:35 2010 +0200
@@ -4,9 +4,6 @@
.. toctree::
:maxdepth: 1
- vreg.rst
- appobject.rst
- selectors.rst
dbapi.rst
cwconfig.rst
--- a/doc/book/en/development/devcore/selectors.rst Fri Apr 02 16:10:17 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/devcore/vreg.rst Fri Apr 02 16:10:17 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,220 +0,0 @@
-The VRegistry
---------------
-
-The recording process on startup
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Details of the recording process
-````````````````````````````````
-
-.. index::
- vregistry: registration_callback
-
-On startup, |cubicweb| have to fill the vregistry with appobjects defined
-in its library and in cubes used by the instance. Appobjects from the library
-are loaded first, then appobjects provided by cubes are loaded in an ordered
-way (e.g. if your cube depends on an other, appobjects 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 a
- 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 vregistry api defined below.
-
-.. note::
- Once the function `registration_callback(vreg)` is implemented, all the objects
- have to be explicitly registered as it disables the automatic object registering.
-
-
-API d'enregistrement des objets
-```````````````````````````````
-.. 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)
-
- # 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)
-
-
-Runtime objects selection
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Using and combining existant selectors
-``````````````````````````````````````
-
-The object's selector is defined by its `__select__` class attribute.
-
-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 `~` operator.
-
-Of course you can use paren to balance expressions.
-
-
-For instance, if you are selecting the primary (eg `__regid__ = 'primary'`) view (eg
-`__registry__ = 'view'`) 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.
-
-
-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
-AnyEntity 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 current behaviour
-(e.g : call to limited_rql).
-
-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)
-
-* non_final_entity, which filters on rsets containing non final
- entities (a 'final entity' being synonym for entity attribute)
-
-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 one_line_rset selector, which filters result sets
-of size 1. 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). Thus, on a multiple
-entities selector, one_line_rset makes the EntityRSSIconBox class non
-selectable. For an rset with one entity, the EntityRSSIconBox class
-will have a higher score then 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 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
-.. autofunction:: cubicweb.selectors.lltrace
-
-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
-
-
-Debugging
-`````````
-
-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).
-
-Also, if you're using python 2.4, which as no 'with' yet, you'll have to to it
-the following way:
-
-.. sourcecode:: python
-
- from cubicweb import selectors
- selectors.TRACED_OIDS = ('wfhistory',)
- mycomp = self._cw.vreg['views'].select('wfhistory', self._cw, rset=rset)
- selectors.TRACED_OIDS = ()
--- a/doc/book/en/development/index.rst Fri Apr 02 16:10:17 2010 +0200
+++ b/doc/book/en/development/index.rst Fri Apr 02 16:10:35 2010 +0200
@@ -11,6 +11,7 @@
:numbered:
cubes/index
+ vreg/index
datamodel/index
entityclasses/index
devcore/index
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/development/vreg/appobject.rst Fri Apr 02 16:10:35 2010 +0200
@@ -0,0 +1,71 @@
+
+
+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
+
+:URL handling:
+ * `build_url(*args, **kwargs)`, returns an absolute URL based on the
+ given arguments. The *controller* supposed to handle the response,
+ can be specified through the first positional parameter (the
+ connection is theoretically done automatically :).
+
+:Data manipulation:
+
+ * `entity(row, col=0)`, returns the entity corresponding to the data position
+ in the *result set* associated to the object
+
+ * `complete_entity(row, col=0, skip_bytes=True)`, is equivalent to `entity` but
+ also call the method `complete()` on the entity before returning it
+
+:Data formatting:
+ * `format_date(date, date_format=None, time=False)` returns a string for a
+ date time according to instance's configuration
+ * `format_time(time)` returns a string for a date time according to
+ instance's configuration
+
+:And more...:
+
+ * `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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/development/vreg/selectors.rst Fri Apr 02 16:10:35 2010 +0200
@@ -0,0 +1,89 @@
+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`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/development/vreg/vreg.rst Fri Apr 02 16:10:35 2010 +0200
@@ -0,0 +1,323 @@
+.. 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/doc/book/en/intro/concepts/index.rst Fri Apr 02 16:10:17 2010 +0200
+++ b/doc/book/en/intro/concepts/index.rst Fri Apr 02 16:10:35 2010 +0200
@@ -131,8 +131,7 @@
An `entity type` defines a set of attributes and is used in some relations.
Attributes may be of the following types: `String`, `Int`, `Float`, `Boolean`,
-`Date`, `Time`, `Datetime`, `Interval`, `Password`, `Bytes`, `RichString`. See
-:ref:`yams.BASE_TYPES` for details.
+`Date`, `Time`, `Datetime`, `Interval`, `Password`, `Bytes`, `RichString`.
A `relation type` is used to define an oriented binary relation between two
entity types. The left-hand part of a relation is named the `subject` and the
@@ -159,8 +158,8 @@
.. _VRegistryIntro:
-Registries and Objects
-----------------------
+Registries and application objects
+----------------------------------
Application objects
~~~~~~~~~~~~~~~~~~~
@@ -193,7 +192,8 @@
|cubicweb| provides a set of basic selectors that may be parametrized. Also,
selectors can be combined with the `~` unary operator (negation) and the binary
operators `&` and `|` (respectivly 'and' and 'or') to build more complex
-selector. Of course complex selector may be combined too.
+selector. Of course complex selector may be combined too. Last but not least, you
+can write your own selectors.
The `vregistry`
~~~~~~~~~~~~~~~
@@ -203,7 +203,8 @@
assigned to registries so that they can be selected dynamically while the
instance is running.
-In a cube, appobject classes are looked in the following modules or packages:
+In a cube, application object classes are looked in the following modules or
+packages:
- `entities`
- `views`
@@ -211,43 +212,21 @@
- `hooks`
-Once initialized, there are three common ways to retrieve some appobject from a
-registry:
+Once initialized, there are three common ways to retrieve some application object
+from a registry:
* get the most appropriate object by specifying an identifier. In that case, the
object with the greatest score is selected. There should always be a single
- appobject with a greater score than others for a particular context (see note
- below).
+ appobject with a greater score than others for a particular context.
- - If no object has a positive score, :class:`cubicweb.NoSelectableObject`
- exception is raised.
-
-* get all appobjects applying to a context by specifying a registry. In that
- case, a list of objects will be returned containing the object with the
- highest score (> 0) for each identifier in that registry.
+* get all objects applying to a context by specifying a registry. In that case, a
+ list of objects will be returned containing the object with the highest score
+ (> 0) for each identifier in that registry.
* get the object within a particular registry/identifier. In that case no
selection process is involved, the vregistry will expect to find a single
object in that cell.
-In all cases:
-
-- If no object is found for the identifier, :class:`cubicweb.ObjectNotFound`
- exception is raised.
-
-- If you ask the `vregistry` for an unexistant registry,
- :class:`cubicweb.RegistryNotFound` exception is raised.
-
-.. 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.
-
.. _RQLIntro:
--- a/selectors.py Fri Apr 02 16:10:17 2010 +0200
+++ b/selectors.py Fri Apr 02 16:10:35 2010 +0200
@@ -74,6 +74,9 @@
print '%s -> %s for %s(%s)' % (selname, ret, vobj, vobj.__regid__)
def lltrace(selector):
+ """use this decorator on your selectors so the becomes traceable with
+ :class:`traced_selection`
+ """
# don't wrap selectors if not in development mode
if CubicWebConfiguration.mode == 'system': # XXX config.debug
return selector
--- a/vregistry.py Fri Apr 02 16:10:17 2010 +0200
+++ b/vregistry.py Fri Apr 02 16:10:35 2010 +0200
@@ -161,11 +161,12 @@
# dynamic selection methods ################################################
def object_by_id(self, oid, *args, **kwargs):
- """return object with the given oid. Only one object is expected to be
- found.
+ """return object with the `oid` identifier. Only one object is expected
+ to be found.
- raise `ObjectNotFound` if not object with id <oid> in <registry>
- raise `AssertionError` if there is more than one object there
+ raise :exc:`ObjectNotFound` if not object with id <oid> in <registry>
+
+ raise :exc:`AssertionError` if there is more than one object there
"""
objects = self[oid]
assert len(objects) == 1, objects
@@ -175,8 +176,9 @@
"""return the most specific object among those with the given oid
according to the given context.
- raise `ObjectNotFound` if not object with id <oid> in <registry>
- raise `NoSelectableObject` if not object apply
+ raise :exc:`ObjectNotFound` if not object with id <oid> in <registry>
+
+ raise :exc:`NoSelectableObject` if not object apply
"""
return self._select_best(self[oid], *args, **kwargs)
@@ -318,6 +320,20 @@
# self[regname].pop(oid, None)
def register_all(self, objects, modname, butclasses=()):
+ """register all `objects` given. Objects which are not from the module
+ `modname` or which are in `butclasses` won't be registered.
+
+ Typical usage is:
+
+ .. sourcecode:: python
+
+ vreg.register_all(globals().values(), __name__, (ClassIWantToRegisterExplicitly,))
+
+ So you get partially automatic registration, keeping manual registration
+ for some object (to use
+ :meth:`~cubicweb.cwvreg.CubicWebRegistry.register_and_replace` for
+ instance)
+ """
for obj in objects:
try:
if obj.__module__ != modname or obj in butclasses:
@@ -329,7 +345,13 @@
self.register(obj, oid=oid)
def register(self, obj, registryname=None, oid=None, clear=False):
- """base method to add an object in the registry"""
+ """register `obj` application object into `registryname` or
+ `obj.__registry__` if not specified, with identifier `oid` or
+ `obj.__regid__` if not specified.
+
+ If `clear` is true, all objects with the same identifier will be
+ previously unregistered.
+ """
assert not '__abstract__' in obj.__dict__
try:
vname = obj.__name__
@@ -344,10 +366,18 @@
self._loadedmods[obj.__module__][classid(obj)] = obj
def unregister(self, obj, registryname=None):
+ """unregister `obj` application object from the registry `registryname` or
+ `obj.__registry__` if not specified.
+ """
for registryname in class_registries(obj, registryname):
self[registryname].unregister(obj)
def register_and_replace(self, obj, replaced, registryname=None):
+ """register `obj` application object into `registryname` or
+ `obj.__registry__` if not specified. If found, the `replaced` object
+ will be unregistered first (else a warning will be issued as it's
+ generally unexpected).
+ """
for registryname in class_registries(obj, registryname):
self[registryname].register_and_replace(obj, replaced)