Passer la navigation

Monthly Archives: janvier 2009

Salut !

Concernant mon projet open source de modélisation d’applications web à l’aide du cadre applicatif Php 5, Symfony.

Ce petit post pour revoir les outils utilisés et pourquoi :

  1. Epsilon Generation Language
    Permet de générer du texte à partir d’instances de méta-modèles
  2. Epsilon Wizard Language
    Permet de définir des wizards utilisables depuis les éditeurs EMF générés
  3. Epsilon Transform Language
    Permet de définir des transformations entre plusieurs modèles en source vers plusieurs modèles cible, de différents méta-modèles
  4. EMF
    Permet de définir des méta-modèles ecore
  5. Epsilon Eugenia
    Permet de réaliser des éditeurs graphiques semi-automatiquement à partir d’un méta-modèle ecore
  6. GMF
    Cadre applicatif pour la réalisation d’éditeurs graphiques sur base ecore

Tous sont basés sur Eclipse Ganymed.

Maintenant la liste des projets qui vont être créés:

  1. php5
    Définition du métamodèle de Php5 dans sa perspective objet (Class, Interface, Namespace, etc) > 5.2.3
  2. doctrine
    Définition du métamodèle de Doctrine (Record, Connection, etc) > 1.0.6
  3. symfony
    Définition du métamodèle de Symfony (Project, Application, Module, etc) > 1.0

Chacun de ces projets nécessitent plusieurs artefacts:

  1. un méta-modèle
  2. un générateur
  3. des wizards pour faciliter l’instantiation de modèles
  4. des contraintes pour valider les instances de métamodèles
  5. un éditeur hierarchique (parent/enfants)
  6. un éditeur graphique (boite-relations)

Il y a aussi les transformations vers une cible technologique définie.
Concrètement, le méta-modèle Php5 doit être vu comme une cible technologique, puisqu’il y a derrière un générateur de code.
A contrario, Symfony, bien qu’implémenté en Php5, reste purement conceptuel : c’est une utilisation du méta-modèle Php5.

Exactement, lorsqu’on créé une application Symfony, on écrit du code Php5.
Donc lorsqu’on modélisera une application Symfony, on écrira un modèle Symfony, qui sera transformé ensuite en modèle Php5, de là le code sera généré.

Là, on réutilise. Enfin on capitalise.

Il faut donc rajouter un projet .xxx2php5 pour Symfony et Doctrine

Les contraintes sont dans un autre projet pour l’instant mais il se peut bien qu’ils rejoignent le projet du méta-modèle ce qui serait aussi sensé.

Ce qui au final donne ça :

  1. php5.model
  2. php5.generator
  3. php5.model.wizards
  4. php5.model.checks
  5. php5.editor
  6. php5.modeler
  7. doctrine.model
  8. doctrine.generator
  9. doctrine.model.wizards
  10. doctrine.model.checks
  11. doctrine.editor
  12. doctrine.modeler
  13. doctrine.doctrine2php5
  14. symfony.model
  15. symfony.generator
  16. symfony.model.wizards
  17. symfony.model.checks
  18. symfony.editor
  19. symfony.modeler
  20. symfony.symfony2php5

Pourquoi Php5 ?

L’objectif est de permettre la modélisation d’applications symfony et symfony étant un framework php il est sensé que ce métamodèle étende le métamodèle de php5, ou alors permet une transformation vers ce méta-modèle.
Dans tous les cas, le méta-modèle Php5 est nécessaire.

Doctrine aussi est un framework php5, et lui aussi étendra le métamodèle php5 ou permettra une transformation vers.

Pourquoi Symfony ?
Symfony est un framework Php5 implémentant le motif de conception Modèle-Vue-Controleur et est pluggable avec Doctrine, un ORM en Php5 lui aussi, s’appuyant sur DBA pour l’accès aux bases de données. De fait, c’est le plus puissant et le plus aboutit des frameworks php5.
C’est bien un framework (cadre applicatif), pas un CMS ou autres Joomla !

Pourquoi Doctrine ?
Doctrine est un Object Relational Mapper écrit en Php5, c’est aussi le plus puissant et le plus aboutit des ORM php5. Il implémente le DQL, dérivé du HQL d’Hibernate du monde des barbus de Java.

