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

Publié par Olivier Balais

Jeune ingénieur logiciel basé à Lyon (@overnetcity) passionné par les NTIC et le développement Web, je suis actuellement salarié chez Reputation VIP et effectue en parallèle des missions ponctuelles en temps que Freelance. Passionné depuis toujours par l'informatique et le développement, suite à une formation solide à l'INSA de Lyon, je me suis spécialisé dans la réalisation de bout en bout de projets web complexes.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *