Catégorie : développement

Tout ce qui concerne le développement en général, que ce soit des choses générales, ou des choses bien précises. Cela va de la gestion de projet à la recherche du fonctionnement de pointeurs en C.

Vim: comment remplacer tous les mots par celui sous le curseur?

Je n’ai pas été bien loin, vous trouverez la version anglaise ici.

:%s/mot/<c-r><c-a>/g

Remplacer chaque occurrence de ‘mot’ avec le mot sous le curseur (le mot est délimité par des espaces ou des retours à la ligne).

<c-r><c-a> signifie qu’il faut appuyer sur Ctrl et R puis sur Ctrl et A.

Le mot sous le curseur sera automatiquement inséré dans la ligne de commande exactement comme il apparaît à l’écran.

Le côté génial, c’est que vous pouvez enregistrer cela dans une macro et il se souviendra du principe !

Exemple concret : j’ai une centaine de partenaires auquels je dois appliquer la même opération SQL. Voilà comment je me suis servi de la fonctionnalité :

  • J’ai mis toute la liste des partenaires dans un fichier, un partenaire par ligne ;
  • J’ai crée une seule fois, pour le partenaire 1, la série de commandes SQL ;
  • Ensuite, j’ai copié collé une centaine de fois cette même série pour tous les partenaires (sous vim: « chiffre+@a » et il joue chiffre fois la macro « a », donc ça ne m’a pris que le temps de taper une dizaine de touches) ;
  • J’ai enregistré la macro suivante, que je vous explique en français : «sélectionne la série de commande courante, monte d’une ligne pour aller sur le nom du partenaire courant, et remplace toutes les occurences de «partenaire1» par le mot sous le curseur.

Ensuite, une fois la macro enregistrée, j’ai tapé « 100@a » = jouer 100 fois la macro « a ».

Et voilà.

Beaucoup de temps de gagné !

PhpDocumentor 2 : howto et résultats exceptionnels

Après plusieurs échanges avec le développeur principal de PhpDocumentor 2, celui-ci a corrigé plusieurs bogues et maintenant la version alpha tourne, et j’ai pu la lancer sur mon framework.

Résultats tout simplement exceptionnels (pas pour mon framework, mais pour PhpDocumentor).

Ce qui m’a le plus bluffé c’est le schéma de diagramme de classes : non seulement il est beau et pratique, mais il est clair.

Donc une vue globale de mon diagramme de classes :

Image petite du diagramme de classes

Et quand on clique pour zoomer, le rendu est tout aussi exceptionnel :

Image petite du zoom du diagramme de classes

C’est pratique, grâce à ça, je vois tout ce qui manque. Par exemple, les ListeXXX ne sont pas dans une classe générique, alors que les ItemXXX oui, des petites évolutions à faire pour rendre un peu tout ça plus propre, mais c’est un bon début !

Ensuite, viennent les classes et la documentation générée : de la même façon, si tout est correctement organisé, le rendu est vraiment très bon et la documentation est enfin utilisable !

Image petite de l'exemple de documentation de classes

jQuery Mobile : comment forcer le rechargement d’une page ?

Si vous faites des boutons de navigation, votre code jQuery Mobile va ressembler à ceci :

<a href="/">Home</a>

Le seul hic, c’est qu’en cliquant dessus, le navigateur ne va pas recharger réellement la page, il va se baser sur son cache, en supposant que la page n’a pas changé. Ce qui peut poser problème. Par exemple, sur mon site mobile, lorsqu’un commande est validée, je vide le panier côté serveur et je fais un lien vers la page racine, le « home ». Avec le code précédent, le navigateur ne recharge pas réellement la page, et garde le panier toujours « rempli » car il garde les informations et variables JavaScript en mémoire. Dans ce cas, il faut forcer au rechargement de la page. Comment faire ?

Grâce à l’astuce de ce site, voici la solution : il suffit d’ajouter cette balise : rel="external".

