Certaines parties de Ember/Ember-Data sont assez obscures pour les nouveaux venus. Parfois, les comportements du framework peuvent paraître relever de la magie aussi longtemps qu’on ne se plonge pas dans le code pour en isoler certains mécanismes.
C’était pour moi le cas concernant les requêtes émises par EmberData en fonction des modèles définit dans votre application.
Considérons le bout de code suivant :
MyApp.User = DS.Model.extend({ firstname: DS.attr('string'), lastname: DS.attr('string') // ... }); MyApp.User.find();
Lorsqu’on exécute un find
sur ce modèle, EmberData est suffisamment intelligent pour lancer une requête GET sur /url/de/votre/api/user.
Mais comment ?
Quand on décortique l’adapter, on constate qu’au moment de construire la requête Ajax, ce dernier fait appel à la méthode rootForType
du Serializer :
rootForType: function(type) { var typeString = type.toString(); // [...] var parts = typeString.split("."); var name = parts[parts.length - 1]; return name.replace(/([A-Z])/g, '_$1').toLowerCase().slice(1); }
C’est en fait tout bête, le Serializer fait un toString
sur notre classe du modèle.
MyApp.User.toString() // => MyApp.User
Et pour obtenir le même résultat depuis une instance, EmberData utilise tout simplement l’attribut constructor
de l’objet :
MyApp.User.find(someId).then(function(someUser) { someUser.contructor.toString(); // => MyApp.User });
On continue néanmoins à nager en pleine magie, comment Ember est-il capable d’afficher ce résultat sur un toString()
d’un objet quelconque.
Ember redéfinie de manière intelligente la méthode toString
de la manière suivante :
Ember.Mixin.prototype.toString = function() { if (!Ember.BOOTED && !this[NAME_KEY]) { processAllNamespaces(); } var ret; if (this[NAME_KEY]) { ret = this[NAME_KEY]; } else { var str = superClassString(this); if (str) { ret = "(subclass of " + str + ")"; } else { ret = "(unknown mixin)"; } this.toString = makeToString(ret); } return ret; };
En lisant ce bout de code, on constate qu’un traitement particulier est fait à l’initialisation de l’application Ember pour parser tous les Namespaces.
Ember parcourt de manière récursive l’ensemble des Namespaces déclarés et surcharge pour chacune des classes appartenant à ces namespaces la méthode toString
, de manière à renvoyer un path de la forme MyNameSpace.MySubNameSpace.MyAmazingClass
.
Tout simplement !
Et je dois dire que ce comportement est bien pratique pour calculer certaines propriétés de manière générique, en se basant justement sur le nom de notre classe via la méthode toString.
Exemple :
// Au sein d'une classe héritant de Ember.Object et appartenant à un Namespace quelconque myPropertyWhichReallyNeedMyClassname: function() { var parts = this.constructor.toString().split('.'), name = parts[parts.length - 1]; return 'Something' + name; }.property(),
Plus d’infos ici : http://www.emberist.com/2012/04/09/naming-conventions.html
Et dans le code évidemment : https://github.com/emberjs/ember.js/blob/master/packages/ember-runtime/lib/system/namespace.js