Skip navigation

Category Archives: Génie Logiciel

Les méthodologies, méthodes et outils de développement de logiciels.

Salut à toutes et à tous,

Ce petit billet pour vous présenter la transformation d’un modèle en code, ou plus simplement, la génération de code au travers d’un modèle.

Dans l’état de l’art du développement logiciel, on fait abstraction du code en créant des « templates », ou en bon vieux français, des « patrons » ou encore des « modèles » 🙂
Cela nécessite donc qu’un template se réfère, dans sa terminologie, à un modèle pour injecter les données.

Je vais ici utiliser le méta-modèle défini dans mon précédent billet ainsi que le modèle et l’outillage réalisé à titre d’exemple.

L’objectif est d’utiliser ce modèle

Simple modèle d'exemple

Pour générer un fichier YAML correspondant à ceci (dans sa forme et non ses données):

Produit:
..columns:
….name: { type: string(255), notnull: true, unique: true }
..relations:
….Categories: { class: Categorie, foreignAlias: Produits, refClass: ProduitCategorie, local: produit_id, foreign: categorie_id }

Categorie:
..columns:
….name: { type: string(255), notnull: true, unique: true }
..relations:
…. Produit: { class:Produit, foreignAlias: Categories, refClass: ProduitCategorie, local: categorie_id, foreign: produit_id }

ProduitCategorie:
..columns:
….produit_id: { type: integer(20), primary: true }
….categorie_id: { type: integer(20), primary: true }
..relations:
….Produit: { local: produit_id, foreign: id, foreignAlias: Produits }
….Categorie: { local: categorie_id, foreign: id, foreignAlias: Categories }

. représente ici un espace, tant les espaces sont importants dans les fichiers yaml.

Voici le code du programme de génération :

[%
var m : Model := Model.allInstances().at(0);

for( class in m.defines )
{
%]
[%=class.name%]:
[%
if( class.column.size() > 0 ) { %]
..columns:
[% for( column in class.column ) { %]
….[%=column.name%]: { [%=column.generateProperties()%] }
[% }
}
if( class.relation.size() > 0 ) { %]
..relations:
[% for( relation in class.relation ) { %]
….[%=relation.name%]: { [%=relation.generateProperties()%] }
[% }
}
%]

[%
}

operation OneToOne generateProperties() : String {
return ‘local: ‘ + self.relates.name.toLowerCase()+’_id’ + ‘, foreign: id, foreignAlias: ‘ + self.relates.generateSingularName();
}

operation OneToMany generateProperties() : String {
return ‘otm’; –je vous laisse vous amuser
}

operation ManyToMany generateProperties() : String {
return ‘class : ‘ + self.relates.name + ‘, foreignAlias: ‘ + self.owner.generateSingularName() + ‘, refClass: ‘ + self.refClass.name + ‘, local: ‘ + self.owner.name.toLowerCase+’_id’ + ‘, foreign: ‘ + self.relates.name.toLowerCase+’_id’;
}

operation Column generateProperties() : String {
return ‘type: ‘+ self.type.generateTypeName()+'(‘+self.type.max+’)’ + ‘, primary: ‘ + self.primary + ‘, notnull: ‘ + self.generateIsNull() + ‘, unique: ‘ + self.generateIsUnique();
}

operation Column generateIsUnique() : String {
return self.unique;
}

operation Column generateIsNull() : String {
return self.notnull;
}

operation Type generateTypeName() : String {
if(self.isTypeOf(sfDoctrine!Integer)){ return ‘integer’; }
}

operation Class generateSingularName () : String {
return self.name;
}
%]

Et voici le résultat généré dans un fichier gen_schema.yml (il faut configurer un lanceur pour exécuter la génération, le site d’Epsilon explique ça très bien, voir les newsgroup aussi) :

Produit:
..columns:
….id: { type: integer(20), primary: true, notnull: true, unique: true }
..relations:
….Categories: { class : Categorie, foreignAlias: Produit, refClass: ProduitCategorie, local: produit_id, foreign: categorie_id }

Categorie:
..columns:
….id: { type: integer(20), primary: true, notnull: true, unique: true }
..relations:
….Produits: { class : Produit, foreignAlias: Categorie, refClass: ProduitCategorie, local: categorie_id, foreign: produit_id }

ProduitCategorie:
..columns:
..categorie_id: { type: integer(20), primary: true, notnull: true, unique: false }
….produit_id: { type: integer(20), primary: true, notnull: true, unique: false }
..relations:
….Categorie: { local: categorie_id, foreign: id, foreignAlias: Categorie }
….Produit: { local: produit_id, foreign: id, foreignAlias: Produit }

N.B.: il y a quelques différences du fait de modifications entre temps (pluriel/singulier, etc), mais on fait ce qu’on veut en tout point, ne vous concentrez donc pas sur les incohérences entre mes deux billets, ce n’est qu’un exemple réalisé en deux temps 😉

Salut à toutes et à tous,

Epsilon est une lettre grec qui signifie en mathématique un élément très petit. Dans 1984, il signifie la plus basse caste de la hiérarchie des Hommes. Entre autres significations pleines de sens

Ce petit billet donc pour vous présenter l’avancée actuelle du projet d’aide à la modélisation de schémas pour sfDoctrine, plug-in de Symfony (cadre applicatif Php5) qui le ponte à Doctrine (Objet-Relational Mapper en Php5).

Cet outil repose, vous l’aurez deviné, fervants lecteurs de mon blog, exclusivement sur des outils open-source que sont Eclipse et ses plug-ins EMF et Epsilon.

Petites explications avant tout : Doctrine est un ORM qui permet de relater un objet en une table relationnel, et vice-versa. Ceci permet d’écrire des logiciels à l’aide du paradigme « objet » tout en utilisant les meilleurs techniques de persistances que sont les Relational DataBase Management Systems (RDBMS ou SGDBR, type MySQL ou PostgreSQL dans l’univers open-source).

