doc/book/en/B4030-registry.en.txt
changeset 291 87c8d96f6173
parent 127 ae611743f5c6
child 295 5da6a12e88fe
equal deleted inserted replaced
290:f1d56beb3dd7 291:87c8d96f6173
       
     1 .. -*- coding: utf-8 -*-
       
     2 
       
     3 The Registry
       
     4 ------------
       
     5 
       
     6 [WRITE ME]
       
     7 
       
     8 * talk about the vreg singleton, appobjects, registration and selection
       
     9 
       
    10 
       
    11 Details of the recording process
       
    12 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
    13 
       
    14 At startup, the `registry` or registers base, inspects a number of directories
       
    15 looking for compatible classes definition. After a recording process, the objects
       
    16 are assigned to registers so that they can be selected dynamically while the
       
    17 application is running.
       
    18 
       
    19 The base class of those objects is `AppRsetObject` (module `cubicweb.common.appobject`).
       
    20 
       
    21 XXX registers example
       
    22 XXX actual details of the recording process!
       
    23 
       
    24 Runtime objects selection
       
    25 ~~~~~~~~~~~~~~~~~~~~~~~~~
       
    26 
       
    27 XXX tell why it's a cw foundation!
       
    28 
       
    29 Application objects are stored in the registry using a two level hierarchy :
       
    30 
       
    31   object's `__registry__` : object's `id` : [list of app objects]
       
    32 
       
    33 The following rules are applied to select an object given a register and an id and an input context:
       
    34 * each object has a selector
       
    35   - its selector may be derivated from a set of basic (or not :)
       
    36     selectors using `chainall` or `chainfirst` combinators
       
    37 * a selector return a score >= 0
       
    38 * a score of 0 means the objects can't be applied to the input context
       
    39 * the object with the greatest score is selected. If multiple objects have an
       
    40   identical score, one of them is selected randomly (this is usually a bug)
       
    41 
       
    42 The object's selector is the `__select__` class method on the object's class.
       
    43 
       
    44 The score is used to choose the most pertinent objects where there are more than
       
    45 one selectable object. For instance, if you're selecting the primary
       
    46 (eg `id = 'primary'`) view (eg `__registry__ = 'view'`) for a result set containing
       
    47 a `Card` entity, 2 objects will probably be selectable:
       
    48 
       
    49 * the default primary view (`accepts = 'Any'`)
       
    50 * the specific `Card` primary view (`accepts = 'Card'`)
       
    51 
       
    52 This is because primary views are using the `accept_selector` which is considering the
       
    53 `accepts` class attribute of the object's class. Other primary views specific to other
       
    54 entity types won't be selectable in this case. And among selectable objects, the
       
    55 accept selector will return a higher score the the second view since it's more
       
    56 specific, so it will be selected as expected.
       
    57 
       
    58 Usually, you won't define it directly but by defining the `__selectors__` tuple
       
    59 on the class, with ::
       
    60 
       
    61   __selectors__ = (sel1, sel2)
       
    62 
       
    63 which is equivalent to ::
       
    64 
       
    65   __select__ = classmethod(chainall(sel1, sel2))
       
    66 
       
    67 The former is prefered since it's shorter and it's ease overriding in
       
    68 subclasses (you have access to sub-selectors instead of the wrapping function).
       
    69 
       
    70 :chainall(selectors...): if one selector return 0, return 0, else return the sum of scores
       
    71 
       
    72 :chainfirst(selectors...): return the score of the first selector which has a non zero score
       
    73 
       
    74 XXX describe standard selector (link to generated api doc!)
       
    75 
       
    76 Example
       
    77 ````````
       
    78 
       
    79 Le but final : quand on est sur un Blog, on veut que le lien rss de celui-ci pointe
       
    80 vers les entrées de ce blog, non vers l'entité blog elle-même.
       
    81 
       
    82 L'idée générale pour résoudre ça : on définit une méthode sur les classes d'entité
       
    83 qui renvoie l'url du flux rss pour l'entité en question. Avec une implémentation
       
    84 par défaut sur AnyEntity et une implémentation particulière sur Blog qui fera ce
       
    85 qu'on veut.
       
    86 
       
    87 La limitation : on est embêté dans le cas ou par ex. on a un result set qui contient
       
    88 plusieurs entités Blog (ou autre chose), car on ne sait pas sur quelle entité appeler
       
    89 la méthode sus-citée. Dans ce cas, on va conserver le comportement actuel (eg appel
       
    90 à limited_rql)
       
    91 
       
    92 Donc : on veut deux cas ici, l'un pour un rset qui contient une et une seule entité,
       
    93 l'autre pour un rset qui contient plusieurs entité.
       
    94 
       
    95 Donc... On a déja dans web/views/boxes.py la classe RSSIconBox qui fonctionne. Son
       
    96 sélecteur ::
       
    97 
       
    98   class RSSIconBox(ExtResourcesBoxTemplate):
       
    99     """just display the RSS icon on uniform result set"""
       
   100     __selectors__ = ExtResourcesBoxTemplate.__selectors__ + (nfentity_selector,)
       
   101 
       
   102 
       
   103 indique qu'il prend en compte :
       
   104 
       
   105 * les conditions d'apparition de la boite (faut remonter dans les classes parentes
       
   106   pour voir le détail)
       
   107 * nfentity_selector, qui filtre sur des rset contenant une liste d'entité non finale
       
   108 
       
   109 ça correspond donc à notre 2eme cas. Reste à fournir un composant plus spécifique
       
   110 pour le 1er cas ::
       
   111 
       
   112   class EntityRSSIconBox(RSSIconBox):
       
   113     """just display the RSS icon on uniform result set for a single entity"""
       
   114     __selectors__ = RSSIconBox.__selectors__ + (onelinerset_selector,)
       
   115 
       
   116 
       
   117 Ici, on ajoute onelinerset_selector, qui filtre sur des rset de taille 1. Il faut
       
   118 savoir que quand on chaine des selecteurs, le score final est la somme des scores
       
   119 renvoyés par chaque sélecteur (sauf si l'un renvoie zéro, auquel cas l'objet est
       
   120 non sélectionnable). Donc ici, sur un rset avec plusieurs entités, onelinerset_selector
       
   121 rendra la classe EntityRSSIconBox non sélectionnable, et on obtiendra bien la
       
   122 classe RSSIconBox. Pour un rset avec une entité, la classe EntityRSSIconBox aura un
       
   123 score supérieur à RSSIconBox et c'est donc bien elle qui sera sélectionnée.
       
   124 
       
   125 Voili voilou, il reste donc pour finir tout ça :
       
   126 
       
   127 * à définir le contenu de la méthode call de EntityRSSIconBox
       
   128 * fournir l'implémentation par défaut de la méthode renvoyant l'url du flux rss sur
       
   129   AnyEntity
       
   130 * surcharger cette methode dans blog.Blog
       
   131 
       
   132 
       
   133 When to use selectors?
       
   134 ```````````````````````
       
   135 
       
   136 Il faut utiliser les sélecteurs pour faire des choses différentes en
       
   137 fonction de ce qu'on a en entrée. Dès qu'on a un "if" qui teste la
       
   138 nature de `self.rset` dans un objet, il faut très sérieusement se
       
   139 poser la question s'il ne vaut pas mieux avoir deux objets différent
       
   140 avec des sélecteurs approprié.
       
   141 
       
   142 If this is so fundamental, why don't I see them more often?
       
   143 ```````````````````````````````````````````````````````````
       
   144 
       
   145 Because you're usually using base classes which are hiding the plumbing
       
   146 of __registry__ (almost always), id (often when using "standard" object),
       
   147 register and selector.