Merged chap_fondements_cubicweb and chap_bases_framework_cubicweb as they were both describing differents aspects of CubicWeb framework.
authorSandrine Ribeau <sandrine.ribeau@logilab.fr>
Tue, 11 Nov 2008 14:27:19 -0800
changeset 22 50f20cf0f440
parent 21 09cad2ba7bc7
child 23 af1e65cb1123
Merged chap_fondements_cubicweb and chap_bases_framework_cubicweb as they were both describing differents aspects of CubicWeb framework. Replaced original content of mise_en_place_environemment that was by mistake erased and replaced with chap_migration content.
doc/devmanual_fr/chap_bases_framework_cubicweb.txt
doc/devmanual_fr/chap_fondements_cubicweb.txt
doc/devmanual_fr/chap_mise_en_place_environnement.txt
--- a/doc/devmanual_fr/chap_bases_framework_cubicweb.txt	Tue Nov 11 14:24:58 2008 -0800
+++ b/doc/devmanual_fr/chap_bases_framework_cubicweb.txt	Tue Nov 11 14:27:19 2008 -0800
@@ -4,7 +4,7 @@
 ================================
 
 Le moteur web d'cubicweb consiste en quelques classes gérant un ensemble d'objets
-chargés dynamiquement au lancement d'cubicweb. Ce sont ces objets dynamiques, issus
+chargés dynamiquement au lancement de cubicweb. Ce sont ces objets dynamiques, issus
 du modèle ou de la librairie, qui construisent le site web final. Les différents
 composants dynamiques sont par exemple : 
 
--- a/doc/devmanual_fr/chap_fondements_cubicweb.txt	Tue Nov 11 14:24:58 2008 -0800
+++ b/doc/devmanual_fr/chap_fondements_cubicweb.txt	Tue Nov 11 14:27:19 2008 -0800
@@ -100,6 +100,233 @@
 .. _`yams`: http://www.logilab.org/project/name/yams/
 
 