Et la page sera rechargée.

<a href="/" rel="external">Home</a>

En espérant que cela aide du monde !

JavaScript, jQuery et closure: petit exercice à faire

Ci suit un petit exercice JavaScript: copiez collez les deux morceaux de code qui suivent dans une page jsfiddle.net

Si vous examinez le code JavaScript, il est très simple : l’objectif de l’exercice semble simple : il faut juste faire « flasher » les libellés les uns après les autres (c’est là que la difficulté se trouve).

Seul problème, avec le code qui suit et semble correct : ça ne fonctionne pas. L’exercice est… de trouver la solution en JavaScript.

Ne regardez pas la fin, car il y a la solution !.

Code HTML :

<table id="s-inscrire-infos">
<tbody>
<tr>
<td id="label-nomprenom"> <span><label for="label-nomprenom"><b>Nom et prénom&nbsp;:&nbsp;</b></label></span> </td>
<td> <span> <input type="text" name="nomprenom" id="nomprenom" class="form-nom-prenom" size="50" maxlength="50" /> </span> </td>
</tr>
<tr class="erreur" id="erreur-nomprenom" style="display:none">
<td colspan="2">* Il faut renseigner votre nom et prénom</td>
</tr>
<tr>
<td id="label-adresse1"> <span> <label for="label-adresse"> <b>Adresse ligne 1 :&nbsp;</b> <br /> <font size="-2">(ou nom de la société)&nbsp;</font> </label> </span> </td>
<td> <span><input type="text" name="adresse1" id="adresse1" class="form-adresse" size="50" maxlength="60" /></span> <br /> <span class="tiny">Rue, voie, boîte postale, nom de société</span> </td>
</tr>
<tr class="erreur" id="erreur-adresse1" style="display:none">
<td colspan="2">* Il faut au moins renseigner une ligne</td>
</tr>
<tr>
<td id="label-adresse2"> <span><label for="label-adresse"><b>Adresse ligne 2 :&nbsp;</b> <br /> <font size="-2">(facultatif)&nbsp;</font> </label></span> </td>
<td> <span><input type="text" name="adresse2" id="adresse2" class="form-adresse" size="50" maxlength="60" /></span> <br /> <span class="tiny">Appartement, bâtiment, étage, digicode, cedex, etc.</span> </td>
</tr>
<tr>
<td id="label-cp"> <span><label for="label-cp"><b>Code postal&nbsp;:&nbsp;</b></label></span> </td>
<td> <span> <input type="text" name="cp" id="cp" class="form-cp" size="20" maxlength="20" /> </span> </td>
</tr>
<tr class="erreur" id="erreur-cp" style="display:none">
<td colspan="2">* Il faut renseigner votre code postal</td>
</tr>
<tr>
<td id="label-ville"> <span><label for="label-ville"><b>Ville&nbsp;:&nbsp;</b></label></span> </td>
<td> <span> <input type="text" name="ville" id="ville" class="form-ville" size="25" maxlength="50" /> </span> </td>
</tr>
<tr class="erreur" id="erreur-ville" style="display:none">
<td colspan="2">* Il faut renseigner votre ville</td>
</tr>
<tr>
<td id="label-tel"> <span> <label for="tel"><b>Numéro de téléphone&nbsp;:&nbsp;</b></label> </span> </td>
<td> <span> <input type="text" name="tel" id="tel" class="form-tel" size="15" maxlength="20" /> </span> </td>
</tr>
<tr class="erreur" id="erreur-tel" style="display:none">
<td colspan="2">* Il faut renseigner votre téléphone</td>
</tr>
<tr>
<td colspan="2" class="form-submit"> <input type="submit" value="Valider" /> </td>
</tr>
</tbody>
</table>

Code JavaScript :

jQuery.fn.getBg = function() {
return $(this).parents().filter(function() {
var color = $(this).css('background-color');
return color != 'transparent' && color != 'rgba(0, 0, 0, 0)';
}).eq(0).css('background-color');
};

