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