diff -r 36e48f7ac61a -r d0907690af55 predicates.py --- a/predicates.py Fri Jul 03 15:23:14 2015 +0200 +++ b/predicates.py Fri Jul 03 14:07:53 2015 +0200 @@ -15,171 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -""".. _Selectors: - -Predicates and selectors ------------------------- - -A predicate is a class testing a particular aspect of a context. A selector is -built by combining existant predicates or even selectors. - -Using and combining existant predicates -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can combine predicates using the `&`, `|` and `~` operators. - -When two predicates are combined using the `&` operator, it means that -both should return a positive score. On success, the sum of scores is -returned. - -When two predicates are combined using the `|` operator, it means that -one of them should return a positive score. On success, the first -positive score is returned. - -You can also "negate" a predicate by precedeing it by the `~` unary operator. - -Of course you can use parenthesis to balance expressions. - -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: - -.. sourcecode:: python - - class RSSIconBox(box.Box): - ''' just display the RSS icon on uniform result set ''' - __select__ = box.Box.__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.predicates.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: - -.. 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.predicates.one_line_rset` predicate, 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. - -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 logout link - ''' - __regid__ = 'loggeduserlink' - - def call(self): - if self._cw.session.anonymous_session: - # 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. - -.. sourcecode:: python - - class UserLink(component.Component): - '''display a link to the connected user object with a loggout link''' - __regid__ = 'loggeduserlink' - __select__ = component.Component.__select__ & authenticated_user() - - def call(self): - # display useractions and siteactions - ... - - class AnonUserLink(component.Component): - '''build a link to login''' - __regid__ = 'loggeduserlink' - __select__ = component.Component.__select__ & anonymous_user() - - def call(self): - # display login link - ... - -The big advantage, aside readability once you're familiar with the -system, is that your cube becomes much more easily customizable by -improving componentization. - - -.. _CustomPredicates: - -Defining your own predicates -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. autodocstring:: cubicweb.appobject::objectify_predicate - -In other cases, you can take a look at the following abstract base classes: - -.. autoclass:: cubicweb.predicates.ExpectedValuePredicate -.. autoclass:: cubicweb.predicates.EClassPredicate -.. autoclass:: cubicweb.predicates.EntityPredicate - -.. _DebuggingSelectors: - -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 predicates fired (or did -not) is the way. The :class:`logilab.common.registry.traced_selection` context -manager to help with that, *if you're running your instance in debug mode*. - -.. autoclass:: logilab.common.registry.traced_selection - +"""Predicate classes """ __docformat__ = "restructuredtext en"