function flash(id, font_color, bg_color, nb) {
var bc = $(id).getBg();
var cl = $(id).css('color');
var mx = parseInt(nb);
if (mx <= 0) {
mx = 1;
}
for (var i = 0; i < mx; i++) {
$(id).animate({
backgroundColor: bg_color,
color: font_color
}, 200).animate({
backgroundColor: bc,
color: cl
});
};
}

dataMessage = new Array("#erreur-nomprenom", "#erreur-adresse1", "#erreur-cp", "#erreur-ville", "#erreur-tel");
var theQueue = $({});
for (key in dataMessage) {
var m = dataMessage[key];
if (m.indexOf('#erreur') == 0) {
console.log('should add:'+m);
theQueue.queue('flash', function(next) {
console.log('i will flash:'+m);
$(m).fadeIn('slow', function() {
flash('#label-' + this.id.substr(7), "#ffffff", "#aa0000", 3);
next();
});
});
}
}
theQueue.dequeue('flash');

Solution :

var theQueue = $({});
for (key in dataMessage) {
var m = dataMessage[key];
if (m.indexOf('#erreur') == 0) {
console.log('should add:' + m);
var toFlash = (function(m) {
return function(next) {
console.log('i will flash:' + m);
$(m).fadeIn('slow', function() {
flash('#label-' + this.id.substr(7), "#ffffff", "#aa0000", 3);
next();
});
}
})(m);
theQueue.queue('flash', toFlash);
}
}
theQueue.dequeue('flash');

Eh oui pas si évident que ça à trouver !

Merci à Julien pour son aide !

Si vous avez d’autres solutions n’hésitez pas à les mettre dans un commentaire en bas !

Php : day of week – jour de la semaine. Astuce

Oui, on peut facilement imaginer plusieurs choses :

Les jours de la semaine sont numérotés de 1 à 7. Faux.

Ils sont numérotés de 0 à 6.

Enfin pour l’astuce qui pourrait certainement vous faire gagner du temps :
Les jours de la semaine commencent à lundi. Faux.

Si on lit la documentation : 0 (pour dimanche) à 6 (pour samedi).

Bon à savoir !

Sencha / ExtJS : comment garder une colonne triée avec une grid ?

Quand on fait une grille de données (datagrid) et qu’on la lie avec un magasin (store) c’est facile.

On peut autoriser à trier par colonnes dans la datagrid.

Seul problème : si le store est un stocké sur le serveur, il fait une seule fois l’appel et ensuite c’est la datagrid qui gère les tris.

Quand on modifie un enregistrement, il est envoyé au serveur, le serveur l’enregistre, et renvoie le résultat de ce qu’il a enregistré. Généralement, il renvoie exactement ce qu’il a reçu. Le seul problème, c’est qu’au retour, la grille ne rafraichit pas l’ordre de tri selon les colonnes qu’on a choisies.

Exemple concret : vous avez une grille avec plein de noms. Vous cliquez sur la colonne « nom », pour la trier par ordre alphabétique. Vous changez le nom « Albert » par « Zoé ». Voici ce qu’il se passe :

  • Le store envoie id=54, nom="Zoé" au serveur ;
  • Le serveur fait la modification en base, et renvoie id=54, nom="Zoé" en retour ;
  • Le store reçoit id=54, nom="Zoé", fait son changement en interne et le transmet à la datagrid ;
  • La datagrid se rafraichit mais ne change pas le tri et laisse "Zoé" à la même place.

La solution : dans le store, lors de l’événement qui signale que le résultat de l’écriture a été intégré (« write« ) il faut forcer l’appel à sort(); qui sera répercuté sur la datagrid automatiquement.

Voici mon code (raccourci à l’extrême sur ma classe de store qui gère les exceptions et plein d’autres choses) :