Les artefacts sont produits en utilisants les outils en regard:

  1. métamodèle : EMF
  2. generateur : Epsilon Generation Language
  3. wizards : Epsilon Wizard Language
  4. checks : Epsilon Validation Language
  5. editor : EMF éditeur xml automatiquement généré
  6. modeler : GMF éditeur généré grâce à Eugenia

Donc au final j’abandonne Acceleo qui est un trés bon produit mais je préfère epsilon car bien plus complet dans tous les domaines de la méta-modélisation (s’incruste dans n’importe quel éditeur EMF, M2M, M2T, manque T2M).

Flow3 est un sérieux concurrent de Symfony, implémentant les concepts DDD d’Eric Evans. Quand ce dernier possèdera une documentation,c’est à dire quand Php 5,3 sera stable et que phpDoc sera mis à jour, sûrement que je créérai un modeleur pour ce dernier.

But for now, Let’s play.

Mon premier module acceleo délivré en open source concerne la génération de templates acceleo à partir d’un modèle Ecore.

Vous trouverez le plugin JARed ici : http://php5meta-model.svn.sourceforge.net/viewvc/php5meta-model/ecore2acceleo-generator/builds/

Et le code source du projet eclipse ici : http://php5meta-model.svn.sourceforge.net/viewvc/php5meta-model/ecore2acceleo-generator/trunk/ecore2acceleo-generator/

A bientôt !

Ca y est, nous y voilà.

J’ai récemment ouvert 3 projets sourceforge, symfony-model, php5meta-model (que je vais demander à renommer en php5-model) et doctrine-model.

https://sourceforge.net/projects/symfony-model/

https://sourceforge.net/projects/doctrine-model/

https://sourceforge.net/projects/php5meta-model/ Va être renommé en php5-model

Explications :

Tous ces projets comprennent (ou comprendront une fois commit)

  • model

C’est le modèle du projet : pour doctrine, il s’agira du modèle ecore des concepts définis par Doctrine (phpdoctrine.org)

  • edit : généré par emf genmodel, défini les providers des éléments du modèle
  • editor : généré par emf genmodel, défini l’éditeur d’une instance du modèle
  • generator : développé, contient des chaines paramétrables (modules) et des templates

Pour l’instant je m’occupe principalement de Php5 Model, étant donné qu’il sera étendu par Doctrine et Symfony.

Ainsi depuis Doctrine::Record, on pourra ajouter des Methods et Properties dans la classe même (avec séparation des concepts en suivant les conventions Doctrine).

Il en ira de même pour Symfony.

Rock’n’roll

Lorsqu’on utilise Ecore et Accelo (possible avec d’autres frameworks de génération tels qu’openArchitectureWare), il est possible de générer des patrons de templates acceleo (ou ATL ou xTend ou … ?) à partir de fichiers ecore (ou UML ou …). C’est beau l’engeenering.

D’abord le modèle ecore d’exemple :

source_ecore_model_content

Ensuite le fichier EPackage.mt qui à la charge de lancer le script generate pour tous les EClass qu’il contient :

<%
metamodel http://www.eclipse.org/emf/2002/Ecore
import EClass
%>

<%script type= »ecore.EPackage » name= »generate »%>
<%for (filter(« EClass »)){%>
<%generate%>
<%}%>

Et le fichier EClass.mt :

[%
metamodel http://www.eclipse.org/emf/2002/Ecore
%]

[%script type= »ecore.EClassifier » name= »genHeader »%]
<%
metamodel [%genNSURI%]
%>

[%script type= »ecore.EClassifier » name= »genNSURI »%]
[%if (getProperty(« nsURI »).nSize() > 0){%][%getProperty(« nsURI »)%]
[%}else{%]
[%getRootContainer().filter(« EPackage »).nsURI%]
[%}%]

[%script type= »ecore.EClass » name= »genFilename »%]
[%ePackage.genName.replaceAll(« . »,getProperty(« directorySeparator ») ) + name%]

[%script type= »ecore.EClass » name= »generate » file= »[%genFilename%].mt »%]
[%genHeader%]
<%script type= »[%genNameType%] » name= »generate » file= »[%name%].mt »%>

[%script type= »ecore.EClass » name= »genNameType »%]
[%if (ePackage.nSize()>0){%][%ePackage.genName%].[%}%][%name%]

[%script type= »ecore.EPackage » name= »genName »%]
[%if (eSuperPackage.nSize()>0){%][%eSuperPackage.genName%].[%}%][%name%]

On créer une chaîne d’exécution à peu près comme celle-ci

chaine_epackage_as_root

Ensuite on l’exécute et on obtient ceci :

generated_files_are_generators

Et un générateur contient le contenu attendu :

class-generator-generated-content

Pour le développement sans registry (mon eclipse bug j’ai plus de model registry) j’ai du rajouté une propriété de génération (generator.properties) qui permet de définir manuellement le nsURI.

On peut bien sûr aller plus loins en se concentrant sur le modèle Ecore et l’instance qu’on développe pour pousser la génération de générateurs (faire abstraction etc, on the way to go).

A bientôt !

En usant de plusieurs outils open source, il est dorénavant possible de réaliser des éditeurs visuels de deux type.

Le premier, vous pouvez le voir dans les screenshots des précédents posts : hiérarchique (parent/enfants)

modele-exemple-du-metamodele-php51

Le second est tout autre, bien plus « objet » : boite-relations (comme l’éditeur de méta-modèle vu aussi précédement).

php5-classes1

A partir d’un métamodèle, on peut donc plus facilement que jamais créer des éditeurs visuels.
Le premier est générable « dynamiquement » ou « statiquement ». C’est à dire qu’on peut tester l’éditeur parent/enfants lors de la création du méta-modèle, en séléctionnant le noeud que l’on souhaite instancier puis clique droit->Create Dynamic instance, là est demandé de renseigner le chemin ou sera enregistré l’instance (fichier xml comprenant tous les espaces de noms nécessaires au noeud racine instancié).
Statiquement, car on peut aussi créer un fichier nommé « GenModel », qui permet de générer l’éditeur, le modèle et d’autres éléments sous forme de fichiers sources java, des fichiers versionnalisables donc. Pas comme la version dynamique, vous l’aurez compris.

Cette différence ammène pas mal de nuances en terme de différences entre environnements : en développement il n’est pas nécessaie d’avoir du code source si on peut avoir une version dynamique, le modèle n’étant généralement pas stable. Ainsi on n’a pas à regénerer le code de l’éditeur hiérarchique puisqu’on utilise la version dynamique de l’éditeur.

Pour ce qui concerne la génération d’éditeur boite-relations, je vous laisse lire les docs de GMF et de Topcased :

http://topcased-mm.gforge.enseeiht.fr/website/modeling/tutorials/tutorialNetwork.html
http://www.eclipse.org/modeling/gmf/
http://wiki.eclipse.org/GMF_Documentation

A l’aide des méta-modèles php5, symfony, et doctrine, on peut donc commencer à penser à réaliser un éditeur d’application web ?
Et d’en générer le code ?
Comment organiser tout ça ?
Nous le verrons bientôt !

A bientôt !

Un petit template par là…

Contenue du template acceleo correspondant à la métaclasse doctrine::Class

<%
metamodel /metamodels/doctrine/doctrine.ecore

%>

<%script type= »Classes.Class » name= »generateClass »%>
<%name.toU1Case()%>:
<%if (connection.nSize() == 1){%>
<%generateConnection%>
<%}%>
<%if (inheritance.nSize() == 1){%>
<%inheritance.generateInheritance%>
<%}%>
<%if (column.nSize() > 0 ){%>
<%generateColumns%>
<%}%>
<%if (relation.nSize() > 0){%>
<%generateRelations%>
<%}%>
<%if (actAs.nSize() > 0){%>
<%generateActAs%>
<%}%>
<%if (option.nSize() > 0){%>
<%generateOptions%>
<%}%>

<%–

ABOUT CONNECTION

–%>
<%script type= »Classes.Class » name= »generateConnection »%>
connection: <%connection.name%>

<%–

ABOUT INHERITANCY

–%>
<%script type= »Inheritancy.Inheritance » name= »generateInheritance » post= »trim() »%>
inheritance:
<%generateExtends%>
<%generateInheritanceType%>
<%if (eClass().name.equalsIgnoreCase(« ColumnAggregationInheritance »)){%><%filter(« ColumnAggregationInheritance »).generateManyToManyOptions%><%}%>

<%script type= »Inheritancy.Inheritance » name= »generateExtends »%>
extends: <%extends.name.toU1Case()%>