Il y a donc des classes, comme on parle de type de choses, puis ces choses à proprement parlé.

Dans le monde de sfDoctrine, on parle donc de Classes et d’Objets, qui sont les instances de classes, de la même manière qu’on parle de « voitures » et de « ma voiture », ou de types de choses et des choses en rapport à ces types. « Typiquement », une « voiture » (la mienne par exemple) est une instance de la classe « Voiture ».
Dans les modèles suivants, les boites jaunes représentes donc des types (ou des classes, techniquement) et les liens représentes ces « relations ».
On pourra par la suite « instancier » ces types de choses (comme on le verra avec la classe Class qui permettra de définir, en l’instanciant par deux fois, les « Classes » Produit et Categorie-). Il est hyper important de comprendre qu’on parle à différents moments dans différents niveaux d’abstraction. Voyez le contexte du vocable pour pouvoir vous repérer. Si ambiguïté, dites le moi en commentaire, j’expliciterai ou corrigerai au besoin.

sfDoctrine relate ces deux univers (objet et relationnel) en les mixant au sein d’un même type d’objet, appelé Class (qui est différent de celui du paradigme objet, par « extension » abstraite : on saute de niveau d’abstraction, l’un permettant à l’autre d' »exister » -ou plutôt d’être défini comme on l’entend-).

Trêve de bla-bla, voici une capture d’écran d’un simple méta-modèle de sfDoctrine réalisé à titre d’exemple :

Méta-modèle de sfDoctrine, diagrame d'entré

On voit ici qu’un modèle permet de définir des classes, et que les deux sont des choses nommés, elles ont obligatoirement un nom.

Voici maintenant le diagramme du méta-modèle des « columns » qui est partie intégrante du méta-modèle ci-dessus (qu’on peut voir en tant que « package » ou « dossier » nommé « Columns ») :

Description de la EClass Column d'une EClass Class

Description de la EClass Column d'une EClass Class

On voit ici qu’une classe possèdes 0 à plusieurs colonnes, et qu’une colonne est elle aussi une chose obligatoirement nommée, en plus de posséder obligatoirement un type qui doit avoir un max de défini.

Et enfin le diagramme des « Relations » :

Méta-modèle des Relations d'une Class

On voit ici qu’une classe peut posséder 0 ou plusieurs relations, relation (en italique, soulignant que c’est « abstrait » -mais à ce niveau-ci d’abstraction-) qui peut être de trois sortes:
une relation de type mono-valuée OneToOne ou un à un, qui permet de dire, par exemple, qu’une voiture n’a qu’un moteur,
une relation de type multi-valué ou un à plusieurs, qui permet de dire, par exemple, qu’une voiture peut avoir plusieurs roues,
une relation de type multi-valué bi-partie ou plusieurs à plusieurs, qui permet de dire, par exemple, qu’un produit peut être lié à plusieurs catégories et qu’une catégorie peut être liée à plusieurs produits.

La big-picture du point de vue du phénomène « Class » :

Big-picture d'un point de vue de Class

Pour finir, et faire comprendre au lecteur ce que permet l’excellentissime plug-in Epsilon, voici une capture « finale » d’un modèle que l’on va créer ensemble ici, pas-à-pas, en usant d’assistance pour créer les colonnes ids, la classe ProduitCategorie ainsi qu’une relation m:m sur deux. Ce sera plus clair par la suite, vous verrez.

Simple modèle d'exemple

Ici, nous avons définis deux types d’objets que sont « Produit » et « Catégorie », nous avons dit que les deux possèdent une colonne nommée « id » (qui s’inscrit dans une perspective SGBDR donc) et qu’ils sont tous deux liés par une relation « ManyToMany », ou « plusieurs-à-plusieurs » comme dit plus haut (perspective « objet »).

Maintenant, pour aider dans la conception de (méta-)modèle, Epsilon fournit tout un ensemble de langages permettant d’écrire des outils de transformation, validation, d’assistance, de mélange, entre autres, qui sont utilisables ensuite par la personne créant un modèle.

Ici, et pour l’instant, j’ai créé trois outils d’assistance :

addIdentifierAsPrimaryKeyToClass pour les EClass Class, qui permet d’ajouter une colonne « id » à une classe en particulier ou à toutes les classes du modèle, si ces colonnes n’existent pas bien sûr (pas de doublon, vérification d’existence par condition),

createManyToManyReferenceClass pour les EClass ManyToMany (spécialisation de la EClass abstraite Relation), qui permet d’aider à créer une classe de cross-référence entre deux classes (sans doublon aussi, évidement) en ayant seulement précisé l’élément relaté par la relation m:m,

setClassPluralAndSingularProperties pour les EClass Class, qui permet de spécifier automatiquement les propriétés « plural » et « singular » qui sont de type EString et qui permettent de préciser les formes plurielle et singulier d’une EClass Class.

Allons dans le détail, et commençons simplement par le dernier qui est le plus simple :

Je créé un fichier modèle de mon méta-modèle (j’ouvre mon méta-modèle puis clique-droit sur l’EClass « Model » et « Create Dynamic Instance », je séléctionne le fichier qui supportera l’instance du modèle)

Première étape : créer une instance du modèle

puis je l’enregistre (n’importe où, peu importe).

Seconde étape : créer une classe d'objet

Vous remarquez que j’ai nommé mon « Model » avec la chaîne de caractère « EspaceDuSommeil ».

Une fois que j’ai cliqué, je me retrouve avec un « Model » ayant un « enfant » (child) de type Class qui ne porte pas de nom.
En cliquant dessus la fenêtre du bas nommée « Properties » (que l’on voit dans l’image ci-dessus en bas qui permet de voir « EspaceDuSommeil ») affiche les propriétés relatives à cet élément, ainsi il me permet de cliquer sur la propriété « Name » et d’y définir le « nom » de mon objet « Class » (si vous remontez jusqu’à la première image, vous remarquerez la relation d’héritage entre Named et Class, qui fait que Class « is_a » (est un) Named, Named voulant dire qu’il est nommé -j’aurai pu mettre Nameable pour dire qu’il peut être nommé, mais cela dépend des besoins, ici il me faut absolument un nom-). J’y saisi donc le mot « Produit », et voici :

Troisième étape : saisir le nom du type d'objet

On fait de même pour créer un type d’objet nommé « Catégorie », puis on va s’occuper de spécifier les propriétés « plural » et « singular » en cliquant seulement sur un petit bouton, grâce à mon script écrit en EWL, langage définit par le plug-in Epsilon :

Quatrième étape : cliquer sur l'assistant pour automatiquement définir les propriétés "plural" et "singular"

Remarquez que les champs « Plural » et « Singular » de la fenêtre de propriété sont vides. Pour l’instant.

Cinquième étape : se fouler le doigt en cliquant

Et tadam ! Voilà le résultat ! Notez qu’il n’y a qu’ajout d’un « s » final pour créer la forme plurielle. La forme singulière est une copie du nom de la classe, simplement. Après, c’est open-source, donc qui veut modifier le comportement n’a qu’à apprendre et modifier selon ses nécessitées.

Pour comprendre, pour qui voudra, voici le script qui permet de réaliser cela :

wizard setClassPluralAndSingularProperties {

guard : self.isTypeOf(Class) and self.name.size() > 0

title : ‘Set plural and singular properties of ‘ + self.name

do {
self.plural := self.generatePluralProperty();
self.singular := self.generateSingularProperty();
}
}

wizard setModelClassesPluralAndSingularProperties {

guard : self.isTypeOf(Class)

title : ‘Set plural and singular properties of classes (Model-wide)’

do {
for( class in Class.allInstances() )
{
class.plural := class.generatePluralProperty();
class.singular := class.generateSingularProperty();
}
}
}

operation Class generatePluralProperty () : String {
return self.name + ‘s’;
}

operation Class generateSingularProperty () : String {
return self.name;
}

Comme vous l’aurez sûrement remarqué, j’ai codé deux « wizards » (assistants) différents, l’un ne se rapproche qu’à la classe séléctionnée lors du clique droit, l’autre s’occupe de définir ce qu’il y a à définir poru toutes les classes (à l’aide de la boucle for(… in …){}).

Simple, non -je parle aux développeurs ici ;-D- ?

Voyons un peu plus complexe comme script, celui permettant d’ajouter une colonne « id » aux classes et qui s’utilise exactement de la même manière -séléction de la classe en question puis clique-droit->wizards-> »add Id… for XXX » ou [..]model-wide »:

wizard addIdentifierAsPrimaryKeyToClass {

guard : self.isTypeOf(Class) and not self.column.exists(c|c.name.matches(‘id’))

title : ‘Add an Id column as primary key to ‘ + self.name

do {

self.column.add( self.generateIdPKUnikColumn() );
}
}

wizard addIdentifiersAsPrimaryKeyToClasses {

guard : self.isTypeOf(Class)

title : ‘Add an Id column as primary key to classes (Model-wide)’

do {
for( class in Class.allInstances() )
{
if( not class.column.exists(c|c.name.matches(‘id’) ) )
{
class.column.add( class.generateIdPKUnikColumn() );
}
}
}
}

operation Class generateIdPKUnikColumn () : Column {
var idcolumn : Column;
idcolumn := Column.createInstance();
idcolumn.name := ‘id’;
idcolumn.primary := true;
idcolumn.unique := true;
var idtype : Model!Integer;
idtype := Model!Integer.createInstance();
idtype.max = 20;
idcolumn.type = idtype;
return idcolumn;
}

Et voilà !

Sixième étape : lâcher de clique pour ajout de la colonne 'id' à toutes les classes

Tadam :

Septième étape : apprécier le résultat :-D

Maintenant, pensez au nombre de manipulations gagnés lorsqu’il y a 10,50,100 classes ? 🙂
Les affictionados de Doctrine me diront : imbécile, Doctrine n’a pas besoin qu’on spécifie la colonne id ! Oui, certes, mais ce n’est qu’un exemple d’utilisation ;-P

Voyons autrement plus complexe, la création en un clique d’une classe de référence pour une association m:m :

Code:

wizard createManyToManyReferenceClass
{
guard : self.isTypeOf(ManyToMany) and self.relates.isDefined()

title: ‘Create ManyToMany\’s Reference Class between ‘ + self.owner.name + ‘ and ‘ + self.relates.name

do
{
self.owner.definedIn.defines.add( self.createReferenceClass() );
}
}

operation ManyToMany createReferenceClass() : Class {
self.name := self.relates.name;

–define variable container for reference Class of ManyToMany.refClass assigned to both ManyToMany sides
var refClass : Class;
refClass := self.owner.definedIn.defines.selectOne(c|c.name.matches(
self.owner.name.firstToUpperCase() + self.relates.name.firstToUpperCase()
)
);
if( not refClass.isDefined() )
{
refClass := Class.createInstance();
refClass.name := self.owner.name.firstToUpperCase() + self.relates.name.firstToUpperCase();
}

–defines self.owner refClass reference
self.refClass := refClass;

–define variable containers for columns pks
var a : Column;
var b : Column;
a := Column.createInstance();
b := Column.createInstance();
a.name := self.relates.getIdColumnName();
b.name := self.owner.getIdColumnName();
a.primary := true;
b.primary := true;

–define variable containers for OneToMany holders in refClass
var aR : OneToMany;
var bR : OneToMany;
aR := OneToMany.createInstance();
bR := OneToMany.createInstance();
aR.relates := self.relates;
bR.relates := self.owner;
aR.name := self.relates.name;
bR.name := self.owner.name;

–add columns and relations to refClass
refClass.column.add(a); refClass.column.add(b); refClass.relation.add(aR); refClass.relation.add(bR);

–add ManyToMany relation to related class
var rC : ManyToMany;
rC := ManyToMany.createInstance();
rC.owner := self.relates;
rC.name := self.owner.name;
rC.refClass := refClass;
rC.relates := self.owner;
self.relates.relation.add(rC);

return refClass;
}

operation Class getIdColumnName() : String {
return self.name.toLowerCase() + ‘_id’;
}

operation Class getPluralName() : String {
return self.name + ‘s’;
}

Création d'une relation m:m entre Produit et Catégorie

Je définie comme nécessaire la propriété « relates » qui me permet d’appeler par la suite le script d’automation :

Définition de la classe relaté par la propriété m:m de la classe Produit comme étant Catégorie

Puis j’appel le script qui s’occupe de tout définir pour moi, pour autant que cela me convienne :

Dernière étape : cliqueeerrrrr

Appréciez VOTRE travail :

La classe de cross-référence est créée automatiquement, la classe cross-référencée Categorie est automatiquement modifiée en conséquence ainsi que la classe Produit et leurs relations m:m en regard

La m:m de Categorie/Produit automatiquement créée

La classe de cross-référence entre Produit et Categorie automatiquement créée et ses propriétés

Tiens, j’ai oublié de coder l’ajout des formes plurielles et singulières, qu’à cela ne tienne, on modifie le code et c’est repartie !
En modifiant le script, je me rend compte que j’ai oublié de faire pas mal de checks sur l’existence d’élements, ce qui implique que si on exécute deux fois cet assistant, il crééra d’autant de classes de référence, c’est un comportement non désiré : hop on modifie en conséquence :

wizard createManyToManyReferenceClass
{
guard : self.isTypeOf(ManyToMany) and self.relates.isDefined()

title: ‘Create ManyToMany\’s Reference Class between ‘ + self.owner.name + ‘ and ‘ + self.relates.name

do
{
self.owner.definedIn.defines.add( self.createReferenceClass() );
}
}

operation ManyToMany createReferenceClass() : Class {
self.name := self.relates.name;

–define variable container for reference Class of ManyToMany.refClass assigned to both ManyToMany sides
var refClass : Class;
refClass := self.owner.definedIn.defines.selectOne(c|c.name.matches(
self.owner.name.firstToUpperCase() + self.relates.name.firstToUpperCase()
)
);
if( not refClass.isDefined() )
{
refClass := Class.createInstance();
refClass.name := self.owner.name.firstToUpperCase() + self.relates.name.firstToUpperCase();
}

–set plural and singular forms for refClass
refClass.plural := self.owner.getPluralName() + self.relates.getPluralName();
refClass.singular := self.owner.name + self.relates.name();

–defines self.owner refClass reference
self.refClass := refClass;

–define variable containers for columns pks
var a : Column;
var b : Column;
var aToAdd: Boolean;
var bToAdd: Boolean;

aToAdd = false; bToAdd = false;
–try to get existing columns
a := refClass.column.selectOne(c|c.name.matches(self.relates.getIdColumnName()));
b := refClass.column.selectOne(c|c.name.matches(self.owner.getIdColumnName()));

–if none exist, create them and mark watchdogs for further add
if( not a.isDefined())
{
a := Column.createInstance();
a.name := self.relates.getIdColumnName();
aToAdd := true;
}
if( not b.isDefined())
{
b := Column.createInstance();
b.name := self.owner.getIdColumnName();
bToAdd  := true;
}
a.primary := true;
b.primary := true;

–define variable containers for OneToMany holders in refClass
var aR : OneToOne;
var bR : OneToOne;
var aRtoAdd: Boolean;
var bRtoAdd: Boolean;
aRtoAdd := false; aRtoAdd := false;
–try to get existing relations
aR := refClass.relation.selectOne(r|r.name.matches(self.relates.name));
bR := refClass.relation.selectOne(r|r.name.matches(self.owner.name));

–if none exists, create them and mark watchdogs for further add
if( not aR.isDefined()){
aR := OneToOne.createInstance();
aR.name := self.relates.name;
aRtoAdd := true;
}
if( not bR.isDefined()){
bR := OneToOne.createInstance();
bR.name := self.owner.name;
bRtoAdd := true;
}
aR.relates := self.relates;
bR.relates := self.owner;

–add columns and relations to refClass only if watchdog says to do so
if(aToAdd){ refClass.column.add(a); }
if(bToAdd){ refClass.column.add(b); }
–relations
if(aRtoAdd){ refClass.relation.add(aR); }
if(bRtoAdd){ refClass.relation.add(bR); }

–add ManyToMany relation to related class
var rC : ManyToMany;
var rCtoAdd: Boolean;
rCtoAdd := false;
rC := self.relates.relation.selectOne(r|r.name.matches(self.owner.name));
if(not rC.isDefined())
{
rC := ManyToMany.createInstance();
rC.name := self.owner.name;
rCtoAdd := true;
}
rC.owner := self.relates;
rC.refClass := refClass;
rC.relates := self.owner;

–add relation to refClass only if needed
if(rCtoAdd){ self.relates.relation.add(rC); }

return refClass;
}

operation Class getIdColumnName() : String {
return self.name.toLowerCase() + ‘_id’;
}

operation Class getPluralName() : String {
return self.name + ‘s’;
}

j’ai mis en gras autant que j’ai pu les différences.

Et voilà, on aura beau lancer plusieurs fois de suite l’assistant, il ne crééra pas plusieurs classes de références poluantes 😉

On finira avec une capture d’écran de la classe de cross-référence :

OneToOne relation de la classe de référence entre celle-ci et Produit

« Et voilà ! », il ne reste plus qu’à écrire un transformateur de modèle à modèle (EGL, Epsilon Generative Language)!

Mais avant ça, il sera bien utile d’écrire un validateur de modèle (EVL, Epsilon Validation Language) !

Comme vous l’aurez compris, le tout pourra être écrit à l’aide du plug-in Epsilon !

Alors, le développement dirigé par les modèles, C’EST PAS DE LA BALLE ? 😀

Le prochain billet (« Les Epsilons endoctrinés génèrent pour se libérer ») portera sur la génération de code : transformer le modèle dans sa syntaxe abstraite en un fichier concret (« ./config/doctrine/schema.yml »), qui, on le verra, sera utilisé par sfDoctrine/Symfony pour générer des classes Php5 de liaison Objet-Table Relationnel, mais aussi, dans la même perspective, qui permettra de générer des outils de gestion type CRUD en quelques lignes de commandes (voir symfony-project.org et son tutoriel Jobeet pour Doctrine).

Enfin je tiens à remercier particulièrement d’abord les « teams » Eclipse, EMF et GMF, entre autres et surtout Dimitris Kolovos qui est l’initiateur de l’outil Epsilon. Sans quoi tout ça ne serai possible !

N.B.: la réalisation du méta-modèle, du modèle d’exemple, des scripts et de ce billet ont nécessités un peu plus de 4h.
Pensez à relativiser le temps pris lorsqu’on connait l’outil, que le transformateur modèle->code (schema.yml), le validateur de modèle et autres assistants seront réalisés quant à la réalisation d’un schéma complexe pour sfDoctrine…
Relativiser tout ça quant à l’écriture d’applications web via l’ingénierie par les modèles (perte de temps dans la résolution de bugs, d’écriture du code en suivant les règles de coding, etc). C’est tout simplement monstrueux comme capitalisation (je parle bien d’un capital HUMAIN, pas spéculatif).

A bon entendeur 😉

(copyleft until credits) Texte et images Stéphane Erard.

Salut !

Ce petit fil pour vous proposer une idée de l’utilité futur des modèles ! Ces petites bêtes qui harcèlent mes potes =)