Ext.define('Ext.data.StoreHandleErrors', {
    extend: 'Ext.data.Store',
    alias: 'data.storehandleerrors',

    constructor: function(config) {
        this.callParent([config]);

        this.on(
            'write',
            function(me, opts) {
                this.sort();
            },
            this
        );
    }
});

PDO, MySQL et erreurs détaillées : comment faire

Voici mon ancien code d’exécution des requêtes SQL :

$stmt = self::$_pdo->prepare($sql);
if ($stmt===false) {
}   
foreach ($tab as $key=>$valeur) {
    $stmt->bindValue($key, $valeur);
}   
$stmt->execute();
if ($stmt===false) {
    throw new Exception(
        "Erreur execution de la requete :\n\"".$sql."\"\n".
        "Paramètres de la requete :\n\"".var_export($tab, true)."\"\n".
        "Details de l'erreur : \n".var_export(self::$_pdo->errorInfo(), true)
    );
}

Le seul (gros) problème, c’est sur erreur d’exécution, il n’y avait aucune explication claire (détail = erreur 0x00). J’ai trouvé la solution : il faut dire de lever une exception si erreur :

self::$_pdo->setAttribute(
    PDO::ATTR_ERRMODE,
    PDO::ERRMODE_EXCEPTION
);

Et maintenant, tout problème d’exécution de query lève une exception qui contient une erreur vraiment détaillée et utile de la requête.

ExtJs : dériver un Store « générique » et s’en servir

Ça fait plus d’une journée que je cherche comment faire un Store générique, c’est à dire que j’ai plusieurs Store qui sont tous basés sur le même modèle avec le même type de proxy, etc.

Donc au lieu de faire un copier coller pour chaque Store, j’ai cherché comment en faire un « générique » auquel je pourrai appliquer une configuration « par défaut ».

Voilà le code complet résultat, avec les fonctions qui gère les erreurs possibles renvoyées par Php (session expirée, problème d’écriture en base de données, etc).

Ce qui m’a pris le plus de temps à trouver c’est que pour « surcharger » le constructeur, ça n’est pas la fonction classique « initComponent: function(){ } » mais la fonction de base "constructor: function(config) { }".

Il ne faut, de plus, surtout pas oublier d’appeler le parent, non pas via this.callParent(); mais via this.callParent([config]);.

Ci suit du code, le code de plus d’une journée de travail, en deux parties (j’espère qu’il sauvera du temps à des personnes, ou qu’il les mettra sur la bonne voie !) :

  • première partie = la surcharge
  • seconde partie = exemple d’utilisation de cette surcharge

Première partie : code de la classe

