ZeNiTHaR'z BLoG

Il existe moins bien mais c'est plus cher.

Backbone.js & CoffeeScript

Cet article va traiter de l’utilisation de CoffeeScript dans le cadre de la réalisation d’une application Backbone.js.

Dans un premier, j’aborderai très rapidement le langage CoffeeScript, puis dans un second temps son utilisation dans l’écriture de script JavaScript.

CoffeeScript - “It’s just javascript !”

Le CoffeeScript est un langage de programmation, qui une fois compilé produit du code Javascript. Le but principal de ce langage est de fournir à l’utilisateur une notation type POO (Programmation Orienté Objet), avec des mots clés comme extends, super, ainsi que de simplifier la syntaxe du Javascript.

En effet Javascript, n’est pas vraiment un langage objet, l’héritage se fait par manipulation du prototype[1]. Ce qui rends la programmation objet pas très facile avec Javascript.

Classes.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal
  constructor: (@name) ->

  move: (meters) ->
    alert @name + " moved #{meters}m."

class Snake extends Animal
  move: ->
    alert "Slithering..."
    super 5

class Horse extends Animal
  move: ->
    alert "Galloping..."
    super 45

sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"

Ce qui va donner après compilation via CoffeeScript :

Classes.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var Animal, Horse, sam, tom,
  __hasProp = {}.hasOwnProperty,
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };

Animal = (function() {
  Animal.name = 'Animal';
  function Animal(name) {
    this.name = name;
  }
  Animal.prototype.move = function(meters) {
    return alert(this.name + (" moved " + meters + "m."));
  };
  return Animal;
})();

Horse = (function(_super) {
  __extends(Horse, _super);
  Horse.name = 'Horse';
  function Horse() {
    return Horse.__super__.constructor.apply(this, arguments);
  }
  Horse.prototype.move = function() {
    alert("Galloping...");
    return Horse.__super__.move.call(this, 45);
  };
  return Horse;
})(Animal);

tom = new Horse("Tommy the Palomino");
tom.move();

Remarquez que le code généré utilise des méthodes pour simplifier le code. Attention, pour les tatillons vous remarquerez que j’utilise pas CoffeeScript mais un dérivé compatible IcedCoffeeScript, qui produit une sortie JavaScript plus propre. Bon c’est un détail, mais ça compte !

L’utilisation de la syntaxe CoffeeScript me permet aussi d’éliminer beaucoup de problèmes liés à la syntaxe du JavaScript.

Je vous invite à aller consulter le site CoffeeScript.org, pour voir toute l’étendue de la syntaxe CoffeeScript.

Outils CoffeeScript

Le langage apporte tout un ensemble d’outils pour rendre service au développeur.

  • Documentation :
    • Docco : Par l’auteur du langage, permet de générer de la documentation en utilisant les commentaires du code. Le site de docco utilise docco, vous voyez donc un exemple de sortie du format de la documentation.
  • Javascript :
    • JS2Coffee : Cet outil permet de traduire vos scripts Js en script CoffeeScript.
  • IDE :
  • Références :

Backbone.js et CoffeeScript

Backbone.js et CoffeeScript ont été écrit par la même organisation DocumentCloud, ils sont “accessoirement” les auteurs :

Les exemples suivants sont extraits du portage Todo App en CoffeeScript par Jason Giedymin.

Les modèles

TotoModel.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Définition du modèle backbone
class Todo extends Backbone.Model
  # Attributs par défault
  defaults:
    # Un objet Todo possède un contenu, "empty todo…" sera la valeur 
    # initiale à la construction.
    content: "empty todo..."
    # Un obj Todo possède un attribut indiquant qu'il est fait ou non, 
    # par défaut à la construction le status sera faux (pas fait)
    done: false

  # A ne pas confondre avec le constructeur, avec backbone il y a deux
  # initialisations: la construction (via constructor), et l'initialisation 
  # (via initialize), ces deux fonctions peuvent prendre en paramètre un objet
  # souvent pour définir les valeurs de les attributs du modèle.
  initialize: ->
    @set({ "content": @defaults.content }) if !@get("content")

  # Fonction utilitaire pour changer le status de la tâche
  toggle: ->
    @save({ done: !@get("done") })

Les collections