Je vais essayer de reprendre tout depuis le début.

Background technique

Systèmes et modèles

On peut considérer un modèle comme étant la représentation « dirigée » d’un système (réel ou virtuel).

Un système peut être représenté par un ensemble de modèles, chacun représentant une certaine perspective du système. En ça un modèle est « dirigé ».

Par exemple dans un système d’information d’entreprise il y a un système de gestion de bases de données relationnels généralement (le SGBDR ou RDBMS ou DB) ou toutes les instances des concepts (du modèle) de l’entreprise sont stockés.
Petit rappel, une instance est la représentation de la chose, et non sa classification. Si il y a un concept Voiture, votre voiture est représentée par une instance du classifieur Voiture (c’est une « classe » dans le paradigme « objet »). Du coup, pour « correspondre » au modèle « relationnel » des sgbdr, on dissocie la « classe » en « table ». Ainsi, un Concept est conceptualisé en classe et est enregistré sous forme de tables relationnels. On a ici 3 perspectives différentes d’une seule et même chose. Parce qu’on en parle avec des perspectives différentes, dans un but différent. C’est complètement « contextuel ».

On peut parler « en gros » d’une chose, puis « dans le détail ». C’est exactement « le tout et ses parties ». Quand on regarde dans le détail, on se « concentre » sur une seule partie de l’ensemble, ce qui ne doit jamais faire oublier qu’il faut, dans ce même temps, prendre en compte ses inter-actions dans son contexte d’existence, les autres parties et ce tout qu’il aide à faire naître.