<%script type= »Inheritancy.Inheritance » name= »generateInheritanceType »%>
type: <%getInheritanceType%>

<%script type= »Inheritancy.Inheritance » name= »getInheritanceType » post= »trim() »%>
<%if (eClass().name.equalsIgnoreCase(« ColumnAggregationInheritance »)){%>column_aggregation
<%}else{%><%if (eClass().name.equalsIgnoreCase(« SimpleInheritance »)){%>simple
<%}else{%>concrete<%}%><%}%>

<%script type= »Inheritancy.ColumnAggregationInheritance » name= »generateManyToManyOptions »%>
<%generateKeyField%>
<%generateKeyValue%>

<%script type= »Inheritancy.ColumnAggregationInheritance » name= »generateKeyField »%>
<%if (sKeyField.length() > 0){%>  keyField: <%sKeyField%><%}else{%><%if (keyField.name.length() > 0){%>  keyField: <%keyField.name%><%}%><%}%>
<%script type= »Inheritancy.ColumnAggregationInheritance » name= »generateKeyValue »%>
<%if (keyValue.length() > 0){%>  keyValue: <%keyValue%><%}%>

<%–

ABOUT COLUMNS

–%>
<%script type= »Classes.Class » name= »generateColumns » post= »trim() »%>
<%if (column.nSize() > 0){%>
columns:
<%for (column){%>
<%generateColumn%>
<%}%>
<%}%>

<%script type= »Columns.Column » name= »generateColumn » post= »trim() »%>
<%name%>:
<%generateType%>
<%if (option.nSize() > 0 ){%>  <%generateOptions%><%}%>

<%script type= »Columns.Column » name= »generateType »%>
type: <%type.name.toLowerCase()%>(<%getMaxValue%>)

<%script type= »Columns.Column » name= »getMaxValue » post= »trim() » %>
<%constraint.filter(« withMaxLength »).max%>

<%– ABOUT COLUMN OPTIONS –%>
<%script type= »Columns.Column » name= »generateOptions » post= »trim() »%>
<%for (option){%>
<%name%>: <%value%>
<%}%>

<%–

ABOUT RELATIONS

–%>
<%script type= »Classes.Class » name= »generateRelations »%>

<%–

ABOUT actAs

–%>
<%script type= »Classes.Class » name= »generateActAs »%>

<%–

ABOUT Options

–%>
<%script type= »Classes.Class » name= »generateOptions »%>

Personnellement quand je vois ça et le modèle, je me dis qu’il y a encore et toujours des dimensions supérieures, à regarder, à rêver et à explorer.

A bientôt !

Une petite capture d’écran pour montrer une mini instance du méta-modèle Symfony :

symfony-metamodel-instance-example

Il n’est pas fini, mais ça va viendre.

BAANNZZAAAAAAIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII

Des modèles comme s’il en pleuvait.

J’ai modélisé les concepts de doctrine et j’ai créé une instance pour le transformer en code.

Cela passe par:

  1. modéliser le méta-modèle
  2. créer une instance du méta-modèle
  3. écrire les templates appropriés

Voici un morceau du méta-modèle

root-doctrine-metamodel

Et en voici un autre (au niveau du concept Doctrine_Record réifié en « Class ») :

doctrine-class-connections-in-metamodel1

Et on peut maintenant créer de jolies modèles-instances du méta-modèle :

a-doctrine-schema-modeled

Après l’écriture des templates, on peut générer du code à partir de ce modèle. Et voilà le résultat depuis l’aperçu temps-réel d’Acceleo :

generated-content-from-a-doctrine-model

Elle est pas belle la vie ?

Je suis à peine en train d’écrire les templates, mais le méta-modèle est plutôt complet vis-à-vis de Doctrine.

doctrine-meta-model-features

Ce qui est super fun, c’est que j’ai réalisé le méta-modèle de Php5 déjà, et devinez, Doctrine repose sur ce language.
Autrement dit, je n’ai qu’à faire étendre le language php5 par le language doctrine.
Ainsi depuis un modèle Doctrine, je pourrai aussi parler php, et c’est normal !

Donc dans la classe « Class », il y aura aussi Property et Method (définis dans php5).

Aussi on peut le revoir entièrement et dire que le doctrine::relation étend php5::property par exemple, on peut ainsi ré-utilise les templates php5 !

