doc/book/en/02-foundation.en.txt
changeset 112 52bf52e6fc77
parent 111 7a06f06de32f
parent 110 d3005cdc968f
equal deleted inserted replaced
111:7a06f06de32f 112:52bf52e6fc77
    52   classes based on `yams`_ library. This is the core piece
    52   classes based on `yams`_ library. This is the core piece
    53   of an application. It is initially defined in the file system and is
    53   of an application. It is initially defined in the file system and is
    54   stored in the database at the time an instance is created. `CubicWeb`
    54   stored in the database at the time an instance is created. `CubicWeb`
    55   provides a certain number of system entities included automatically as
    55   provides a certain number of system entities included automatically as
    56   it is necessarry for the core of `CubicWeb` and a library of
    56   it is necessarry for the core of `CubicWeb` and a library of
    57   cubes that can be explicitely included if necessarry.
    57   cubes that can be explicitely included if necessary.
    58 
    58 
    59 
    59 
    60 *entity type*
    60 *entity type*
    61   An entity is a set of attributes; the essential attribute of
    61   An entity is a set of attributes; the essential attribute of
    62   an entity is its key, named eid
    62   an entity is its key, named eid
    74 
    74 
    75 *final relation type*
    75 *final relation type*
    76   A relation is said final if its `object` is a final type. This is equivalent
    76   A relation is said final if its `object` is a final type. This is equivalent
    77   to an entity attribute.
    77   to an entity attribute.
    78 
    78 
       
    79 *relation definition*
       
    80   a relation definition is a 3-uple (subject entity type, relation type, object entity type),
       
    81   with an associated set of property such as cardinality, constraints...
       
    82   
    79 *repository*
    83 *repository*
    80   This is the RQL server side of `CubicWeb`. Be carefull not to get
    84   This is the RQL server side of `CubicWeb`. Be carefull not to get
    81   confused with a Mercurial repository or a debian repository.
    85   confused with a Mercurial repository or a debian repository.
    82 
    86 
    83 *source*
    87 *source*
   146   own. Any generated view can be overridden by defining a new one with
   150   own. Any generated view can be overridden by defining a new one with
   147   the same identifier.
   151   the same identifier.
   148 
   152 
   149   
   153   
   150 .. _`Python Remote Object`: http://pyro.sourceforge.net/
   154 .. _`Python Remote Object`: http://pyro.sourceforge.net/
   151 .. _`yams`: http://www.logilab.org/project/name/yams/
   155 .. _`yams`: http://www.logilab.org/project/yams/
   152 
   156 
   153 
   157 
   154 `CubicWeb` engine
   158 `CubicWeb` engine
   155 -----------------
   159 -----------------
   156 
   160 
   157 The web engine in `CubicWeb` is a set of classes managing a set of objects loaded
   161 The engine in `CubicWeb` is a set of classes managing a set of objects loaded
   158 dynamically at the startup of `CubicWeb`. Those dynamics objects, based on the schema
   162 dynamically at the startup of `CubicWeb` (*appobjects*). Those dynamics objects, based on the schema
   159 or the library, are building the final web site. The differents dymanic components are
   163 or the library, are building the final application. The differents dymanic components are
   160 by example:
   164 by example:
   161 
   165 
   162 * client and server side
   166 * client and server side
   163 
   167 
   164   - entities definition, containing the logic which enables application data manipulation
   168   - entities definition, containing the logic which enables application data manipulation
   182 
   186 
   183 The components of the engine are:
   187 The components of the engine are:
   184 
   188 
   185 * a frontal web (only twisted is available so far), transparent for dynamic objects
   189 * a frontal web (only twisted is available so far), transparent for dynamic objects
   186 * an object that encapsulates the configuration
   190 * an object that encapsulates the configuration
   187 * a `vregistry` (`cubicweb.cwvreg`) containing the dynamic objects loaded automatically
   191 * a `registry` (`cubicweb.cwvreg`) containing the dynamic objects loaded automatically
   188 
   192 
   189 
   193 Every *appobject* may access to the instance configuration using its *config* attribute
   190 Details of a recording process
   194 and to the registry using its *vreg* attribute.
   191 ------------------------------
   195 
   192 
   196 Details of the recording process
   193 At startup, the `vregistry` or registers base, inspects a number of directories
   197 --------------------------------
       
   198 
       
   199 At startup, the `registry` or registers base, inspects a number of directories
   194 looking for compatible classes definition. After a recording process, the objects
   200 looking for compatible classes definition. After a recording process, the objects
   195 are assigned to registers so that they can be selected dynamically while the
   201 are assigned to registers so that they can be selected dynamically while the
   196 application is running.
   202 application is running.
   197 
   203 
   198 The base class of those objects is `AppRsetObject` (module `cubicweb.common.appobject`).
   204 The base class of those objects is `AppRsetObject` (module `cubicweb.common.appobject`).
       
   205 
       
   206 XXX registers example
       
   207 XXX actual details of the recording process!
       
   208 
       
   209 Runtime objects selection
       
   210 -------------------------
       
   211 XXX tell why it's a cw foundation!
       
   212 
       
   213 Application objects are stored in the registry using a two level hierarchy :
       
   214 
       
   215   object's `__registry__` : object's `id` : [list of app objects]
       
   216 
       
   217 The following rules are applied to select an object given a register and an id and an input context:
       
   218 * each object has a selector
       
   219   - its selector may be derivated from a set of basic (or not :)
       
   220     selectors using `chainall` or `chainfirst` combinators
       
   221 * a selector return a score >= 0
       
   222 * a score of 0 means the objects can't be applied to the input context
       
   223 * the object with the greatest score is selected. If multiple objects have an
       
   224   identical score, one of them is selected randomly (this is usually a bug)
       
   225 
       
   226 The object's selector is the `__selector__` class method on the object's class.
       
   227 
       
   228 The score is used to choose the most pertinent objects where there are more than
       
   229 one selectable object. For instance, if you're selecting the primary
       
   230 (eg `id = 'primary'`) view (eg `__registry__ = 'view'`) for a result set containing
       
   231 a `Card` entity, 2 objects will probably be selectable:
       
   232 
       
   233 * the default primary view (`accepts = 'Any'`)
       
   234 * the specific `Card` primary view (`accepts = 'Card'`)
       
   235 
       
   236 This is because primary views are using the `accept_selector` which is considering the
       
   237 `accepts` class attribute of the object's class. Other primary views specific to other
       
   238 entity types won't be selectable in this case. And among selectable objects, the
       
   239 accept selector will return a higher score the the second view since it's more
       
   240 specific, so it will be selected as expected.
       
   241 
       
   242 Usually, you won't define it directly but by defining the `__selectors__` tuple
       
   243 on the class, with ::
       
   244 
       
   245   __selectors__ = (sel1, sel2)
       
   246 
       
   247 which is equivalent to ::
       
   248 
       
   249   __selector__ = chainall(sel1, sel2)
       
   250 
       
   251 The former is prefered since it's shorter and it's ease overriding in
       
   252 subclasses (you have access to sub-selectors instead of the wrapping function).
       
   253 
       
   254 :chainall(selectors...): if one selector return 0, return 0, else return the sum of scores
       
   255 
       
   256 :chainfirst(selectors...): return the score of the first selector which has a non zero score
       
   257 
       
   258 XXX describe standard selector (link to generated api doc!)
       
   259 
       
   260 Example
       
   261 ~~~~~~~
       
   262 Le but final : quand on est sur un Blog, on veut que le lien rss de celui-ci pointe
       
   263 vers les entrées de ce blog, non vers l'entité blog elle-même.
       
   264 
       
   265 L'idée générale pour résoudre ça : on définit une méthode sur les classes d'entité
       
   266 qui renvoie l'url du flux rss pour l'entité en question. Avec une implémentation
       
   267 par défaut sur AnyEntity et une implémentation particulière sur Blog qui fera ce
       
   268 qu'on veut.
       
   269 
       
   270 La limitation : on est embêté dans le cas ou par ex. on a un result set qui contient
       
   271 plusieurs entités Blog (ou autre chose), car on ne sait pas sur quelle entité appeler
       
   272 la méthode sus-citée. Dans ce cas, on va conserver le comportement actuel (eg appel
       
   273 à limited_rql)
       
   274 
       
   275 Donc : on veut deux cas ici, l'un pour un rset qui contient une et une seule entité,
       
   276 l'autre pour un rset qui contient plusieurs entité.
       
   277 
       
   278 Donc... On a déja dans web/views/boxes.py la classe RSSIconBox qui fonctionne. Son
       
   279 sélecteur ::
       
   280 
       
   281   class RSSIconBox(ExtResourcesBoxTemplate):
       
   282     """just display the RSS icon on uniform result set"""
       
   283     __selectors__ = ExtResourcesBoxTemplate.__selectors__ + (nfentity_selector,)
       
   284 
       
   285 
       
   286 indique qu'il prend en compte :
       
   287 
       
   288 * les conditions d'apparition de la boite (faut remonter dans les classes parentes
       
   289   pour voir le détail)
       
   290 * nfentity_selector, qui filtre sur des rset contenant une liste d'entité non finale
       
   291 
       
   292 ça correspond donc à notre 2eme cas. Reste à fournir un composant plus spécifique
       
   293 pour le 1er cas ::
       
   294 
       
   295   class EntityRSSIconBox(RSSIconBox):
       
   296     """just display the RSS icon on uniform result set for a single entity"""
       
   297     __selectors__ = RSSIconBox.__selectors__ + (onelinerset_selector,)
       
   298 
       
   299 
       
   300 Ici, on ajoute onelinerset_selector, qui filtre sur des rset de taille 1. Il faut
       
   301 savoir que quand on chaine des selecteurs, le score final est la somme des scores
       
   302 renvoyés par chaque sélecteur (sauf si l'un renvoie zéro, auquel cas l'objet est
       
   303 non sélectionnable). Donc ici, sur un rset avec plusieurs entités, onelinerset_selector
       
   304 rendra la classe EntityRSSIconBox non sélectionnable, et on obtiendra bien la
       
   305 classe RSSIconBox. Pour un rset avec une entité, la classe EntityRSSIconBox aura un
       
   306 score supérieur à RSSIconBox et c'est donc bien elle qui sera sélectionnée.
       
   307 
       
   308 Voili voilou, il reste donc pour finir tout ça :
       
   309 
       
   310 * à définir le contenu de la méthode call de EntityRSSIconBox
       
   311 * fournir l'implémentation par défaut de la méthode renvoyant l'url du flux rss sur
       
   312   AnyEntity
       
   313 * surcharger cette methode dans blog.Blog
       
   314 
       
   315 When to use selectors?
       
   316 ~~~~~~~~~~~~~~~~~~~~~~
       
   317 Il faut utiliser les sélecteurs pour faire des choses différentes en
       
   318 fonction de ce qu'on a en entrée. Dès qu'on a un "if" qui teste la
       
   319 nature de `self.rset` dans un objet, il faut très sérieusement se
       
   320 poser la question s'il ne vaut pas mieux avoir deux objets différent
       
   321 avec des sélecteurs approprié.
       
   322 
       
   323 If this is so fundamental, why don't I see them more often?
       
   324 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
   325 Because you're usually using base classes which are hiding the plumbing
       
   326 of __registry__ (almost always), id (often when using "standard" object),
       
   327 register and selector.
   199 
   328 
   200 API Python/RQL
   329 API Python/RQL
   201 --------------
   330 --------------
   202 
   331 
   203 Inspired from the standard db-api, with a Connection object having the methods
   332 Inspired from the standard db-api, with a Connection object having the methods
   433 
   562 
   434 The only required files are:
   563 The only required files are:
   435 
   564 
   436 * the file ``__pkginfo__.py``
   565 * the file ``__pkginfo__.py``
   437 * the schema definition
   566 * the schema definition
       
   567   XXX false, we may want to have cubes which are only adding a service, no persistent data (eg embeding for instance)