Reprenons. Le SGBDR a une structure qui représente le modèle du système d’information dans sa perspective « relationnel ». Concrètement, ici on ne parlera pas des ordinateurs ni de classes, on parlera de tables, de colonnes et d’indexs.
Il y a aussi le modèle topologique du SIE, c’est à dire sa répartition en sites en tant que conteneurs et les nœuds de réseaux (ordinateurs, etc) en tant que contenus (c’est un modèle possible, peu riche mais illustratif).
Il y a pleins d’autres modèles, chacun représentant une perspective d’une système à un degrés apprécié au besoin.

Chaque modèle prit à part permet de diviser la vision mentale du problème pour mieux diviser le problème.
Mais lorsqu’on réunit tous ces modèles, nous avons le système d’information dans sa globalité.
On peut alors construire un outil permettant de naviguer entre les modèles de l’entreprise selon sa propre perspective (ou l’une auxquelles ont a accès) en tant qu’utilisateur.

On dit qu’un modèle est une abstraction d’un système (on se concentre sur une perspective du problème -dirigé- en oubliant volontairement les autres perspectives) en ce sens qu’on se concentre sur une perspective du système à représenter, selon un domaine précis (qui forme la perspective), et non sur la totalité du système.
Un système nécessite souvent plusieurs points de vues. En architecture, il y a le point de vue de l’électricien, du domoticien, du vitrier, du plombier et l’architecte qui tend à avoir la vision la plus globale et aussi la plus détaillée, en tout points des modèles du système. Dûr d’être architecte ! Heureusement pour eux, les architectes du bâtiment, la plupart des éléments sont industrialisés. Alors bien sûr ça fait des trucs pas beaux, mais c’est un autre problème. Dans le monde virtuel on a contrôle sur tout. Et sur encore plus de choses pourvue qu’on ait le code source.

Nous voyons donc qu’un système peut être représenté par plusieurs modèles, chacun étant une perspective du système. Mais un modèle, c’est quoi ?

Qu'est-ce qu'un modèle par rapport à un système ?

Qu'est-ce qu'un modèle par rapport à un système ?

Modèles, métamodèles, métamétamodèles.

Un ensemble de modèles représentent un système. Si on considère un modèle comme un système, on arrive à se dire qu ‘il y a un modèle représentant le modèle. Et ainsi de suite.
On arrive alors à se dire qu’il y a un modèle qui établit un langage qui définit lui même un langage qui définit lui même un langage qui définit lui même …. Mais il faut bien s’arrêter à un moment.

Ainsi l’OMG a aidé à définir MOF, Meta Object Facility qui officie comme étant le langage de plus bas niveau dans la hiérarchie des méta langages (suivant la perspective, c’est le plus haut). Il s’appel aussi M3, car c’est le méta-méta-modèle (MMM).

Du côté Eclipse de la force, MOF est implémenté par Ecore, partie intégrante du projet Eclipse Modeling Framework (EMF).

Le monde réel et ses modèles

Le monde réel et ses modèles

Nous avons donc un système représenté par un modèle qui est conforme à un méta modèle qui est lui même conforme à un autre méta méta modèle, lui même conforme à lui-même (dans la plupart des cas, 4 méta-niveaux suffisent, en fait j’en connais pas encore ou ce n’est pas suffisant ou trop).