J’ai trop envie de tester, alors c’est partie !

La seule modification faite ici est donc de définir le fait que doctrine::Class étend php5::Class :

Class

Just add an extension from doctrine::Class to php5::Class

Comparez avec la première image de ce post.

Une fois le méta-modèle enregistré, on peut créer des opérations dans une classe doctrine, quoi de plus naturel ?
Ce qu’il reste à préciser, c’est qu’on a le contrôle sur ou cela sera générer.
Par exemple, dans le cadre d’une utilisation avec Symfony, les méthodes pourront être générés dans lib/ plutôt que dans *Table.

Et voici maintenant une instance possible :

doctrine-metamodel-instance-example

On peut y voir que notre doctrine::Class est maintenant définie comme une php5::Class 🙂

A bientôt !

Nous avons dans le dernier post vu qu’on peut définir le modèle d’un modèle, à savoir le métamodèle, et ainsi en créer des instances, ces fameux modèles.

Maintenant nous allons voir qu ‘on peut, à partir d’une instance de métamodèle, générer du code.

Je vais ici utiliser Acceleo, cadre applicatif qui se prête très bien au jeu 🙂

Voici le code du template class.mt

<%
metamodel /metamodels/php5/php5.ecore
import templates.php5.method
import templates.php5.property
import templates.php5.classifier_constant
%>

<%script type= »Classes.Class » name= »genAbstract »%>
<%if (metamodel::isAbstract){%>abstract <%}%>

<%script type= »Classes.Class » name= »genClass » file= »<%name%>.txt »%>
<%script::genAbstract%>class <%name%><%script::genExtends%><%script::genImplements%>{
// CONSTANTS
<%if (constants.nSize()> 0){%><%script::genConstants%><%}%>

//PROPERTIES
<%if (properties.nSize()> 0){%><%script::genProperties%><%}%>

//METHODS
<%if (properties.nSize()> 0){%><%script::genMethods%><%}%>

}

<%script type= »Classes.Class » name= »genExtends »%>
<%if (metamodel::extends.nSize() > 0){%> extends <%metamodel::extends.nGet(0).name%><%}%>

<%script type= »Classes.Class » name= »genImplements »%>
<%if (metamodel::implements.nSize() > 0 ){%> implements <%metamodel::implements.name.sep(« , »)%><%}%>

La première ligne déclare le métamodèle utilisé par ce template. Il peut y en avoir plusieurs, faisant appel à plusieurs métaclasses de plusieurs métamodèles différents.
Les trois lignes qui suivent importent des fichiers tel que celui-ci dans l’espace de nom du compilateur Acceleo. Ainsi depuis ce template je peux appeller des scripts déclarés dans d’autres templates.

En fait un template contient des scripts, et un script se rapporte à une métaclasse. Prochainement, le métamodèle d’Accéléo, promis.

Maintenant, réalisons un modèle exemple de notre méta-modèle Php 5 :

myClass et myExtendingClass

myClass et myExtendingClass

Pour faire simple, il n’y a que deux classes, myBaseClass et myExtendingBaseClass. La première déclare une constante, une propriété et une méthode ayant un paramètre. L’autre classe ne fait qu’étendre la première.

Après avoir chargé le template à l’aide de l’éditeur reflexif Acceleo, on clique sur « source » (sur l’image en bas à droite) et on voit la prévisualisation du résultat de la génération, wonderfull world.

Previsualisation temps reel du résultat de l'exécution du template de metaclasse Classes.Class

Et voilà, on y est ! Il ne reste plus qu’à générer !

Maintenant, imaginez posséder le méta-modèle d’un système d’information (imaginez ce que vous voulez être comme étant un système d’information).

Modélisez-le, et générez-le.

A bientôt !

Beh nan, pas ma mère, plutôt celle de tous, de tout.

Salut, un petit post sur le « méta » et le développement dirigé par des modèles.

Pour une définition de Méta, je ne peux que vous conseiller la lecture de Wikipedia

La voici:

méta est un préfixe qui provient du grec μετά (meta) (après, au-delà de, avec). Il exprime, tout à la fois, la réflexion, le changement, la succession, le fait d’aller au-delà, à côté de, entre ou avec.

méta signifie aussi à propos, comme dans métaphysique, la physique de la physique, ou dans métalinguistique, désignant le lexique linguistique.

