doc/book/en/development/devcore/vreg.rst
changeset 5159 2543cfa5d54a
parent 5158 5e9055b8c10a
parent 5154 834269261ae4
child 5160 27d4cab5db03
equal deleted inserted replaced
5158:5e9055b8c10a 5159:2543cfa5d54a
     1 The VRegistry
       
     2 --------------
       
     3 
       
     4 The recording process on startup
       
     5 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
     6 
       
     7 Details of the recording process
       
     8 ````````````````````````````````
       
     9 
       
    10 .. index::
       
    11    vregistry: registration_callback
       
    12 
       
    13 On startup, |cubicweb| have to fill the vregistry with appobjects defined
       
    14 in its library and in cubes used by the instance. Appobjects from the library
       
    15 are loaded first, then appobjects provided by cubes are loaded in an ordered
       
    16 way (e.g. if your cube depends on an other, appobjects from the dependancy will
       
    17 be loaded first). Cube's modules or packages where appobject are looked at is explained
       
    18 in :ref:`cubelayout`.
       
    19 
       
    20 For each module:
       
    21 
       
    22 * by default all objects are registered automatically
       
    23 
       
    24 * if some objects have to replace other objects or be included only if a
       
    25   condition is true, you'll have to define a `registration_callback(vreg)`
       
    26   function in your module and explicitly register *all objects* in this
       
    27   module, using the vregistry api defined below.
       
    28 
       
    29 .. note::
       
    30     Once the function `registration_callback(vreg)` is implemented, all the objects
       
    31     have to be explicitly registered as it disables the automatic object registering.
       
    32 
       
    33 
       
    34 API d'enregistrement des objets
       
    35 ```````````````````````````````
       
    36 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_all
       
    37 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_and_replace
       
    38 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register
       
    39 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_if_interface_found
       
    40 .. automethod:: cubicweb.cwvreg.CubicWebVRegistry.unregister
       
    41 
       
    42 
       
    43 Examples
       
    44 ````````
       
    45 .. sourcecode:: python
       
    46 
       
    47    # web/views/basecomponents.py
       
    48    def registration_callback(vreg):
       
    49       # register everything in the module except SeeAlsoComponent
       
    50       vreg.register_all(globals().values(), __name__, (SeeAlsoVComponent,))
       
    51       # conditionally register SeeAlsoVComponent
       
    52       if 'see_also' in vreg.schema:
       
    53           vreg.register(SeeAlsoVComponent)
       
    54 
       
    55    # goa/appobjects/sessions.py
       
    56    def registration_callback(vreg):
       
    57       vreg.register(SessionsCleaner)
       
    58       # replace AuthenticationManager by GAEAuthenticationManager 
       
    59       vreg.register_and_replace(GAEAuthenticationManager, AuthenticationManager)
       
    60       # replace PersistentSessionManager by GAEPersistentSessionManager
       
    61       vreg.register_and_replace(GAEPersistentSessionManager, PersistentSessionManager)
       
    62 
       
    63 
       
    64 Runtime objects selection
       
    65 ~~~~~~~~~~~~~~~~~~~~~~~~~
       
    66 
       
    67 Using and combining existant selectors
       
    68 ``````````````````````````````````````
       
    69 
       
    70 The object's selector is defined by its `__select__` class attribute.
       
    71 
       
    72 When two selectors are combined using the `&` operator (formerly `chainall`), it
       
    73 means that both should return a positive score. On success, the sum of scores is returned.
       
    74 
       
    75 When two selectors are combined using the `|` operator (former `chainfirst`), it
       
    76 means that one of them should return a positive score. On success, the first
       
    77 positive score is returned.
       
    78 
       
    79 You can also "negate"  a selector by precedeing it by the `~` operator.
       
    80 
       
    81 Of course you can use paren to balance expressions.
       
    82 
       
    83 
       
    84 For instance, if you are selecting the primary (eg `__regid__ = 'primary'`) view (eg
       
    85 `__registry__ = 'view'`) for a result set containing a `Card` entity, 2 objects
       
    86 will probably be selectable:
       
    87 
       
    88 * the default primary view (`__select__ = implements('Any')`), meaning
       
    89   that the object is selectable for any kind of entity type
       
    90 
       
    91 * the specific `Card` primary view (`__select__ = implements('Card')`,
       
    92   meaning that the object is selectable for Card entities
       
    93 
       
    94 Other primary views specific to other entity types won't be selectable
       
    95 in this case. Among selectable objects, the implements selector will
       
    96 return a higher score than the second view since it's more specific,
       
    97 so it will be selected as expected.
       
    98 
       
    99 
       
   100 Example
       
   101 ````````
       
   102 
       
   103 The goal: when on a Blog, one wants the RSS link to refer to blog
       
   104 entries, not to the blog entity itself.
       
   105 
       
   106 To do that, one defines a method on entity classes that returns the
       
   107 RSS stream url for a given entity. The default implementation on
       
   108 AnyEntity and a specific implementation on Blog will do what we want.
       
   109 
       
   110 But when we have a result set containing several Blog entities (or
       
   111 different entities), we don't know on which entity to call the
       
   112 aforementioned method. In this case, we keep the current behaviour
       
   113 (e.g : call to limited_rql).
       
   114 
       
   115 Hence we have two cases here, one for a single-entity rsets, the other
       
   116 for multi-entities rsets.
       
   117 
       
   118 In web/views/boxes.py lies the RSSIconBox class. Look at its selector ::
       
   119 
       
   120   class RSSIconBox(ExtResourcesBoxTemplate):
       
   121     """just display the RSS icon on uniform result set"""
       
   122     __select__ = ExtResourcesBoxTemplate.__select__ & non_final_entity()
       
   123 
       
   124 It takes into account:
       
   125 
       
   126 * the inherited selection criteria (one has to look them up in the
       
   127   class hierarchy to know the details)
       
   128 
       
   129 * non_final_entity, which filters on rsets containing non final
       
   130   entities (a 'final entity' being synonym for entity attribute)
       
   131 
       
   132 This matches our second case. Hence we have to provide a specific
       
   133 component for the first case::
       
   134 
       
   135   class EntityRSSIconBox(RSSIconBox):
       
   136     """just display the RSS icon on uniform result set for a single entity"""
       
   137     __select__ = RSSIconBox.__select__ & one_line_rset()
       
   138 
       
   139 Here, one adds the one_line_rset selector, which filters result sets
       
   140 of size 1. When one chains selectors, the final score is the sum of
       
   141 the score of each individual selector (unless one of them returns 0,
       
   142 in which case the object is non selectable). Thus, on a multiple
       
   143 entities selector, one_line_rset makes the EntityRSSIconBox class non
       
   144 selectable. For an rset with one entity, the EntityRSSIconBox class
       
   145 will have a higher score then RSSIconBox, which is what we wanted.
       
   146 
       
   147 Of course, once this is done, you have to:
       
   148 
       
   149 * fill in the call method of EntityRSSIconBox
       
   150 
       
   151 * provide the default implementation of the method returning the RSS
       
   152   stream url on AnyEntity
       
   153 
       
   154 * redefine this method on Blog.
       
   155 
       
   156 When to use selectors?
       
   157 ``````````````````````
       
   158 
       
   159 Selectors are to be used whenever arises the need of dispatching on the shape or
       
   160 content of a result set or whatever else context (value in request form params,
       
   161 authenticated user groups, etc...). That is, almost all the time.
       
   162 
       
   163 XXX add and example of a single view w/ big "if" inside splitted into two views
       
   164 with appropriate selectors.
       
   165 
       
   166 
       
   167 .. CustomSelectors_
       
   168 
       
   169 Defining your own selectors
       
   170 ```````````````````````````
       
   171 .. autoclass:: cubicweb.appobject.Selector
       
   172    :members: __call__
       
   173 
       
   174 .. autofunction:: cubicweb.appobject.objectify_selector
       
   175 .. autofunction:: cubicweb.selectors.lltrace
       
   176 
       
   177 Selectors __call__ should *always* return a positive integer, and shall never
       
   178 return `None`.
       
   179 
       
   180 Useful abstract base classes for 'entity' selectors:
       
   181 
       
   182 .. autoclass:: cubicweb.selectors.EClassSelector
       
   183 .. autoclass:: cubicweb.selectors.EntitySelector
       
   184 
       
   185 
       
   186 Debugging
       
   187 `````````
       
   188 
       
   189 Once in a while, one needs to understand why a view (or any AppObject)
       
   190 is, or is not selected appropriately. Looking at which selectors fired
       
   191 (or did not) is the way. There exists a traced_selection context
       
   192 manager to help with that, *if you're running your instance in debug mode*.
       
   193 
       
   194 Here is an example:
       
   195 
       
   196 .. sourcecode:: python
       
   197 
       
   198      from cubicweb.selectors import traced_selection
       
   199      with traced_selection():
       
   200          mycomp = self._cw.vreg['views'].select('wfhistory', self._cw, rset=rset)
       
   201 
       
   202 Don't forget the 'from __future__ import with_statement' at the module
       
   203 top-level if you're using python 2.5.
       
   204 
       
   205 This will yield additional WARNINGs in the logs, like this::
       
   206 
       
   207     2009-01-09 16:43:52 - (cubicweb.selectors) WARNING: selector one_line_rset returned 0 for <class 'cubicweb.web.views.basecomponents.WFHistoryVComponent'>
       
   208 
       
   209 You can also give to traced_selection the registry ids of objects on which to debug
       
   210 you want to debug selection ('wfhistory' in the example above).
       
   211 
       
   212 Also, if you're using python 2.4, which as no 'with' yet, you'll have to to it
       
   213 the following way:
       
   214 
       
   215 .. sourcecode:: python
       
   216 
       
   217          from cubicweb import selectors
       
   218          selectors.TRACED_OIDS = ('wfhistory',)
       
   219          mycomp = self._cw.vreg['views'].select('wfhistory', self._cw, rset=rset)
       
   220          selectors.TRACED_OIDS = ()