Concrètement, prenons pour exemple un système application php type web, servi par apache.

On peut dire dans ce cas de figure que le système est l’ensemble en cours d’exécution. C’est le système d’exploitation du serveur plus le apache binaire et le php ainsi que le code php et la base de données. Concentrons-nous (faisons abstraction du reste) sur la perspective application métier faite en php pour l’exemple. Ici le code php5 est un langage de programmation. Nous avons donc un méta-modèle Php5 nous permettant de nous exprimer dans ce langage (dans sa forme abstraite). Ce métamodèle s’exprime lui même en MOF, le méta-méta-modèle qui lui-même s’exprime de lui-même. Il se conforme à lui même.

Retour

Les modèles, le nouveau code source

On sait qu’un méta-modèle permet d’exprimer la forme abstraite d’un langage. En exemple le méta-modèle de Php5 nous dit ce qu’on peut faire en Php5 : créer des classes, des interfaces, des fonctions, des directives opérationnels (appels de méthodes de classes) ou fonctionnels (opérateurs logiques – <,<, =, ==, ===, … – ou appels de fonctions). A partir d’une instance de ce méta-modèle, un modèle, on peut générer du code Php5, à l’aide d’un générateur de code qui parle le même langage : son code généré repose sur le même méta-modèle. De fait, un modèle Php5 se conforme au métamodèle Php5 (lui même se conformant à MOF). Un modèle Php5 valide permet la génération et l’exécution de code sur une plateforme Php5. D’un point de vue MDA, le méta-modèle Php5 est un modèle cible (PSM, Platform Specific Model). C’est à dire que ce modèle est destiné à être utilisé dans des transformations de modèle à texte en bout de toolchain.

On peut alors permettre la diffusion de modèles à n’importe quel niveau, celui approprié : soit au plus « bas » tel que Php5 ou Java; ou on peut en fournir de plus haut niveau comme un model Business (qui serai complètement indépendant du reste, et n’impliquerai que des requis type « il me faut une base de données relationnel », « il me faut un espace disque », etc).

Les métamodèles, le nouveau langage ?

Lorsqu’on créé un méta-modèle, il est admis qu’on créé un langage. On modélise un langage.
Typiquement, le langage Php5 peut être modélisé en Ecore (implémentation du MOF2.0 de l’OMG par l’équipe d’Eclipse EMF Project). UML est aussi modélisé en Ecore. Ecore lui-même est un langage qui s’exprime lui-même (il est auto-reflexif, « la langue parle de la langue »).
Comment ça marche ? Toute l’intelligence se trouve dans le client Eclipse et son plugin EMF. Elle est axiomatisée, formant une logique. Et puisque c’est une logique, il faut la comprendre pour en jouer.
Ainsi toute la créativité se trouve dans les méta-modèles : ces fichiers d’extension « .ecore ».
De ce point de vue, c’est un peu comme un navigateur qui est une application exécutable qui permet le rendu du DHTML, qui permet la créativité, accessible sur Internet ou dans le système de fichier de l’ordinateur. Et on voit ce que ça donne, en terme d’applications : gmail, facebook, etc. Tout ça grâce à bien des choses, mais notamment grâce à la séparation client/serveur en terme d’interface et de contrôle, impliquant la nécessité d’une application lourde (pour dire qu’il faut un exécutable binaire s’exécutant sur le système d’exploitation de l’utilisateur) côté client pour gérer l’interface utilisateur qui est définit dans un standard nommé HTML. Après c’est une question d’implémentation des standards. Certains implémentent le HTML 1.1, d’autres le 3 et le 4.
Les standards ont aussi permis d’implémenter une bonne division des perspectives du code de l’application client léger (celle faite en HTML) pour séparer le côté définition de l’affichage (emboitement des conteneurs de texte et autres controles) et définition de son style (emboitement des styles à appliquer aux conteneurs selon leurs classes ou leurs identités, CSS). Mais il reste plus sympa de se rendre compte que tout ça peut être factorisé en Javascript -ECMAScript 4-, au travers d’un cadre applicatif adapté type GEARS ou Qooxdoo.