+Moteur `CubicWeb`
+-----------------
+Le moteur web de cubicweb consiste en quelques classes gérant un ensemble d'objets
+chargés dynamiquement au lancement de cubicweb. Ce sont ces objets dynamiques, issus
+du modèle ou de la librairie, qui construisent le site web final. Les différents
+composants dynamiques sont par exemple : 
+
+* coté client et serveur
+
+ - les définitions d'entités, contenant la logique permettant la manipulation des
+   données de l'application
+
+* coté client
+
+  - les *vues* , ou encore plus spécifiquement 
+
+    - les boites
+    - l'en-tête et le pied de page
+    - les formulaires
+    - les gabarits de pages
+
+  - les *actions*
+  - les *controleurs*
+
+* coté serveur
+
+  - les crochets de notification
+  - les vues de notification
+
+Les différents composants du moteur sont :
+
+* un frontal web (seul twisted disponible pour le moment), transparent du point
+  de vue des objets dynamiques
+* un objet encapsulant la configuration
+* un `vregistry` (`cubicweb.cwvreg`) contenant les objets chargés dynamiquements
+
+Détail de la procédure d'enregistrement
+---------------------------------------
+Au démarage le `vregistry` ou base de registres inspecte un certain nombre de
+répertoires à la recherche de définition de classes "compatible". Après une
+procédure d'enregistrement les objets sont affectés dans différents registres
+afin d'être ensuite séléctionné dynamiquement pendant le fonctionnement de
+l'application.
+
+La classe de base de tout ces objets est la classe `AppRsetObject` (module
+`cubicweb.common.appobject`). 
+
+
+API Python/RQL
+--------------
+
+Inspiré de la db-api standard, avec un object Connection possédant les méthodes
+cursor, rollback et commit principalement. La méthode importante est la méthode
+`execute` du curseur :
+
+`execute(rqlstring, args=None, eid_key=None, build_descr=True)`
+
+:rqlstring: la requête rql à éxécuter (unicode)
+:args: si la requête contient des substitutions, un dictionnaire contenant les
+       valeurs à utiliser
+:eid_key: 
+   un détail d'implémentation du cache de requêtes RQL fait que si une substitution est
+   utilisée pour introduire un eid *levant des ambiguités dans la résolution de
+   type de la requête*, il faut spécifier par cet argument la clé correspondante
+   dans le dictionnaire
+
+C'est l'objet Connection qui possède les méthodes classiques `commit` et
+`rollback`. Vous ne *devriez jamais avoir à les utiliser* lors du développement
+d'interface web sur la base du framework CubicWeb étant donné que la fin de la
+transaction est déterminée par celui-ci en fonction du succès d'éxécution de la
+requête. 
+
+NOTE : lors de l'éxécution de requêtes de modification (SET,INSERT,DELETE), si une
+requête génère une erreur liée à la sécurité, un rollback est systématiquement
+effectuée sur la transaction courante.
+
+
+La classe `Request` (`cubicweb.web`)
+------------------------------------
+Une instance de requête est créée lorsque une requête HTTP est transmise au
+serveur web. Elle contient des informations telles que les paramètres de
+formulaires, l'utilisateur connecté, etc. 
+
+**De manière plus générale une requête représente une demande d'un utilisateur,
+que se soit par HTTP ou non (on parle également de requête rql coté serveur par
+exemple)**
+
+Une instance de la classe `Request` possède les attributs :
+
+* `user`, instance de`cubicweb.common.utils.User` correspondant à l'utilisateur
+  connecté 
+* `form`, dictionaire contenant les valeurs de formulaire web
+* `encoding`, l'encodage de caractère à utiliser dans la réponse
+
+Mais encore :
+
+:Gestion des données de session:        
+  * `session_data()`, retourne un dictionaire contenant l'intégralité des
+    données de la session
+  * `get_session_data(key, default=None)`, retourne la valeur associée à
+    la clé ou la valeur `default` si la clé n'est pas définie
+  * `set_session_data(key, value)`, associe une valeur à une clé
+  * `del_session_data(key)`,  supprime la valeur associé à une clé
+    
+
+:Gestion de cookie:
+  * `get_cookie()`, retourne un dictionnaire contenant la valeur de l'entête
+    HTTP 'Cookie'
+  * `set_cookie(cookie, key, maxage=300)`, ajoute un en-tête HTTP `Set-Cookie`,
+    avec une durée de vie 5 minutes par défault (`maxage` = None donne un cooke
+    *de session"* expirant quand l'utilisateur ferme son navigateur
+  * `remove_cookie(cookie, key)`, fait expirer une valeur
+
+:Gestion d'URL:
+  * `url()`, retourne l'url complète de la requête HTTP
+  * `base_url()`, retourne l'url de la racine de l'application
+  * `relative_path()`, retourne chemin relatif de la requête
+
+:Et encore...:
+  * `set_content_type(content_type, filename=None)`, place l'en-tête HTTP
+    'Content-Type'
+  * `get_header(header)`, retourne la valeur associé à un en-tête HTTP
+    arbitraire de la requête
+  * `set_header(header, value)`, ajoute un en-tête HTTP arbitraire dans la
+    réponse 
+  * `cursor()` retourne un curseur RQL sur la session
+  * `execute(*args, **kwargs)`, raccourci vers .cursor().execute()
+  * `property_value(key)`, gestion des propriétés (`EProperty`)
+  * le dictionaire `data` pour stocker des données pour partager de
+    l'information entre les composants *durant l'éxécution de la requête*.
+
+A noter que cette classe est en réalité abstraite et qu'une implémentation
+concrète sera fournie par le *frontend* web utilisé (en l'occurent *twisted*
+aujourd'hui). Enfin pour les vues ou autres qui sont éxécutés coté serveur,
+la majeure partie de l'interface de `Request` est définie sur la session
+associée au client. 
+
+
+La classe `AppObject`
+---------------------
+
+En général :
+
+* on n'hérite pas directement des cette classe mais plutôt d'une classe
+  plus spécifique comme par exemple `AnyEntity`, `EntityView`, `AnyRsetView`,
+  `Action`...
+
+* pour être enregistrable, un classe fille doit définir son registre (attribut
+  `__registry__`) et son identifiant (attribut `id`). Généralement on n'a pas à
+  s'occuper du registre, uniquement de l'identifiant `id` :) 
+
+On trouve un certain nombre d'attributs et de méthodes définis dans cette classe
+et donc commune à tous les objets de l'application :
+
+A l'enregistrement, les attributs suivants sont ajoutés dynamiquement aux
+*classes* filles:
+
+* `vreg`, le `vregistry` de l'application
+* `schema`, le schéma de l'application
+* `config`, la configuration de l'application
+
+On trouve également sur les instances les attributs :
+
+* `req`, instance de `Request`
+* `rset`, le "result set" associé à l'objet le cas échéant
+* `cursor`, curseur rql sur la session
+
+
+:Gestion d'URL:
+  * `build_url(method=None, **kwargs)`, retourne une URL absolue construites à
+    partir des arguments donnés. Le *controleur* devant gérer la réponse
+    peut-être spécifié via l'argument spécial `method` (le branchement est
+    théoriquement bien effectué automatiquement :).
+
+  * `datadir_url()`, retourne l'url du répertoire de données de l'application
+    (contenant les fichiers statiques tels que les images, css, js...)
+
+  * `base_url()`, raccourci sur `req.base_url()`
+
+  * `url_quote(value)`, version *unicode safe* de de la fonction `urllib.quote`
+
+:Manipulation de données:
+
+  * `etype_rset(etype, size=1)`, raccourci vers `vreg.etype_rset()`
+
+  * `eid_rset(eid, rql=None, descr=True)`, retourne un objet result set pour
+    l'eid donné
+  * `entity(row, col=0)`, retourne l'entité correspondant à la position données
+    du "result set" associé à l'objet
+
+  * `complete_entity(row, col=0, skip_bytes=True)`, équivalent à `entity` mais
+    appelle également la méthode `complete()` sur l'entité avant de la retourner
+
+:Formattage de données:
+  * `format_date(date, date_format=None, time=False)`
+  * `format_time(time)`,
+
+:Et encore...:
+
+  * `external_resource(rid, default=_MARKER)`, accède à une valeur définie dans
+    le fichier de configuration `external_resource`
+    
+  * `tal_render(template, variables)`, 
+
+
+**NOTE IMPORTANTE**
+Lorsqu'on hérite d'`AppObject` (même indirectement), il faut **toujours**
+utiliser **super()** pour récupérer les méthodes et attributs des classes
+parentes, et pas passer par l'identifiant de classe parente directement.
+(sous peine de tomber sur des bugs bizarres lors du rechargement automatique
+des vues). Par exemple, plutôt que d'écrire::
+
+      class Truc(PrimaryView):
+          def f(self, arg1):
+              PrimaryView.f(self, arg1)
+
+Il faut écrire::
+      
+      class Truc(PrimaryView):
+          def f(self, arg1):
+              super(Truc, self).f(arg1)
+
+
+
+
+
+
 Structure standard d'un cube
 ----------------------------
 