méta est souvent utilisé dans le vocabulaire scientifique pour indiquer l’auto-référence (réflexion) :

  • méta-physique = physique de la physique
  • méta-langage = système ou langage permettant de décrire d’autres langages
  • méta-livre = livre à propos d’un livre
  • méta-documentation = documentation sur la documentation

méta est aussi très souvent utilisé en science pour désigner un niveau d’abstraction supérieur, un modèle. Exemple : une métadonnée est une donnée sur les données.

Nous voilà donc à parler de ce qu’on parle, mais tout autrement.

Ah ?

Bien sûr que oui. Et on le fait tout le temps d’ailleurs, mais on est devenue tellement arrogant qu’on ne le voit plus.

Ainsi quand on parle de quelque chose, on est obligé de savoir au pire  du meilleur la structure et au meilleur en y ajoutant la connaissance du comportement, de ce dont on parle.

Quand on parle d’une personne, on s’attend à ce qu’elle est des jambes, parce qu’on pense à un truc du genre « Homme possède deux Jambes » et que si bizarrement un homme n’en possède pas, ça choque un peu, mais s’il en a trois ?

En bref, quand on se place dans un contexte « méta », on parle donc de ce qui est : la structure topologique et relationnelle de l’être (ce qui est), ce qui inclus évidement son comportement autant interne qu’externe vis-à-vis des autres partis.

Et on fait ça selon nos intérêts : un informaticien va prêter plus d’attention à ce qu' »est » l’informatique, et ainsi mieux y voir (à force de temps, de passion ou autre) les concepts affairants et leurs relations entre elles. Il se peut même que certains concepts n’y soit que « référencés ». Ils ne sont pas hors de contexte, seulement que ce contexte l’utilise (référencement ou encapsulation). Par exemple, dans le contexte « Pays », on pourrait y définir « Entreprise ». Après tout un pays possède des entreprises, dans un certains sens (« les entreprises françaises »). Et dans un autre sens, Pays pourrait être référencé par un produit « Voyage » d’un tout autre contexte. Ils n’ont rien à voir ?

C’est ce qu’exprime Wiki dans sa définition, dans la première phrase « le fait d’aller au-delà, à côté de, entre ou avec« . Depuis le niveau méta, on peut aller absolument n’importe ou, lier n’importe quoi à n’importe quoi d’autre. Pourvue qu’on sache en comprendre le sens (documentation du modèle, etc).

Je n’ai jusqu’ici utilisé que peu de mots inusuels.

Un exemple : Php 5.

Php5 est un language de développement informatique.
Ce language définit des concepts, tels que Classe, Interface, Propriété, Methode, Opération, etc.
A l’aide d’un méta-language, on peut définir le modèle.

Métaclasse Class

Métaclasse Class

Ici est définit une métaclasse « Class » qui possède des relations :

  • extends: une classe peut étendre une autre classe, une seulement
  • methods : une classe peut posséder une à plusieurs méthodes
  • properties : une classe peut posséder une à plusieurs propriétés
  • implements : une classe peut implémenter plusieurs interfaces

Ici nous avons donc décrit de quoi on parle, de classes. Ainsi je peux parler de la classe Personne qui possède l’attribut « jambes ».

Création de classes php5 nommées Person et Jambe et d'une relation entre elles

Création de classes php5 nommées Person et Jambe et d'une relation entre elles

Vous pouvez voir ici que le travail effectué plus haut me permet maintenant d’éditer un modèle d’une classe php5 de manière un peu plus visuelle.Ceci a été réalisé « dynamiquement », sans générer aucun code !

En premier lieu vient (d’office) un éditeur XML hiérarchique comme dans cette image. Ensuite, en utilisant GMF, on peut créer un éditeur visuel à base de concepts et de relations, un « vrai » modeleur php5 en somme.

C’est de cette même manière que j’ai réalisé script.ecore, filesystem.ecore, php5.ecore, symfony.ecore ou encore doctrine.ecore.

Générer du code à partir d’un modèle (instance de méta-modèle) :

On peut utiliser des outils tels que openArchitectureWare ou Acceleo pour générer du code à partir des modèles. Je tenterai d’aborder l’un comme l’autre dans d’autres posts.

Je grossi un peu les choses, mais c’est plus pour donner envie à d’autres de s’y mettre qu’autre chose :p

A bientôt !