En ce sens, les navigateurs peuvent être perçus comme des méta applications basiques (sans cadre applicatif ou intégration particulière) ou complexes (avec cadre applicatif ou intégration particulière dans le système d’exploitation), permettant une intégration normal (typique web 1.0 avec de simples liens compris par le système d’exploitation *.url par exemple) ou avisée (permissions de lectures/écritures sur le disque à certains endroits prédéfinis et accessibles uniquement à l’utilisateur en cours, accès à des périphériques médias type caméra, meilleure gestion des connexions, etc) avec le système d’exploitation hôte.
De nos jours, GEARS implémente un bon début de HTML 5 (Qooxdoo est derrière à mon sens), qui permet justement de rapprocher l’espace vide entre une application lourde et une application légère type web x.0 (via un simple lien/raccourci), ou plus précisément web « 2.0 » avec une intégration particulière plus poussée sur le système d’exploitation (type GEARS qu’il faut installer au préalable, comme .Net). Côté serveur, c’est grâce aux technologies réseaux : distribution de la charge en tout point mais aussi grâce à l’implémentation logiciel, par exemple mysql et sa gestion des réplications que ce type d’architecture logiciel offre un grand avantage : un même code peut être configuré pour une utilisation mono-poste mono-utilisateur et changé en peu de temps (celui de la migration) en multi-poste multi-utilisateur. Le tout en ayant tout le code utilisé à sa réalisation car tout est open source. Vous pouvez ainsi tout modifier selon vos besoins. Le code est capitalisé (en votre possession, avec droits de modifications impliquant souvent devoir de remettre le code source, remettre dans le tronc commun) et la formation « académique » est distribuée. Mais on s’égare un peu (je ne veux pas politiser ce débat, réservant ça à un prochain petit fil).

Concrètement, quand on créé un méta-modèle, généralement, on créé aussi des générateurs de code et/ou des transformateurs/mélangeurs vers d’autres méta-modèles (selon qu’il soit source ou cible).
Prenons un exemple. Lorsque je choisis de conceptualiser le méta-modèle de Php5 (cible) et de Symfony (source), j’ai le choix : soit Php5 et Symfony sont complètement autonomes, soit Symfony « étend » Php5.
Etant donné que Symfony est un langage « instance » du langage Php5 (comprenez que Symfony est un ensemble de classes Php5, lesquelles peuvent être définis dans un modèle « Symfony.phpmodel » qui a pour méta-modèle Php5), il peut être compréhensible que Php5 et Symfony soient complètement autonomes l’un de l’autre, découplés.

On peut aussi se dire que l’on veut, dans le langage Symfony  (quand on « parle » Symfony de même qu’on parlerait « voiture » entre potes, ou que l’on parle Thieli ou Hades chez Orange/S.-A.) aussi parler Php5, par exemple ajouter une méthode de classe à la classe « Actions »(php5::Class::Extensions::Action ? Php5::Action ? Symfony::Action ? Que faire ?) d’une classe « Module » (idem, même problématique). Ce qui est normal aussi, puisque Symfony est une instance du langage Php5. On peut s’y prendre de bien des manières, mais il ne faut jamais perdre de vue la « sémantique » véhiculée par un méta-modèle !
Si on déconnecte Symfony de Php5, on perd tout l’intérêt du méta langage : on déconnecte sémantiquement le langage Symfony du langage Php5. Pourtant, un Symfony::Module est une Php5::Class !?

Alors, mapping ou extension ?

Dans tous les cas, il faut que le modèle Php5 soit « complet » : c’est à dire qu’il comprenne la définition abstraite (métamodèle) et concrète (générateur de code, m2t, t2m) de tout le jeu d’instructions Php5 : des classes en passant par les exceptions jusqu’aux boucles for, constructs internes ou « user defined » (en fournissant un modèle de base décrivant toutes les fonctions et classes internes de Php5, dans un fichier standards.php5model type, comme le fait UML et Java. C’est une méthode !). Ainsi on peut créer un vrai engeenering round-trip modèle<->code  :une modification du modèle modifie le code, une modification du code modifie le modèle. Il y a une corrélation totale entre le code et ses modèles l’ayant « construit », par transformations. Mais pour arriver à ça, il faut créer tout plein d’outils qu’on appel des workflows : ils permettent de réaliser des « toolchain » spécifiques aux besoins. Par exemple générer les entités seulement ou seulement générer le code sql pour la création de la structure des bases de données relationnels. Mais revenons un peu plus en arrière.

Si on fait une extension du méta-modèle Php5 pour créer le méta-modèle Symfony, il faut garder à l’esprit que l’on créé une identité, un type : une méta-classe qui étend une autre méta-classe. De ce point de vue, la métaclasse Action qui étend la métaclasse Php5::Class est une Php5::Class (sic!). On vera alors un éditeur de modèle Symfony proposer la création de Php5::Class, Php5::Interface ou encore Symfony::Action !
En un clic, on a créé un éditeur dédié à Symfony et générique au langage d’instance, Php5. C’est comme la langue française et le langage d’un mécanicien parlant français.

Si on fait un mapping, les langages sont déconnectés l’un de l’autre. La « connection » ne s’effectue plus au niveau sémantique des méta-modèles mais dans une transformation de modèle à modèle, la plupart du temps, le code étant plutôt une cible technologique. Plus sûr, il y a aussi les modèles de mappings ecore.

Résumons : nous avons les moyens de créer des langages s’exprimant au travers de modèles (« la langue permet de parler de la langue », valable pour la langue française, mais pas pour toutes les langues !) et permettant des transformations en d’autres modèles ou d’en générer du texte, mais aussi de mapper les méta-modèles entre eux.

Finalement, si les utilisateurs de mon application (de modèle) ont sur leur machine un certain outil (qui serait « standardisé », type gnu toolchain puis encapsulé type aptitude de debian ou PEAR de Php) installé, ils n’ont qu’a télécharger le modèle de l’application et la « compiler ». Dans l’esprit GNU Toolchain, on télécharge le code source, puis on créé une configuration en rapport à son propre contexte utilisateur (os, compilateurs installés, librairies utilisables, bref, on parle système d’exploitation type *nix, c’est un domaine, donc un langage) et enfin on tape la commande magique : make && make install, qui a pour effet de compiler l’application et de l’installer. C’est le multi-click du Windows en mode install.