TodoList.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# Notez la syntaxe pour la déclaration d'une classe
class TodoList extends Backbone.Collection

  # Type de la collection
  model: Todo

  # Notez bien la différence, le '=' au lieu du ':', cette différence va
  # générer deux codes Js différents :
  #  - ':' : pour des membres d'instances
  #  - '=' : pour des membres de classes privés (fonctions statiques)
  getDone = (todo) ->
    # Remarquez le '?', qui va faire un appel à get, uniquement si todo != null
    # => qu'est ce que j'aimerais avoir ça en java !!! (vive scala …)
    return todo?.get("done")

  # Biensur cette fonction n'a aucun intérêt dans le modêle.
  # Mais c'est comme ça que l'on déclare des fonctions statiques publiques.
  # Elles pourront être invoqués de la sorte :
  # TodoList.pouet()
  #
  # Notez que le "return" est implicite, en CoffeeScript la dernière instruction
  # du scope, produit toujours un "return", d'ailleurs attention au effet de bord.
  @pouet = () ->
    "toto fait du vélo !"
      
  # Utilise la méthode filter des collections Backbone (en fait Underscore),
  # avec note méthode statique privée comme prédicat.
  done: ->
    return @filter( getDone )

  # Retourne les tâches qui ne font pas partie des tâches terminées, donc cela
  # retourne les tâches inachevées.
  remaining: ->
    return @without.apply( this, @done() )

  nextOrder: ->
    # Alors on apprécie ou pas cette syntaxe mais c'est quand même beau !
    return 1 if !@length
    return @last().get('order') + 1

  # Function héritée de la collection Backbone utilisée pour ordonner la collection.
  comparator: (todo) ->
    return todo.get("order")

Les vues

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class TodoView extends Backbone.View
  # L'élément DOM de base crée par la vue sera un <li>, cela veut dire que
  # this.el (@el) sera "<li></li>"
  tagName:  "li"

  # Utlisation du moteur de template Underscore
  template: _.template( $("#item-template").html() )

  # Bind des évènements DOM
  events:
    "click .check"              : "toggleDone"
    "dblclick div.todo-content" : "edit"
    "click span.todo-destroy"   : "clear"
    "keypress .todo-input"      : "updateOnEnter"

  # Initialisation de la vue
  initialize: ->
    @model.on 'change', this.render

  # Remarquez le '=>' que l'on appelle la 'fat arrow', c'est le genre de
  # nouveauté qu'apporte CoffeeScript, cela permet de sauvegarder la valeur
  # de 'this', et de référencer le this interne par une autre variable, permettant
  # l'utilisation classique du 'this' dans la méthode.
  #
  # On n'est pas obligé d'utiliser la 'fat arrow', il suffit de faire une fonction
  # simple '->', ET ne pas oublier le '_.bindAll(this, "render")' dans le bloc 
  # initialize. 
  render: =>
    # Depuis Backbone 0.9 @.$(@el) est devenu @$el
    @$el.html( @template(@model.toJSON()) )
    @setContent()
    # Ne pas oublier par convention render DOIT retourner "this"
    @

  # Volontairement écourté …

Conclusion

Voila c’est fini, pour cet article, j’espère vous avoir donné l’envie de continuer les recherches, et les expérimentations dans le “nouveau” monde du JavaScript et du HTML5.

Il existe des alternatives sérieuses à Backbone.js :

  • Ember.js : Qui semble être capable de rivaliser avec backbone.js dans le royaume des MVC.JS
  • Spine.js : dans la même veine (je l’ai découvert en bossant sur swagger-core.)
  • Knockout.js : possède une approche différente, type MVVM, personellement j’accroche pas, je trouve trop compliqué, et ça me rappelle trop WPF…
  • et bien d’autres sortes et surf sur la vague du JS

Toutes ces nouvelles technologies ne rendront que plus agréable vos futurs développements JS, et n’hésitez pas, foncez, c’est pour votre bien !

Qui plus est, la compétence Backbone.js commence à être demandée sur le marché du travail, ça fait toujours une corde de plus à son arc.

A venir …

Mon prochain article parlera de l’utilisation de Require.js au sein d’application Backbone.js, en reprenant l’article d’Addy Osmani - Writing modular js. J’essayerai de résumer, et d’apporter mon retour d’expérience.

Sur ce, bonne soirée (enfin bonne nuit vue l’heure !).

[Iced]CoffeeScript minification

J’ai recherché un moyen de modifier la sortie du code généré par le compilateur CoffeeScript. En consultant la documentation, j’ai trouvé ceci

Pour utiliser cette “extension”, il suffit d’invoquer la commande de la sorte :

1
#> coffee -bp -r ./ext.coffee file.coffee

