À propos de l'experience et de mes motivations. 1000 excuses, c'est pas super bien écrit et il manque plein d'accents, je suis au boulot je peux difficilement passer plus de temps sur ce pavé).
J'ai malgré moi dû me convertir au developpement web il y a quelques années pour manger (je ne suis pas vraiment developpeur). J'ai découvert des horreurs (PHP) et des technos super cool (MODx—meme si c'est du PHP, et Javascript/Node.js) qui m'ont vraiment éclaté.
Ça faisait longtemps que je voulais faire quelque chose avec les nouvelles technos client/serveur completement en Javascript, et Meteor s'est imposé comme la techno que je voulais utiliser. J'avais testé Express (autre framework pour Node.js) que j'avais trouvé fascinant mais qui m'avait vite decouragé, parce qu'il y a énormement de travail de plomberie avant d'acceder au code metier. Meteor n'a pas ce defaut: il permet vraiment d'attaquer directement la partie qui importe (et pour un programmeur, la partie juteuse du projet, celle qui motive).
Sur les difficultés rencontrées: d'abord je suis une merde en HTML/CSS et c'est vraiment un truc qui me rebute (et ça se voit plutot pas mal dans ce projet). Autant le reste du code était un bonheur à écrire, autant j'ai eu l'impression de perdre des heures à le connecter pour en faire une interface utilisateur. Pour le coup ce n'est pas de la faute de Meteor, qui simplifie enormement la tache en proposant une version adaptée de Handlebar.js.
Les concepts de Meteor sont un peu difficile à appréhender au debut. Il laisse beaucoup de liberté pour organiser son projet comme on veut, et ça peut vite devenir le bordel (ce n'est pas un framework). Le principe des publications/inscriptions, très puissant, est aussi un peu difficile à comprendre au debut. Dans l'idée, le serveur met a disposition du client des informations de la base de donnée. Le client a sa propre base de donnée et son propre systeme de base de donnee, minimongo—un mongodb simplifié. Du coup, il faut generalement refaire les mêmes requêtes côté client et côté serveur. Au debut on a l'impression que c'est redondant et peu efficace, mais c'est un chouilla plus subtil.
Les publications (la mise a disposition d'information côté serveur) permettent de sécuriser un peu ce que l'utilisateur peut voir sur le site en fonction du contexte. Par exemple, dans mon projet il y a différentes publications qui mettent à disposition des informations provenant de la meme base de donnée. Ainsi, lorsqu'un client est sur la page d'accueil, il a acces a son propre document (son propre profil), à la liste de ses amis (une version tres light des profils, surtout pour recuperer le nom et la photo de profil), à ses amis en ligne (qui intersecte necessairement avec la liste de ses amis). Vous pouvez le testez vous meme: allez voir la demo et tapez Meteor.users.find({}).fetch() pour voir quels infos de la base Meteor.users vous pouvez voir. Du coup, côté client il faut organiser ces informations et refaire des requêtes pour séparer ses amis de soit-même lorsqu'on manipule les informations.
Si je visite le profil de quelqu'un, le contexte change et une publication me donne les informations pour ce profil.
En gros, on fournit au client les données et la logique, et il se demerde pour faire le rendu. Si vous voulez jouer, tester Collection.find({}).fetch() dans la console, em remplacant Collection par, au choix, Friends, Messages, Conversations, Notifications... pour voir quels informations sont dans votre BDD.
Maintenant, lorsqu'un document est ajouté/retiré/mis-à-jour dans la base de donnée, et que je suis inscrit a une publication qui diffuse ce document, ma mini-base de donnée côté client est automatiquement mise-à-jour. C'est là ou ca devient un peu compliqué: il y a tout un système de dépendence entre les Templates (bout de DOM utilise pour generer le rendu) et la base de donnée. En gros, si j'ai une Template qui utilise des informations de la BDD, Meteor va décider ou non de refaire le rendu de la Template, de la mettre a jour ou de ne rien faire lorsque la BDD est mise-à-jour. C'est de là que viens la réactivité, mais ça peut aussi etre terriblement chiant. Par exemple pour la création de profil, quand on clique sur "Save", le document de l'utilisateur est modifié , et ca devrait donc refaire le rendu de la page, puisque les champs de formulaire dependent de la valeur du profile utilisateur si elle existe. Du coup il m'a fallu déclarer que les informations que je récupere sont non-réactive, pour bloquer la mise-à-jour automatique de cette Template en particulier (jetez un oeil a client/views/includes/form/form_fields.js, la fonction en haut retourne la valeur a mettre pour un champ lorsqu'on génère un formulaire, notez que la dependance est bloquée en utilisant reactive: false).
Autre exemple un peu tordu qui m'a pris un peu de temps à concevoir, lors de la recherche d'utilisateur. Dans à peut prêt n'importe quel autre système, le client fait une requête du genre "Envoie moi tous les gens qui sont X et Y", le serveur fait la recherche puis renvoie une liste au client. Cette facon de faire serait tres inefficace avec Meteor, puisqu'il est très possible que certains utilisateurs soient dejà dans la base de donnée côté client, mais aussi parce que le serveur devrait retourner une liste d'utilisateur sous forme de liste qu'il faudra ensuite manipuler côté client—pourquoi faire ça alors que le client dispose de sa propre BDD? Du coup l'astuce consiste à demander au serveur de metter a jour une publication (qui, comme les templates précedemment, depend cette fois d'une variable de Session 'searchQuery'—si cette variable change, la publication est recalculée), lorsque le serveur a finit de mettre a jour la publication, il met a jour une autre variable de Session côté client ('searchQueryDone'). Côté client, j'ai une template qui depend de cette variable (et non pas des donnees, sinon ma page de recherche clignoterait a chaque fois qu'un nouvel utilisateur se creerait un profil par exemple), qui va donc se mettre a jour en utilisant les informations de la base de donnée ET refaire la requete locallement.
Là où Meteor brille, c'est qu'il s'occupe comme un grand d'optimiser tout ça, d'envoyer les bonnes informations au client, de refaire le rendu des parties concernées par les changements. Quand ça marche, c'est magique et vraiment super simple a faire fonctionner. Quand une heuristique foire (typiquement, dependance un peu severe, ou tout simplement lorsque Meteor n'arrive pas a faire un diff propre d'un bout de DOM avant/après), ca devient une super prise de tête.
Un dernier exemple pour la route, parce que ma matinee est foutue de toute facon. Imaginons que l'on veuille rajouter un nouveau widget qui affiche la liste des amis communs entre soit-meme et la personne dont on regarde le profil. La liste de mes amis est deja publie et disponible. La liste des amis de ma cible est publie lorsque je visite son profil, j'ai donc toutes les informations a disposition, rien besoin de changer cote serveur. La collection "Friends" indique que A est en relation avec B (et il y a un document de B vers A si la relation est reciproque). Bout de JS que vous devriez pouvoir tester chez vous avec firebug ou la console de Chrome:
varmyFriends=Friends.find({me:Meteor.userId()}).fetch();// mes amis a moi.// 'currentUserProfile': variable de Session contextuellement mis a jour pour indiquer quel profil on visite.varhisFriends=Friends.find({me:Session.get('currentUserProfile')}).fetch()// Ses amis a lui.varmyTargetIds=[],hisTargetIds=[];myFriends.forEach(function(f){myTargetIds.push(f.target)})// la liste des _id de mes amishisFriends.forEach(function(f){hisTargetIds.push(f.target)})// la liste des _id de ses amis.varcommonFriendsIds=_.intersection(myTargetIds,hisTargetIds);// _ -> underscore.js, propose ce genre de fonctions pratiques.// De la, on fait ce qu'on veut, par exemple:varcommonFriends=Friends.find({_id:{$in:commonFriendsIds}});// Extrait de la base de donnee les amis communs, retourne un pointeur facile a manipuler.
[^] # Re: Demo
Posté par Duncan Idaho . En réponse au journal Socialite, une web-app avec Meteor. Évalué à 10.
À propos de l'experience et de mes motivations. 1000 excuses, c'est pas super bien écrit et il manque plein d'accents, je suis au boulot je peux difficilement passer plus de temps sur ce pavé).
J'ai malgré moi dû me convertir au developpement web il y a quelques années pour manger (je ne suis pas vraiment developpeur). J'ai découvert des horreurs (PHP) et des technos super cool (MODx—meme si c'est du PHP, et Javascript/Node.js) qui m'ont vraiment éclaté.
Ça faisait longtemps que je voulais faire quelque chose avec les nouvelles technos client/serveur completement en Javascript, et Meteor s'est imposé comme la techno que je voulais utiliser. J'avais testé Express (autre framework pour Node.js) que j'avais trouvé fascinant mais qui m'avait vite decouragé, parce qu'il y a énormement de travail de plomberie avant d'acceder au code metier. Meteor n'a pas ce defaut: il permet vraiment d'attaquer directement la partie qui importe (et pour un programmeur, la partie juteuse du projet, celle qui motive).
Sur les difficultés rencontrées: d'abord je suis une merde en HTML/CSS et c'est vraiment un truc qui me rebute (et ça se voit plutot pas mal dans ce projet). Autant le reste du code était un bonheur à écrire, autant j'ai eu l'impression de perdre des heures à le connecter pour en faire une interface utilisateur. Pour le coup ce n'est pas de la faute de Meteor, qui simplifie enormement la tache en proposant une version adaptée de Handlebar.js.
Les concepts de Meteor sont un peu difficile à appréhender au debut. Il laisse beaucoup de liberté pour organiser son projet comme on veut, et ça peut vite devenir le bordel (ce n'est pas un framework). Le principe des publications/inscriptions, très puissant, est aussi un peu difficile à comprendre au debut. Dans l'idée, le serveur met a disposition du client des informations de la base de donnée. Le client a sa propre base de donnée et son propre systeme de base de donnee, minimongo—un mongodb simplifié. Du coup, il faut generalement refaire les mêmes requêtes côté client et côté serveur. Au debut on a l'impression que c'est redondant et peu efficace, mais c'est un chouilla plus subtil.
Les publications (la mise a disposition d'information côté serveur) permettent de sécuriser un peu ce que l'utilisateur peut voir sur le site en fonction du contexte. Par exemple, dans mon projet il y a différentes publications qui mettent à disposition des informations provenant de la meme base de donnée. Ainsi, lorsqu'un client est sur la page d'accueil, il a acces a son propre document (son propre profil), à la liste de ses amis (une version tres light des profils, surtout pour recuperer le nom et la photo de profil), à ses amis en ligne (qui intersecte necessairement avec la liste de ses amis). Vous pouvez le testez vous meme: allez voir la demo et tapez Meteor.users.find({}).fetch() pour voir quels infos de la base Meteor.users vous pouvez voir. Du coup, côté client il faut organiser ces informations et refaire des requêtes pour séparer ses amis de soit-même lorsqu'on manipule les informations.
Si je visite le profil de quelqu'un, le contexte change et une publication me donne les informations pour ce profil.
En gros, on fournit au client les données et la logique, et il se demerde pour faire le rendu. Si vous voulez jouer, tester Collection.find({}).fetch() dans la console, em remplacant Collection par, au choix, Friends, Messages, Conversations, Notifications... pour voir quels informations sont dans votre BDD.
Maintenant, lorsqu'un document est ajouté/retiré/mis-à-jour dans la base de donnée, et que je suis inscrit a une publication qui diffuse ce document, ma mini-base de donnée côté client est automatiquement mise-à-jour. C'est là ou ca devient un peu compliqué: il y a tout un système de dépendence entre les Templates (bout de DOM utilise pour generer le rendu) et la base de donnée. En gros, si j'ai une Template qui utilise des informations de la BDD, Meteor va décider ou non de refaire le rendu de la Template, de la mettre a jour ou de ne rien faire lorsque la BDD est mise-à-jour. C'est de là que viens la réactivité, mais ça peut aussi etre terriblement chiant. Par exemple pour la création de profil, quand on clique sur "Save", le document de l'utilisateur est modifié , et ca devrait donc refaire le rendu de la page, puisque les champs de formulaire dependent de la valeur du profile utilisateur si elle existe. Du coup il m'a fallu déclarer que les informations que je récupere sont non-réactive, pour bloquer la mise-à-jour automatique de cette Template en particulier (jetez un oeil a client/views/includes/form/form_fields.js, la fonction en haut retourne la valeur a mettre pour un champ lorsqu'on génère un formulaire, notez que la dependance est bloquée en utilisant reactive: false).
Autre exemple un peu tordu qui m'a pris un peu de temps à concevoir, lors de la recherche d'utilisateur. Dans à peut prêt n'importe quel autre système, le client fait une requête du genre "Envoie moi tous les gens qui sont X et Y", le serveur fait la recherche puis renvoie une liste au client. Cette facon de faire serait tres inefficace avec Meteor, puisqu'il est très possible que certains utilisateurs soient dejà dans la base de donnée côté client, mais aussi parce que le serveur devrait retourner une liste d'utilisateur sous forme de liste qu'il faudra ensuite manipuler côté client—pourquoi faire ça alors que le client dispose de sa propre BDD? Du coup l'astuce consiste à demander au serveur de metter a jour une publication (qui, comme les templates précedemment, depend cette fois d'une variable de Session 'searchQuery'—si cette variable change, la publication est recalculée), lorsque le serveur a finit de mettre a jour la publication, il met a jour une autre variable de Session côté client ('searchQueryDone'). Côté client, j'ai une template qui depend de cette variable (et non pas des donnees, sinon ma page de recherche clignoterait a chaque fois qu'un nouvel utilisateur se creerait un profil par exemple), qui va donc se mettre a jour en utilisant les informations de la base de donnée ET refaire la requete locallement.
Là où Meteor brille, c'est qu'il s'occupe comme un grand d'optimiser tout ça, d'envoyer les bonnes informations au client, de refaire le rendu des parties concernées par les changements. Quand ça marche, c'est magique et vraiment super simple a faire fonctionner. Quand une heuristique foire (typiquement, dependance un peu severe, ou tout simplement lorsque Meteor n'arrive pas a faire un diff propre d'un bout de DOM avant/après), ca devient une super prise de tête.
Un dernier exemple pour la route, parce que ma matinee est foutue de toute facon. Imaginons que l'on veuille rajouter un nouveau widget qui affiche la liste des amis communs entre soit-meme et la personne dont on regarde le profil. La liste de mes amis est deja publie et disponible. La liste des amis de ma cible est publie lorsque je visite son profil, j'ai donc toutes les informations a disposition, rien besoin de changer cote serveur. La collection "Friends" indique que A est en relation avec B (et il y a un document de B vers A si la relation est reciproque). Bout de JS que vous devriez pouvoir tester chez vous avec firebug ou la console de Chrome:
Et voila! Le mega-pavé.