Comme dans la gnu toolchain, le code est généré après l’édition et la validation d’un modèle (instance du méta-modèle Configuration d’Application) de Configuration (type make menuconfig) confrontant le système d’exploitation et sa Configuration actuelle (instance d’un méta-modèle de Configuration de Système d’Exploitation, par exemple) aux requis de l’application à installer. On connait tous les mérites qu’apportent cette méthodologie réputée (./configure; make; make install). C’est redondant, mais bien que je parle de la même chose, je ne l’exprime pas de la même manière. Il y a tant d’angles d’attaques « morts », pour l’instant.

Ainsi, les modèles sont le renouveau du code source : distribuables, compréhensibles parce que auto-documenté par leurs méta-modèles et vérifiables parce que contraints par des descripteurs de règles de validation, réflexifs, ouvrant la voie à de meilleurs applications.
Sans parler des facultés de MOF2.0 et d’Ecore, permettant l’import de méta-modèles permettant une meilleure utilisation des ressources. Mais il y a mieux, le MOF2.0 Versioning qui gère la versionnalisation des modèles.
La version d’un modèle devient ainsi le point d’entrée du modèle d’une application.
Sans parler de tous l’outillage réalisable (et réalisé, notamment au travers d’Epsilon, merveilleuse boite à outils) en utilisant les plugins Eclipse et les extensions d’EMF et des projets liés tels que GMF, GEF, SDO, TEXIO, etc.

Voilà la « modeling toolchain » : modélisation, transformation/merging, génération d’artefacts (fichiers exécutables, de configurations, scripts de déploiements, …) et ça s’exécute. A condition qu’il y ait un cadre applicatif de modélisation et d’exécution reposant sur un même jeu de concepts !

En développant de nouveaux langages (méta-modèles, transformateurs et/ou générateurs, avec leur validateurs, puis éditeurs et modéleurs graphiques) et des outils de transformations entre les langages nouveaux et les plateformes technologiques existantes (pour commencer!), on est à même de rendre l’informatique « compréhensible » par Tata Jeannine, capitalisée par l’écriture d’applications qui créent des applications, auto-documentés par les modèles.

De la compréhension du phénomène méta à la création d’outils créateurs

Quand on comprend l’utilité de ces outils, on voit vite ce qu’on pourrait en faire :

Par exemple créer un langage de haut niveau qui permette de faire des choses type « je veux une appli web, qui me permette de gérer ce schéma, qui me permette de réaliser telles actions, qui m’affiche les choses ainsi, etc ».
Puis par des transformations, mélanges et générations on arrive à un résultat final.

Il y en a qui diront « oui mais c’est stéréotypé ». Beh ça dépend comment on s’y prend.

Par exemple, on va utiliser l’architecture « client/serveur » qu’on va implémenter à l’aide des standards du « W3C », on va utiliser côté client (le « navigateur », firefox, etc) un framework (Qooxdoo en Javascript est à mon sens le meilleur), côté serveur idem (Symfony en Php5, pluggé à Doctrine pour l’ORM qui sont à l’heure actuel, à mon sens les meilleurs, bien que Flow3 va arriver enfin documenté -c’est un énorme souhait de ma part!-).
Ainsi on peut générer du code à destination des frameworks là ou ils agissent : on réutilise du code, des librairies. Par exemple l’indexExecute (qui exécute la méthode « index » d’un « module » dans le langage « symfony ») redirigera vers le « js » écrit en qooxdoo (on pourra même spécifier par configuration à qooxdoo-build de générer le source ici, la release là, etc), et c’est lui qui génèrera les prochaines requêtes, en tant qu’application web exécutée côté client (xmlhttprequest ou iframe caché etc) -une vraie application écrite en qooxdoo-. Bref chacun est expert de son domaine et l’utilisation des modèles et des générateurs et mélangeurs permettent de créer les artefacts nécessaires sans réinventer la roue. Limite glue-model. Il n’y à qu’à regarder la ressemblance entre fichier yaml et fichier de déclaration. Les développeurs tendent à permettre aux développeur de « déclarer » tout et n’importe quoi n’importe comment. Ce n’est pas plus mal, c’est simplement un effort du « style déclaratif » qui est dû à l’effet d’encapsulation par problématique des frameworks.

Au final oui le code génère une application type, mais elle est entièrement modifiable. Et il nous appartient de créer des modèles de configurations pour chaques éléments, découplés tous ces phénomènes au travers d’entités distinctes (pas de conteneurs, juste des réferences).

Il appartient ensuite lors du merging de vérifier l’intégrité des modèles (qu’il y est bien un modèle objet, avec un modèle de conf pour chaque objet, etc).

Mais juste une chose. Quand on parle de « modèle », on parle bien de manière conceptuel. Physiquement, un modèle peut être explosé en plusieurs fichiers, plusieurs versions, etc. Grâce à XMI et donc XML.

Bref, on se créé ses propres outils, selon nos besoins.

Pour faciliter le développement des modèles ecore j’ai réalisé un petit wizard Epsilon qui permet de définir les propriétés des EPackages d’un modèle ecore, d’un simple clique.

Voici le code :

wizard setNestedPackagesInfos {

guard : self.isTypeOf(EPackage)

title : ‘Set properties of nested packages’

do{
for(package in EPackage.allInstances() ){
package.nsPrefix := package.name.toLowerCase();
package.nsUri := package.generateNsUri();
}
}
}

operation EPackage generateNsUri () : String {
var ret : String;
if(self.eSuperPackage.isDefined())
{
ret := self.eSuperPackage.generateNsUri() + ‘/’ + self.name;
}
else
{
ret := self.nsUri;
}
return ret;
}

C’est aussi simple ! Ne reste plus qu’à en faire un plugin pourquoi pas ? Mais j’arrive pas à exporter 😦

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 !