--- a/doc/devmanual_fr/chap_mise_en_place_environnement.txt	Tue Nov 11 14:24:58 2008 -0800
+++ b/doc/devmanual_fr/chap_mise_en_place_environnement.txt	Tue Nov 11 14:27:19 2008 -0800
@@ -1,218 +1,96 @@
-.. -*- coding: utf-8 -*-
-
-
-Migration
-=========
-
-Une des idées de base d'Erudi est la création incrémentale d'application, et
-pour cela de nombreuses actions sont fournies afin de facilement faire évoluer
-une application et tout particulièrement le modèle de données manipulé sans
-perdre les données des instances existantes.
-
-La version courante d'un modèle d'application est données dans le fichier
-`__pkginfo__.py` sous forme d'un tuple de 3 entiers.
-
-
-Gestion des scripts de migrations
----------------------------------
-Les scripts des migrations doivent être placés dans le répertoire `migration` de
-l'application, et nommé de la manière suivante :
-
-::
-
-  <n° de version X.Y.Z>[_<description>]_<mode>.py
-
-dans lequel : 
-
-* X.Y.Z correspond au n° de version du modèle vers lequel le script permet de
-  migrer,
+Mise en place d'un environnement de développement Erudi
+=======================================================
 
-* le *mode* (entre le dernier "_" et l'extension ".py") indique à quelle partie
-  de l'application (serveur RQL, serveur web) le script s'applique en cas
-  d'installation distribuée. Il peut valoir : 
-
-  * `common`, s'applique aussi bien sur le serveur RQL que sur le serveur web,
-    et met à jour des fichiers sur le disque (migration de fichier de
-    configuration par exemple).
-
-  * `web`, s'applique uniquement sur le serveur web, et met à jour des fichiers
-    sur le disque 
-
-  * `repository`, s'applique uniquement sur le serveur RQL, et met à jour des
-    fichiers sur le disque 
+.. toctree::
+   :maxdepth: 1
 
-  * `Any`, s'applique uniquement sur le serveur RQL, et met à jour des
-    données en base (migrations de schéma et de données par ex.)
-
+   sect_mercurial.txt
+   sect_installation.txt
+   sect_cubicweb-ctl.txt
 
-Toujours dans le répertoire `migration`, le fichier spécial `depends.map` permet
-d'indiquer que pour migrer vers une version spécifique du modèle, il faut tout
-d'abord avoir migrer vers une version données de erudi. Ce fichier peut contenir
-des commentaires (lignes commençant par un "#"), et une dépendance est notée sur
-une ligne de la manière suivante : ::
-
-  <n° de version du modèle X.Y.Z> : <n° de version erudi X.Y.Z>
-
-Par exemple ::
-
-  0.12.0: 2.26.0
-  0.13.0: 2.27.0
-  # 0.14 works with 2.27 <= erudi <= 2.28 at least
-  0.15.0: 2.28.0
 
 
-Contexte de base
-----------------
-Les identifiants suivants sont préféfinis dans les scripts de migration : 
-
-* `config`, configuration de l'instance
-
-* `interactive_mode`, booléen indiquant si le script est éxécuté en mode
-  interactif ou non
+Création d'un composant
+-----------------------
+Commençons par créer un squelette qui nous servira de base au développement de
+notre composant ou application ::
 
-* `appltemplversion`, version du modèle d'application de l'instance
-
-* `applerudiversion`, version erudi de l'instance
-
-* `templversion`, version du modéle d'application installée
+  cd ~/hg
+  erudi-ctl newtemplate moncomposant
+  # répondre aux questions
+  hg init moncomposant
+  cd moncomposant
+  hg add .
+  hg ci
 
-* `erudiversion`, version erudi installée
-
-* `confirm(question)`, fonction posant une question et retournant vrai si
-  l'utilisateur a répondu oui, faux sinon (retourne toujours vrai en mode non
-  interactif) 
+A partir de là si tout va bien, votre composant devrait être affiché par
+`erudi-ctl list` dans la section *Available components*, si ce n'est pas le cas
+revoir la section `Configuration de l'environnement`_.
 