Pour faire fonctionner, cette extension pour le langage Iced CoffeeScript, il suffit de remplacer l’import ”coffee-script” par ”iced-coffee-script”.

Backbone.js - Les évènements

Introduction

Cet article fait suite à la présentation des concepts Backbone.JS. Vous devez avoir connaissance d’une base de Javascript pour aborder de manière sereine la partie suivante.

Dans cet article, je vais m’attacher à expliquer le fonctionnement des évènements dans Backbone.JS, ainsi que, de fait, les principes de programmation asynchrone utilisés.

Programmation asynchrone

La programmation asynchrone est le coeur de la programmation Javascript, vous avez toujours la possibilité de faire du synchrone, cependant ce serait perdre un des avantages de Javascript.

Le principe est simple :

On sait quand on appelle la fonction, mais on ne sait pas quand elle sera traitée !

De ce fait, cela induit des modes de programmation qui ne sont pas forcément habituels :

Si on prend l’exemple suivant (Node.js) :

file.js
1
2
3
4
5
6
7
8
9
var fs = require("fs");
fs.readFile("fichier.txt", function(err, content) {
  if (err) {
    console.err(err);
  } else {
    console.log("file read: " + content.length + " bytes");
  }
}
console.log("after readFile");

Produira la sortie suivante :

1
2
3
#> node file.js
after readFile
file read: 234 bytes

On peut remarquer une particularité, la fonction d’appel à la lecture du fichier “fichier.txt”, prends en paramètre une fonction dîtes “anonyme” (sans nom).

Cette fonction sera exécutée comme un “callback”, lors de la lecture du fichier.

Il est très important de comprendre ce mécanisme pour la suite de l’article.

Evènements Backbone.JS

Les évènements Backbone.JS sont aussi traités de manière asynchrone, on enregistre des “handlers”, sur des évènements qui peuvent intervenir n’importe quand.

Une bonne application Backbone.JS est une application complètement décorellée et évènementielle. Les composants sont implémentés, et reliés par des messages (event).

Et je vais encore une fois citer Addy Osmani, pour son article expliquant comment construire une application Web Javascript “scalable”, et découplée :

Gestion des évènements

La gestion des évènements avec Backbone.JS se fait comme pour jQuery, avec les fonctions ”on”, ”off”.

Les évènements sont gérés sous la forme d’un modèle de conception très bien connu aujourd’hui, le modèle Publish / Subscribe (Producteur / Consommateur). Le bus de dispatch utilisé pour la communication entre les composants se nomme le médiateur.

Tous les concepts de Backbone.JS peuvent être à la fois producteur et consommateur. Cependant certains concepts possèdent des mécanismes déjà implémentés de publication. Il n’y a pas de consommateur par défaut.

Les modèles : Backbone.Model

Extrait de la documentation Backbone.JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var Sidebar = Backbone.Model.extend({
  promptColor: function() {
    var cssColor = prompt("Please enter a CSS color:");
    // Publication de l'évènement "change:color"
    this.set({color: cssColor});
  }
});

window.sidebar = new Sidebar;

// Enregistre un callback, sur l'évènement "change:color", la fonction
// sera appelée dès que quelqu'un publiera l'évènement.
sidebar.on('change:color', function(model, color) {
  $('#sidebar').css({background: color});
});

// Modification de l'attribut, publication automatique de l'évènement 
// "change:<attribute-name>". 
sidebar.set({color: 'white'});

// On demande une couleur
sidebar.promptColor();

// On annule toutes les inscriptions sur l'évènement
sidebar.off('change:color');
EvènementsDescription
change:attributeEmis lors de la modification d’un attribut du modèle.
errorEmis par la validation du modèle s’il y a des erreurs.
destroyEmis quand l’objet a été détruit.
syncEmis quand l’objet a été synchronisé.

Les collections : Backbone.Collection

Par convention, tous les évènements émis d’un modèle peuvent être capturé par la collection.

Extrait de la documentation Backbone.JS
1
2
3
4
5
6
7
8
9
10
var ships = new Backbone.Collection;

ships.on("add", function(ship) {
  alert("Ahoy " + ship.get("name") + "!");
});

ships.add([
  {name: "Flying Dutchman"},
  {name: "Black Pearl"}
]);
EvènementsDescription
addEmis lors de l’ajout d’un modèle à la collection.
removeEmis lors de la suppression d’un modèle de la collection.
resetEmis lors de la réinitialisation de la collection.

Les routeurs : Backbone.Router

Extrait de la documentation Backbone.JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var Workspace = Backbone.Router.extend({

  routes: {
    "help":                 "help",    // #help
    "search/:query":        "search",  // #search/kiwis
    "search/:query/p:page": "search"   // #search/kiwis/p7
  },

  help: function() {
    ...
  },

  search: function(query, page) {
    ...
  }

});

router.on("route:help", function(page) {
  ...
});
EvènementsDescription
route:<name>Emis lors de l’activation de la route “name”.

Les vues : Backbone.View

Les évènements de la vue sont un peu particuliers car ils symbolisent des actions DOM (click, mouseOver, etc.)

Extrait de la documentation Backbone.JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var DocumentView = Backbone.View.extend({

  events: {
    // Appel "open" s'il y a un doubleclick sur l'élément contenant la vue
    "dblclick"                : "open",
    // Appel "select" s'il y a un click gauche sur les sous éléments du contenant
    // dont la classe est "doc" et sont fils d'élements dont la classe est
    // .icon
    "click .icon.doc"         : "select",
    // Appel "showMenu" sur un click droit
    "contextmenu .icon.doc"   : "showMenu",
    "click .show_notes"       : "toggleNotes",
    "click .title .lock"      : "editAccessLevel",
    // Appel "showTooltip" lorsque la souris se trouve sur l'élément sélectionné
    "mouseover .title .date"  : "showTooltip"
  },

  initialize: function() {
      // Très importante cette ligne, elle permet de définir le this dans les fonctions
      // callback, n'oubliez pas tout est asynchrone, donc le this n'est pas forcément
      // celui qu'on croit quand on l'utilise.
      _.bindAll(this, "render","open","select");
  },

  render: function() {
    $(this.el).html(this.template(this.model.toJSON()));
    return this;
  },

  open: function() {
    window.open(this.model.get("viewer_url"));
  },

  select: function() {
    this.model.set({selected: true});
  },

  ...

});

Les évènements personnalisés : Backbone.Events

Il est tout à fait possible d’ajouter des évènements à tous les objets en utilisant le Mixin Events.

Extrait de la documentation Backbone.JS
1
2
3
4
5
6
7
8
9
var object = {};

_.extend(object, Backbone.Events);

object.on("alert", function(msg) {
  alert("Triggered " + msg);
});

object.trigger("alert", "an event");

Tout d’abord qu’est qu’un mixin ? Comme son nom l’indique, il s’agit d’une chose (un objet) qui pourra être mélangé à un autre. Alors vous me direz, “ok mais et l’héritage alors ?”, et bien si y a deux termes c’est que (normalement) il y a des différences.

Un héritage sert à rendre commun un morceau de code, des propriétés à toute un hiérarchie, alors qu’un mixin peut être utilisé sur des objets qui n’ont pas forcément de rapport.

Bref, pour revenir à nos moutons, Backbone.Events est un mixin, que l’on peut injecter dans n’importe quel objet JS.

D’ailleurs, il est fort conseillé si vous utilisez les Events, de définir votre propre médiateur. Et pour une approche, modulaire et extensible de votre application, vous pouvez utiliser ce qu’on appelle un “Event Aggregator”.

Event Aggregator Pattern

Ou comment découpler au maximum les objets et leurs interactions.

Extrait du site link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
var AddEditView = Backbone.View.extend({

  // On initialise la vue avec le ventilator
  initialize: function(options){
    _.bindAll(this, "editMedication");

    // On connecte un évènement "editMedication"
    options.vent.bind("editMedication", this.editMedication);
  },

  editMedication: function(medication){
    this.model = medication;
    this.render();
  }
});

var MedicationView = Backbone.View.extend({
  events: {
    "click #edit": "editMedication"
  },

  // On initialise la vue avec le ventilator
  initialize: function(options){
    this.vent = options.vent;
  },

  // On envoie des évènements au ventilator, qui se chargera
  // de distribuer aux destinataires le message.
  editMedication: function(){
    // Un évènement est émis via le ventilator "editMedication"
    this.vent.trigger("editMedication", this.model);
  }
});

// Initialisation de l'application, et connexion des évènements
// en utilisant l"event aggregator".

var vent = _.extend({}, Backbone.Events);

var addEditView = new AddEditView({vent: vent});

medicationList.each(function(med){
  new MedicationView({model: med, vent: vent});
});

Cette dernière partie est un peu brutale, je sais, mais c’est pour finir en beauté. Comme je l’ai déjà dit, le principal avantage de Backbone.JS, c’est de fournir à l’utilisateur un moyen de bien développer, par la structuration du code, les concepts de séparation de responsabilité, etc.

Cela permet aussi d’utiliser des patrons de conception déjà éprouvés dans le monde de l’informatique au sein du navigateur (et aussi du serveur) avec JavaScript.

Conclusion

Voila cet article terminé sur la gestion des évènements Backbone.JS. Comme toujours, j’espère avoir été clair, enfin plus clair que la documentation sur le sujet, ou au moins avoir levé un peu du voile que l’on affronte quand on arrive sur Backbone et tous les JS friends.

Mon prochain article parlera de l’utilisation de CoffeeScript avec Backbone.JS, pour simplifier l’écriture du code.

Bonne soirée à toutes et à tous.

PS : Un grand merci à jekyll, octopress et Git, qui rendent l’écriture des articles sur mon blog vraiment agréable.

Backbone.js - Présentation

Introduction

Cet article s’adresse aux personnes désirants découvrir ou redécouvrir, l’utilité et l’utilisation de Backbone.JS au sein d’une application Web Javascript. En effet Backbone.JS s’inscrit dans les technologies Javascript clientes (puisqu’il existe aussi un coté serveur via Node.js).

Les concepts

Chaque concept est implémenté en Javascript de telle sorte qu’ensemble ils forment votre application.

Les modèles

Les modèles Backbone sont des objets Javascript servant à représenter l’information, qui sera souvant utilisée pour l’affichage.

1
2
3
4
5
6
7
8
9
10
11
12
var Person = Backbone.Model.extend({
  // Déclaration des attributs de la classe MonModel
  defaults : {
      firstname : null,
        lastname : null
    }
});
// Pour utiliser le modèle 
var tno = new Person({firstname: "Thibaud", lastname: "Normand"});
console.log(tno.get("firstname"));  // "Thibaud"
tno.set({firstname: "Thibaud"});
console.log(tno.get("firstname")); // "Thibault"

Les collections

Les collections sont des ensembles d’objets, en général d’un même type de modêle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var PersonCollection = Backbone.Collection.extend({
  // C'est une collection d'object de type Personne
  model: Person
});
// Utilisation
var collection = new PersonCollection();
collection.add(new Person({"firstname": "toto", "lastname": "fait du vélo !"}));
// Mais aussi …
var collection2 = new PersonCollection(
  [
      {id: "toto", "firstname": "toto", "lastname": "fait du vélo !"},
      {id: "tutu", "firstname": "tutu", "lastname": "fait du vélo !"}
  ]
);

Remarquez que j’ai ajouté un attribut id au modèle lorsque j’ai initialisé ma collection. Cela permet à Backbone.JS de ne pas générer un code, mais de le définir, pour pouvoir ensuite utilser la méthode suivante :

1
collection2.get("toto"); // Retourne l'objet Person avec l'id "toto"

Les vues

Les vues sont utilisées pour représenter graphiquement (HTML, jQuery / Zepto) les informations issues d’un modèle ou d’une collection. C’est l’IHM du modèle ou de la collection associée.

1
2
3
4
5
6
7
8
9
10
11
12
var PersonDetailsView = Backbone.View.extend({
  // Ici on utilise un selecteur CSS3, pour indiquer le point d'insertion 
  // de la vue dans l'arbre DOM. (On recherche un élément dont l'id est "details")
  el: $("#details"),
  // Ici on compile le template, cela utilise la fonction template d'underscore.js
  template: _.template("
    Hello <%= firstname %> <%= lastname %> ! 
  "),
  render: function() {
      this.el.append(this.template(this.model.toJSON()));
  }    
});

Il est important de retenir que les vues sont du codes Javascript qui va modifier la page courante (arbre DOM). Il est de ce fait très facile de contruire des vues qui sont complètement décorréllées de la page ou elle va être “rendue” (affichée). J’utilise la fonction de template Underscore.js.

Il est tout à fait possible d’utiliser d’autres moteurs de template, par exemple :

Les routeurs

Les routeurs sont utilisés pour diriger les appels provenant de l’extérieur, vers le bon code Javascript associé à l’url.

En effet, les routeurs utilisent une partie spéciale de l’URL, que l’on nomme le hashbang

1
http://www.zenithar.org/index.html#!/person/toto
ExtraitDescription
“http://”le protocole
“www”l’hôte
“index.html”la ressource
”#!/person/toto”le hasbang

Ce hasbang était historiquement utilisé pour atteindre des ancres dans la page HTML, mais il peut aussi être utilisé via Javascript pour définir des comportements, et surtout les changements sont stockés dans l’historique de votre navigateur.

Par exemple, ici le hasbang peut très bien faire appel à la vue PersonneDetailsView, en utilisant l’objet Personne ayant pour code “toto”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var ApplicationRouter = Backbone.Router.extend({
  routes : {
      "!/person/:code" : showPerson,
      "": index
  },
  initialize: function() {
      this.detailsView = new PersonDetailsView();
      this.collection = new PersonCollection();
  },
  showPerson : function(code) {
      this.detailsView.model = this.collection.get(code);
      this.detailsView.render();
  },
  index : function() {
      $("#details").hide();
  }
});
// Initialisation
var router = new ApplicationRouter();
// Mise en place de l'écoute de changement de hashbang, et dispatch des routes.
Backbone.history.start();

Et les contrôleurs ?

Si vous avez compris, c’est un modèle de conception MVC. Cependant le ‘C’ de MVC, n’est pas clairement représenté dans Backbone.JS, en effet il y a les ‘V’ues, les ‘M’odêles, mais pas (plus) de Contrôleurs.

Pour être propre, il fortement conseiller d’utiliser un objet Javascript contenant les interactions entre Modêles / Collections / Vues, et de faire en sorte que les Routers utilisent le contrôleur.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// On crée ici un singleton Javascript.
var PersonController = (function() {
  // Sera initialisé à la création du singleton
  // Fait office de code privé
  var collection = new PersonCollection();
  var view = new PersonDetailsView();
  return {
      // Code public exposé par le contrôleur
      this.showPerson: function(code) {
          var m = collection.get(code);
          view.model = m;
          view.render();
      }
  }
})();
// De ce fait le code du router devient
...
  showPerson : function(code) {
      PersonController.showPerson(code);
  }
...

Communication

La communication de Backbone.JS avec le serveur se fait par l’intermédiaire d’une méthode “Backbone.sync”, cette méthode va définir les comportements à adopter en fonction de l’opération élémentaire à réaliser.

Par défaut, Backbone.JS utilise un mode de communication via des services type REST :

URLVerbe HTTPDescription
/collectionsGETRécupèration de la liste des objets
/collectionsPOSTCréation d’un objet
/collections/idPUTMise à jour de l’objet
/collections/idDELETESuppression de l’objet

C’est à dire qu’il vous faudra concevoir des services coté serveur pour pouvoir stocker les modifications coté client, via les services REST.

Par exemple :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var RemotePersonCollection = Backbone.Collection.extend({
  // C'est une collection d'object de type Personne
  model: Person,
  // On ajoute l'information nécessaire à Bacbone.sync pour le chargement 
  // de la collection, le format de retour devra être conforme au JSON du 
  // modèle
  url: "/api/persons"
});
// Utilisation
var remoteCollection = new RemotePersonCollection();
remoteCollection.fetch();
// Execute un appel Ajax qui va retourner une liste de personne.
// L'utilisation de la collection est comme en locale.
var m = remoteCollection.get('toto');
// Modification de l'objet de la collection
m.set({"firstname":"dudu"});
m.save(); // Requête PUT '/api/persons/toto'

En attendant la suite

Bon voila pour le moment, j’espère pas vous avoir perdu. Au menu du prochain article, la programmation asynchrone, les évènements Backbone.JS, les tests unitaires, CoffeeScript, et enfin le désossement de l’application phare Backbone.JS la TODO App.

Je vous invite à aller lire le livre qui est en train d’être écrit par Addy Osmani

PS : Vive Markdown, et Mou ^^

Copier / Coller depuis la console

Petite découverte de la journée, j’ai eu le besoin de copier un fichier de la console vers un programme “graphique”.

Xsel, est l’outil qu’il faut pour ça ! Il permet de manipuler le clipboard depuis la console.

Pour transférer le contenu d’un fichier dans le clipboard
1
#> cat file | xsel --clipboard

Je vous invite à aller sur le site pour plus d’exemples, vraiment pas mal, il fallait y penser et il l’a fait ^^

Bye bye Wordpress

Au revoir, Wordpress, et dîtes bonjour à Octopress, j’ai eu envie de migrer mon blog sur cette solution, car depuis quelques temps, je cherchais de plus en plus à rendre mon blog statique pour des raisons de performances mais aussi pour les petits malins qui tente par bruteforce de trouver le mdp d’administration du blog Wordpress.

J’ai choisi une migration vers une solution moins fournie mais correspondant plus à mes besoins, tout en allégeant le serveur car le blog est entièrement statique.

J’ai mis en tout et pour tout, 4 jours a temps plein pour migrer :

  • Revue des posts un par un, pour corriger les problèmes liés à la syntaxe différente
  • Correction du plugin d’import Exitwp, pour pouvoir générer les fichiers au format Markdown.
  • Transfert des ressources statiques (images, fichiers joints, etc.)
  • Traduction de Octopress, seul bémol pour le moment les dates ne sont pas traduites (ça c’est jekyll, qui n’a pas été conçu à la base pour être internationalisable.)

Je vais continuer les modifications, notamment au niveau du thème que j’aime bien mais trop de bloggueurs ont le même.

Donnez un coup de jeune à vos applications Web !

Le développement d’application Web a bien évolué avec l’essor émergeant des nouvelles technologies (HTML5, JS, etc.). Autrefois, la complexité des applications étaient déportée sur le serveur hébergeant le service, avec la montée en puissance des navigateurs et technologies clientes, il est possible d’effectuer une partie du travaille dans le navigateur :

  • Mise en forme.
  • Conditionnement technologique. (j’espère que cela va disparaitre ou au moins se réduire …)
  • Traiter l’information avant validation par le serveur pour éliminer le plus d’échange possible.

L’utilisation de ces technologies permettent entre autres d’alléger le serveur hébergeant le système, et de limiter sa fonction à un fournisseur de données.

Le fait de déplacer la complexité des applications du serveur vers le client, ainsi que l’évolution des langages utilisables dans un navigateur, a généré chez les développeurs un manque de structuration des applications coté client.

Les applications serveurs sont souvent intégrées à l’aide d’un ensemble de technologies servant à structurer  les développements, telles que :

  • Plateforme : J2EE, Ruby, Python
  • IoC : Spring, Guice
  • MVC : Spring MVC, Stru…. (désolé j’y arrive pas !), Rails, Django
  • et plein d’autres.

Coté client,  il s’agit souvent de technologie basée JavaScript ou via plugin propriétaire (Flash, Silverlight), mais bon aux vues des tendances actuelles il serait dangereux de croire que le propriétaire va continuer à vivre, face aux technologies standards (HTML5, SVG, Canvas, etc.) et bien d’autres dérivées (WebGL).

Dans un prochaine article, nous aborderons comment concevoir un écran complet, en utilisant SpringMVC (coté client), ainsi que Backbone.JS (framework JS). Cet écran sera complètement décorellé du service, et pourra être porté sur une autre technologie sans trop d’effort (Sinatra).

Du JavaScript à toutes les Sauces !!!

Vous n’êtes pas sans savoir (en tout cas ceux qui bossent avec moi le savent ^), ou alors vous avez vécu sur une autre planète qu’en ce moment (depuis 6mois - 1an), le monde du JavaScript a subit d’énormes mutations. Et qu’il y trouve ça place au rang des PHP, Ruby, voir même Java;

Les technologies dérivées permettent de mettre à plat un serveur Apache tant au niveau performance, qu’au niveau consommation mémoire. Qu’il peut être utilisé comme interface de communication avec une base de données type NoSQL comme MongoDB, CouchDB.

Ce langage, utilisé jusqu’à présent pour ajouter un caractère dynamique au HTML, peut être utilisé pour divers activités, et remets au gout du jour une manière de programmer : la programmation asynchrone.

A tel point que certaines personnes se posent la question simple : Le JavaScript va-t-il détroner les autres langages Web ?

Il y a 20 ans (et oui déjà), les infrastructures “Web 0.1” utilisaient un rendu complet coté serveur (l’AJAX n’existait pas), ce qui faisait faire un aller-retour pour chaque page demandée. Puis est arrivé, la notion d’AJAX, permettant d’effectuer des requêtes hors cycle de chargement principal, ce qui a permit de mettre en place des chargements partiels d’informations tout en minimisant de fait le trafic Client/Serveur.

Puis sont arrivés les outils de développements “structurés” JavaScript :

Permettant de construire de vraies applications JavaScripts, avec des notions de modèles, de vues, de contrôleurs, de templates. Templates permettant ainsi de mettre en forme d’informations issues du serveur de manière brute (JSON / XML mais surtout JSON !) via la consultation de service Web REST.

L’évolution des machines virtuelles JS permet de nos jours de faire tourner des applications en 3D Temps Rééls (grâce au WebGL ), de concurrencer des plateformes dédiées comme Flash, Silverlight (ExtJS). (ROME) Voir même émuler un noyau linux dans votre navigateur ^^

Alors pourquoi continuer à ignorer une telle technologie, c’est pas comme si c’était nouveau … Tout cela est selon moi, un virage technologique Web et d’autres plateformes (Mobiles) qu’il ne faut pas louper, au risque de devenir vraiment obsolète sur le marché du travail.

Je ne suis volontairement pas rentré dans les détails, car je pense qu’il faudrait bien plus qu’un article pour vous parler des choses que l’on peut faire avec tout ça, chaque élément aura son article associé.

La sécurité informatique une nécessité ou un paradoxe de l’utilisateur ?

Voila plus de deux ans que j’ai quitté le monde de la sécurité informatique, pour me diriger vers le développement. Et je me pose une simple question : La sécurité informatique est-ce une nécessité ou un paradoxe induit de son utilisation ?

Pour reformuler, faut-il protéger un utilisateur de son système ou bien protéger le système de l’utilisateur ?

De nos jours, la sécurité est mal comprise : souvent comme une série de contraintes appliquées aux utilisateurs d’un SI.

Il suffit d’aller dans un hôtel en semaine, équipé d’un ordinateur + wifi, pour obtenir des tas d’informations sur les sociétés, où les VRP présents résident. Ils se retrouvent sur un wifi en clair (Hôtel responsable dans ce cas), effectuant des transactions POP/IMAP (Auth PLAIN biensûr ! C’est de la simple Base64). Je trouve ça particulièrement navrant pour certaines sociétés dont le fer de lance est la sécurité de l’information.

Faut-il se dire que c’est l’entreprise qui ne protège pas la connexion du VRP, ou le VRP lui même qui par négligence / confiance / méconnaissance utilise des médias non sécurisés pour échanger des informations stratégiques.

Mon expérience me dit qu’en fait c’est souvent les deux cas :

  • l’entreprise qui ne consacre pas le temps nécessaire à la sécurité du SI,
  • mais aussi aux employés, qui ne sont pas sensibilisés / formés.

Ne serait-ce qu’utiliser un simple transport chiffré (SSL/TLS), pour éviter (ralentir) l’interception d’information. Souvent la même réponse, on verra plus tard ! Mais quand l’évènement sécurité arrive il est trop tard ! C’est des mesures préventives, pas des mesures a posteriori !

Vous me direz, bien sûr personne ne se balade avec un kismet, wireshark dans les hôtels ! Moi je réponds comment en avez vous la preuve ? Sans pour autant tomber dans la paranoïa !

L’espionnage est une réalité, même au culot, j’ai déjà été contacté par un soi disant représentant de café, qui souhaitait simplement entrer pour faire une dégustation, au final il s’est avéré être un journaliste. Il suffit d’avoir les yeux ouverts pour voir quantité d’informations. D’où le principe de mise en place d’un espace d’accueil clôt immédiat à la porte.

Pour se recentrer sur le sujet, l’utilisateur final, pas un espion, doit connaître les dispositifs de sécurité mis en place par l’entreprise, et surtout pourquoi ils sont là ! Et d’un autre coté, l’entreprise doit comprendre pourquoi l’information doit être protégée.

La négligence d’un seul des protagonistes risquent de rendre public des informations privées, pas forcément exploitable, mais qui pourraient servir à élaborer un scénario d’approche, dans le but d’en obtenir d’autres plus précises (produits, marchés, clients, etc.)

  • Vous êtes responsable d’un SI, pensez à cette phrase : La sécurité est un métier comme un autre, pas forcément le vôtre !
  • Vous êtes utilisateur du SI : Vous êtes la faiblesse du système, pensez-y !

Réferences :

Merci XSL-FO !

Pour des besoins professionnels, j’ai du apprendre à utiliser XSL-FO, au debut j’ai avais vraiment pas envie, mais bon au final si on a une bonne connaissance HTML / CSS2, on s’en sort sans problème.

Du coup j’ai ajouté à partir du XML de mon CV, 3 cibles au travers de XSL-FO :

J’ai essayé de me rapprocher le plus possible du rendu HResume HTML, mais sans les possibilités CSS3 (l’ombre du texte).

Il faudrait que je documente un peu ma machine de génération, peut être pour vous, mais surtout pour moi ^.