Contexte
- consultant indépendant (société OQube), spécialisé dans le génie logiciel et les méthodes agiles
- On va parler de
- On ne va pas parler de
Objectif de la session
- donner des pistes pour améliorer les pratiques de programmation en javascript
- ... inpirées des techniques mises en oeuvre dans jQuery
- ... pour produire du code plus lisible au moyen de techniques telles que Domain Specific Language
- ... et de meilleure qualité par le Développement Dirigé par les Tests
...et ses règles
- l'âge du conducteur doit être supérieur ou égal à 18
- le nombre d'années de permis ne peut être supérieur à l'âge du conducteur moins 18
- le nom est en majuscules
Version old-school
function validform(){
var form = document.getElementById('testform');
var nbYearDl = parseInt(document.getElementById('nbYearDL').value(),10);
var age = parseInt(document.getElementById('age').value(),10);
var nom = document.getElementById('name');
if(!nom.match(/[A-Z-]*/)
|| age < 18
|| (age - nbYearDL) < 18)
return false;
};
document.getElementById('testform').onsubmit = "javascript: validform();";
Version jQuery
previous$(function() {
$('#testform')
.submit(function() {
var nbYearDl = $("#nbYearDL",this);
var age = $("#age",this);
var nom = $('#name',this);
if(!nom.match(/[A-Z-]*/)
|| age < 18
|| (age - nbYearDL) < 18)
return false;
});
});
Version DSL
previousvar rules = function(form) {
with(form) {
age.must(beANumber().and(range().from(18)));
name.must(match("^[A-Z-]+$"));
nbYearDL.must(beANumber().and(range().from(0).to(function() {
return age.value - 18; })));
}
}
... sans le bruit
previousvar rules =
with form
age must beANumber and range from 18 ;
name must match "^[A-Z-]+$" ;
nbYearDL must beANumber and range from 0 to
age value - 18; ;
Règles de base
previousvar regexp = new ValidateRegex("^\\d+\\w+$");
var check = regexp.isOk("113dfsf121");
assert(check.result,"'113dfsf121' is ok");
check = regexp.isOk(" 113dfsf121");
log(check.message);
assert(!check.result ,"' 113dfsf121' is not ok");
var range = new ValidateRange().from(12).to(25);
assert(range.isOk(14).result ,"'14' is ok");
assert(!range.isOk(25).result ,"'25' is not ok");
Un intervalle accepte des fonctions
previousvar lb = 12;
function lbound() {
return lb;
};
function ubound() {
return lb * 10;
};
var range = new ValidateRange().from(lbound).to(ubound);
assert(range.isOk(14).result ,"'14' is ok");
lb = 15;
assert(!range.isOk(14).result ,"'14' is not ok anymore");
On peut savoir si un champ est un nombre
previousassert(beANumber().isOk(14).result ,"14 is a number");
assert(beANumber().isOk("14").result ,"'14' is a number");
assert(beANumber().isOk("f14").result ,"'f14' is not a number");
assert(beANumber().isOk(NaN).result ,"NaN is not a number");
On peut combiner les champs entre eux
previousvar and = new And(new ValidateRange(10,13), new ValidateRange(8,11));
assert(!and.isOk(12).result,"12 is ko");
assert(and.isOk(10).result,"10 is ok");
var or = new Or(new ValidateRange(10,13), new ValidateRange(8,11));
assert(!or.isOk(13).result,"13 is ko");
assert(or.isOk(9).result,"9 is ok");
... de manière arbitrairement complexe
previousvar not = new Not(new ValidateRange(10,13));
assert(not.isOk(13).result,"13 is ok");
assert(!not.isOk(10).result,"10 is ko");
var complex = new And(new Not(new ValidateRange(10,13))
,new Or(new ValidateRange(7,10)
,new ValidateRange(13,18)));
assert(complex.isOk(13).result,"13 is ok");
Le test d'acceptation
previousvar rules = function(form) {
with(form) {
age.must(beANumber().and(range().from(18)));
name.must(match("^[A-Z-]+$"));
nbYearDL.must(beANumber().and(range().from(0).to(function() {
return age.value - 18; })));
}
}
$('#testform').checkWith(rules,'#errorslist')
$('#testform')
.find('input[name=nbYearDL]').val("6").end()
.find('input[name=age]').val("23").end()
.find('input[name=name]').val("Dupont");
$('#testform').submit();
assert($('#errorslist>li').size() == 2,'two errors expected');
Ressources
- Une vision historicisée du code
- Les sources:
Motivation
- Rendre le code plus expressif, capable d'être compris par celui que ne l'a pas écrit
- Rendre le code plus proche du domaine métier concerné: exprimer le quoi plutôt que le comment directement dans l'implantation
- donc, rendre le code plus maintenable, plus évolutif, plus souple...
Appliquer un style sur un élément
previous$('div#titre').css('color', 'red');
setTimeout(function() {
$('div#titre').css('color', 'white');
},2000);
assert($('div#titre').css('color') == 'red');
Fonctionne aussi pour un ensemble d'éléments
previous$('input[type=button]').css('border', '2px solid blue')
.add('input[type=submit]').css('background-color', 'lightgreen');
assert($('#run').css('background-color') == rgb(144, 238, 144));
DSL Embarqués
- language embarqué dans un langage hôte, eg. le javascript. On utilise la syntaxe et la sémantique de l'hôte pour exprimer les concepts de la cible
- avantages: on a à sa disposition tous les outils du langage hôte (analyse syntaxique, compilateur, tests unitaires,déboggeurs...)
- inconvénients: la syntaxe est contrainte par l'hôte, ce qui peut parfois limiter l'expressivité, diminue le ratio signal/bruit
functional.js
previous// DSL for functional programming in JS
Functional.install();
var droite = 'x y -> x+2*y'.lambda();
assert(droite(2,3) == 8,"evaluate a lambda");
var square = '_ -> _*_'.lambda();
assert(square(3) == 9,"anonymous arguments");
processing.js
previous
class Spin
{
float x, y, speed;
float angle = 0.0;
Spin(float xpos, float ypos, float s) {
x = xpos;
y = ypos;
speed = s;
}
void update() {
angle += speed;
}
}
DSL Externes
- langage possédant une syntaxe et une sémantique propre. Manipulé sous forme de chaînes de caractères ou de donnéesarbitraires dans un langage hôte
- avantages: expressivité et précision plus grande, on ne dit que ce qui est pertinent pour le domaine concerné, SNR maximal
- inconvénient: plus lourd à mettre en oeuvre, nécessite analyse syntaxique, pas d'outils
Références

Remerciements
- Perrick Penet pour m'avoir incité à venir ici
- l'équipe de No Parking pour ses commentaires constructifs
- Christophe Thibaut