-* `_`, fonction équivalente à `unicode` permettant de marquer des chaines à
-  internationaliser dans les scripts de migration
-
-Dans les scripts "repository", les identifiants suivant sont également définis :
-
-* `checkpoint`, demande confirmant et effectue un "commit" au point d'appel
-
-* `repo_schema`, schéma persistent de l'instance (i.e. schéma de l'instance en
-  cours de migration)
+Pour utiliser un composant, il faut le mentionner dans la variable
+__use__ du fichier __pkginfo__ de l'application. Cette variable
+contrôle à la fois le packaging de l'application (dépendances gérées
+par les utilitaires système comme les outils APT) et les composants
+effectivement utilisables lors de la création de la base
+(import_erschema('Moncomposant') ne fonctionne pas sinon).
 
-* `newschema`, schéma installé sur le système de fichier (i.e. schéma de la
-  version à jour du modèle et de erudi)
-
-* `sqlcursor`, un curseur SQL pour les très rares cas où il est réellement
-  nécessaire ou avantageux de passer par du sql
+Création d'une instance de développement
+----------------------------------------
 
-* `repo`, l'objet repository
+Maintenant que nous avons notre squelette de modèle, on peut en créer une
+instance afin de voir ce que tout ça donne dans un simple navigateur web.
+Nous allons utiliser une configuration `all-in-one` afin de simplifier les
+choses ::
 