Ext.define('Ext.data.StoreHandleErrors', {
    extend: 'Ext.data.Store',
    alias: 'data.storehandleerrors',

    constructor: function(config) {
        /* (!!) Réécriture par dessus certaines propriétés
         *      du proxy : si jamais elles existent déjà,
         *      elles vont être réécrites.
         */
        config.autoLoad= true;
        config.autoSync= true;
        config.proxy.type= 'ajax';
        config.proxy.reader= {
            type: 'json',
            successProperty: 'success',
            root: 'data',
            messageProperty: 'message'
        };  
        config.proxy.writer= {
            type: 'json',
            writeAllFields: true,
            root: 'data'
        };  
        config.proxy.listeners= {

            exception: function(proxy, response, operation) {

                var error=operation.getError(),
                    title='Erreur du serveur';

                if (error instanceof Array) {
                  error=error.join("
"); } switch(response.status) { case 200: if (response.responseText!='') { var b = Ext.JSON.decode(response.responseText); if (b.title) { title=b.title; } if (b.success==false) { if (b.timeout==true) { windowLoginPanel.show(); } } } break; case -1: var error= 'Le serveur met trop de temps à répondre'+ '

'+ 'On ne peut rien faire, essayez '+ 'd\'actualiser la page.'; break; case 500: var error= 'Le serveur a une erreur interne.'+ '

'+ 'On ne peut rien faire, essayez '+ 'd\'actualiser la page.'; break; default: var error= 'Erreur renvoyée par le serveur non gérée.
'+ 'Détails :
'+ response.statusText+ '

'+ 'On ne peut rien faire, essayez '+ 'd\'actualiser la page.'; break; } Ext.MessageBox.show({ title: title, msg: error, icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK }); } }; this.callParent([config]); this.on( 'write', function(proxy, operation) { if ( (operation.action == 'create') || (operation.action == 'update') ) { var m = this.getById( parseInt( operation.resultSet.records[0].internalId ) ); } else if (operation.action == 'destroy') { var m = this.getAt(0); } if (m) { this.panelGridEtEdit.gsGrid.getSelectionModel().select(m); } else { this.panelGridEtEdit.gsGrid.getSelectionModel().deselectAll(); } Ext.example.msg( Ext.String.capitalize(operation.action), operation.resultSet.message ); }, this ); } });

Seconde partie : utilisation de la classe

    var storeAdresses = Ext.create('Ext.data.StoreHandleErrors', {
        model: 'Intranet.Adresse',
        proxy: {
            api: {
                read: '/json/intranet/liste/adresses/',
                create:  '/json/intranet/item/adresse/?mode=create',
                update:  '/json/intranet/item/adresse/?mode=update',
                destroy: '/json/intranet/item/adresse/?mode=destroy'
            }   
        }   
    }); 

Php Code Sniffer : changer l’indentation

PHPCodeSniffer est un super outil de vérification de qualité de code.
Le seul souci c’est qu’il vérifie en ayant une indentation de 4.

Tous mes sources sont basés sur une indentation de 2.

La solution :

  • chercher où se trouve le fichier :
    CodeSniffer/Standards/Generic/Sniffs/WhiteSpace/ScopeIndentSniff.php
  • éditer à la main le fichier et changer la valeur :
    public $indent = 4;
    en :
    public $indent = 2;

Pour information, mon fichier se trouvait ici (Ubutunu 10.04) :
/usr/share/php/PHP/CodeSniffer/Standards/Generic/Sniffs/WhiteSpace/ScopeIndentSniff.php

La seconde modification c’est pour les variables passées à l’intérieur des fonctions : de la même façon le code est censé avoir une indentation de 4.

C’est dans le fichier :
CodeSniffer/Standards/PEAR/Sniffs/Functions/FunctionCallSignatureSniff.php

Qu’il vous faudra modifier :
$expectedIndent = ($functionIndent + 4);
par :
$expectedIndent = ($functionIndent + 2);

Pour information, mon fichier se trouvait ici (Ubutunu 10.04) :
/usr/share/php/PHP/CodeSniffer/Standards/PEAR/Sniffs/Functions/FunctionCallSignatureSniff.php

Linux, MySQL, utf8 : requêtes et queries : comment éviter le décalage du résultat

Peut être avez-vous déjà rencontré ce problème : vous faites toute une base de données en utf-8.

En suivant les recommandations ici, c’est facile. Le seul problème, et ils ne donnent pas de solution, c’est que lorsqu’on fait les requêtes, les résultats sont bien renvoyés en utf-8 mais il y a un décalage s’il y a des accents :

Image qui montre un décalage de résultat d'une requête mysql en ligne de commande

Comment faire pour éviter ce décalage ? Facile. La solution ici.

En fait dans votre fichier de configuration, il faut aussi configurer le client, pas uniquement le serveur.

Il faut ajouter la directive « default-character-set = utf8 » dans le fichier de configuration :

sudo vim /etc/mysql/my.cnf

[client]
port    = 3306
socket    = /var/run/mysqld/mysqld.sock
default-character-set = utf8

Et puis relancer le service :

sudo service mysql restart

Et voici le résultat : tout est rentré dans l’ordre :

Image qui montre le problème résolu de décalage de résultat d'une requête mysql en ligne de commande