-                        
-Migration de schéma
--------------------
-Les fonctions de migration de schéma suivantes sont disponibles dans les scripts
-"repository" : 
+  erudi-ctl create -c all-in-one moncomposant moninstance
 
-* `add_attribute(etype, attrname, attrtype=None, commit=True)`, ajoute un
-  nouvel attribut à un type d'entité existante. Si le type de celui-ci n'est pas
-  spécifié il est extrait du schéma à jour.
-        
-* `drop_attribute(etype, attrname, commit=True)`, supprime un
-  attribut à un type d'entité existante.
-
-* `rename_attribute(etype, oldname, newname, commit=True)`, renomme un attribut
-            
-* `add_entity_type(etype, auto=True, commit=True)`, ajoute un nouveau type
-  d'entité. Si `auto` est vrai, toutes les relations utilisant ce type d'entité
-  et ayant un type d'entité connu à l'autre extrémité vont également être
-  ajoutées.
+Une série de questions vont être posées, la réponse par défaut est généralement
+suffisante. Vous pourrez de toute façon modifier la configuration par la suite
+en éditant les fichiers générés. Lorsqu'un login/mot de passe d'accès au sgbd
+vous est demandé, il est recommandé d'utilisé l'utilisateur créé lors de la
+`Configuration Postgres`_.
 
-* `drop_entity_type(etype, commit=True)`, supprime un type d'entité et toutes
-  les relations l'utilisant.
-
-* `rename_entity_type(oldname, newname, commit=True)`, renomme un type d'entité
-            
-* `add_relation_type(rtype, addrdef=True, commit=True)`, ajoute un nouveau type
-  de relation. Si `addrdef` est vrai, toutes les définitions de relation de ce
-  type seront également ajoutées.
-
-* `drop_relation_type(rtype, commit=True)`, supprime un type de relation et
-  toutes les définitions de ce type.
-
-* `rename_relation(oldname, newname, commit=True)`, renomme une relation.
+Il est important de distinguer ici l'utilisateur utilisé pour accéder au sgbd,
+et l'utilisateur utilisé pour s'authentifier dans l'application erudi. Lorsque
+l'application erudi démarre, elle utilise le login/mot de passe sgdb pour
+récupérer le schéma et gérer les transactions bas-niveau. En revanche, lorsque
+`erudi-ctl create` vous demande un login/mot de passe `manager` pour erudi, il
+s'agit d'un utilisateur qui sera créé dans l'application `erudi` pour pouvoir
+s'y connecter dans un premier temps et l'administrer. Il sera par la suite possible
+de créer des utilisateurs différents pour l'application.
 
-* `add_relation_definition(subjtype, rtype, objtype, commit=True)`, ajoute une
-  définition de relation.
+A l'issue de cette commande, la définition de votre instance se trouve dans
+*~/etc/erudi.d/moninstance/*. Pour la lancer, il suffit de taper ::
 
-* `drop_relation_definition(subjtype, rtype, objtype, commit=True)`, supprime
-  une définition de relation.
+  erudi-ctl start -D moninstance
 
-* `synchronize_permissions(ertype, commit=True)`, synchronise les permissions
-  d'un type d'entité ou de relation
-        
-* `synchronize_rschema(rtype, commit=True)`, synchronise les propriétés et
-  permissions d'un type de relation.
-                
-* `synchronize_eschema(etype, commit=True)`, synchronise les propriétés et
-  permissions d'un type d'entité.
-    
-* `synchronize_schema(commit=True)`, synchronise le schéma persistent avec le
-  schéma à jour (mais sans ajouter ni supprimer de nouveaux types d'entités ou
-  de relations ni de définitions de relation).
-        
-* `change_relation_props(subjtype, rtype, objtype, commit=True, **kwargs)`, change
-  les propriétés d'une definition de relation en utilisant les arguments nommés
-  pour les propriétés à changer.
+L'option `-D` indique le *debug mode* : l'instance ne passe pas en mode serveur
+et ne se déconnecte pas du terminal, ce qui simplifie le dépannage en cas de non
+démarrage de l'instance. Vous pouvez ensuite allez voir ce que ça donne en
+pointant votre navigateur sur l'url `http://localhost:8080` (le n° de port
+dépend de votre configuration). Pour vous authentifier vous pouvez utiliser le
+login/mot de passe administrateur que vous avez spécifié lors de la création de
+l'instance.
 
-* `set_widget(etype, rtype, widget, commit=True)`, change le widget à utiliser
-  pour la relation <rtype> du type d'entité <etype>
+Pour arrêter l'instance, un Ctrl-C dans la fenêtre où vous l'avez lancé
+suffit. Si l'option `-D` a été omise, il faut taper ::
 
-* `set_size_constraint(etype, rtype, size, commit=True)`, change la contrainte
-  de taille pour la relation <rtype> du type d'entité <etype>
+  erudi-ctl stop moninstance
+
+Voilà, tout est en place pour démarrer le développement du modèle...
 
 
-Migration de données
---------------------
-Les fonctions de migration de données suivantes sont disponibles dans les scripts
-"repository" : 
-
-* `rql(rql, kwargs=None, cachekey=None, ask_confirm=True)`, éxécute une
-  requête rql arbitraire, d'interrogation ou de modification. Un objet result
-  set est retourné.
-
-* `add_entity(etype, *args, **kwargs)`, ajoute une nouvelle entité du type
-  données. La valeur des attributs et relations est spécifiée en utilisant les
-  arguments nommés et positionnels.
-
-  
-Création de workflow
---------------------
-Les fonctions de création de workflow suivantes sont disponibles dans les scripts
-"repository" : 
-
-* `add_state(name, stateof, initial=False, commit=False, **kwargs)`, ajoute un
-  nouvel état de workflow
-    
-* `add_transition(name, transitionof, fromstates, tostate, requiredgroups=(), commit=False, **kwargs)`, 
-  ajoute une nouvelle transtion de workflow
+Utilisation de erudi-liveserver
+-------------------------------
 
-Migration de configuration
---------------------------
-Les fonctions de migration de configuration suivantes sont disponibles dans tout
-les scripts : 
-
-* `option_renamed(oldname, newname)`, indique qu'une option a été renommée
-
-* `option_group_change(option, oldgroup, newgroup)`, indique qu'une option a
-  changé de groupe
-
-* `option_added(oldname, newname)`, indique qu'une option a été ajoutée
-
-* `option_removed(oldname, newname)`, indique qu'une option a été supprimée
+Afin de tester rapidement un nouveau composant, on peut également
+utiliser le script `erudi-liveserver` qui permet de créer une
+application en mémoire (utilisant une base de données SQLite par
+défaut) et la rendre accessible via un serveur web::
 
-
-Autres fonctions de migration
------------------------------
-Ces fonctions ne sont utilisés que pour des opérations de bas niveau
-irréalisables autrement ou pour réparer des bases cassées lors de session
-interactive. Elles sont disponibles dans les scripts "repository".
+  erudi-liveserver moncomposant
 
-* `sqlexec(sql, args=None, ask_confirm=True)`, éxécute une requête sql
-  arbitraire, à n'utiliser 
+ou bien, pour utiliser une base de données existante (SQLite ou postgres)::
 
-* `add_entity_type_table(etype, commit=True)`
-* `add_relation_type_table(rtype, commit=True)`
-* `uninline_relation(rtype, commit=True)`
+  erudi-liveserver -s monfichier_sources moncomposant
+