Je suis développeur web freelance et propose des formations à Symfony2 ! Contactez-moi pour en discuter.

Même si les bases de données NoSQL ont la côte en ce moment, les bases de données SQL ont encore de beaux jours à vivre devant eux. En effet, dans beaucoup de situations, il est assez facile de sortir des statistiques avec quelques lignes de SQL. Voici quelques astuces que j’ai découvert il y a quelques temps, et qui sont très pratiques pour sortir des chiffres.

Assez rapidement, lorsqu’on fait du SQL, on a besoin de récupérer le nombre d’éléments dans la base qui correspondent à un certain critère. Pour faire des statistiques, comme par exemple extraire le nombre d’utilisateurs enregistrés dans la base par exemple. La réponse est classique :


select
count(u.id) as total
from
users u

Ok, c’était facile. Et si on veut le nombre d’utilisateurs qui ont été créés chaque mois ?

On peut utiliser un « group by date_trunc »

L’astuce, c’est d’utiliser date_trunc. date_trunc permet de ne conserver, dans une date, que la partie de la date qui nous intéresse. Si notre date est le 4 janvier 1981 à 17h23, lorsqu’on tronque au mois, date_trunc nous renverra le 1 janvier 1981 à 00h00. En combinant cela avec un group by, on peut obtenir le nombre d’utilisateur qui a été créé mois par mois :


select
count(u.id) as total,
date_trunc ('months', u.created_at) as considered_month
from
users u
group by date_trunc ('months', u.created_at)
order by date_trunc ('months', u.created_at)

Comme vous l’avez compris, le premier paramètre de date_trunc permet de définir où tronquer. En tronquant au jour ou bien à l’heure on obtient une granularité plus fine, mais également plus de données, ce qui n’est pas toujours ce dont on a besoin.

On obtient des résultats de ce genre, en supposant que nous ayons des utilisateurs entre avril et novembre 2013 :

total considered_month
3956 « 2013-04-01 00:00:00 »
3965 « 2013-05-01 00:00:00 »
3549 « 2013-06-01 00:00:00 »
3728 « 2013-07-01 00:00:00 »
8311 « 2013-08-01 00:00:00 »
6041 « 2013-09-01 00:00:00 »
6381 « 2013-10-01 00:00:00 »
3784 « 2013-11-01 00:00:00 »

En fait, maintenant, on aimerait bien connaitre, pour chaque mois, le pourcentage d’utilisateurs qui s’est connecté au moins 3 fois, par rapport au nombre total d’utilisateurs créés durant le mois. Ok, c’est un exemple tordu, mais c’est pour expliquer comment peut faire un pourcentage sur le nombre d’éléments d’un sous-ensemble de données d’un mois concerné par rapport au nombre d’éléments de l’ensemble de départ.

La structure sum (case …)

L’idée, c’est d’utiliser une structure avec SUM (case … ) pour gérer un compteur « à la main ». Dans l’exemple ci-dessous, vous pouvez voir qu’on compte le nombre d’éléments total avec count, et qu’on compte le nombre d’utisateurs ayant 3 connections en incrémentant un compteur nous même via SUM (case … ). Enfin, à la troisième ligne du select on calcule le pourcentage correspondant.


select
count(u.id) as total,
sum( case when u.nb_connection > 3 THEN 1 ELSE 0 END) as subset_count,
sum( case when u.nb_connection > 3 THEN 1 ELSE 0 END)::float * 100/ count(u.id)::float as percentage,
date_trunc ('months', u.created_at) as considered_month
from
users u

group by date_trunc ('months', u.created_at)
order by date_trunc ('months', u.created_at)

Dans la vraie vie, au lieu de stocker un entier correspondant au nombre de connections, on stockerait plutôt la date de connection dans une table séparée, mais c’est pour simplifier.

On obtient des résultats de ce genre :

total subset_count percentage considered_month
3956 334 8.44287158746208 « 2013-04-01 00:00:00 »
3965 343 8.65069356872636 « 2013-05-01 00:00:00 »
3549 628 17.6951253874331 « 2013-06-01 00:00:00 »
3728 456 12.2317596566524 « 2013-07-01 00:00:00 »
8311 1206 14.5108891830105 « 2013-08-01 00:00:00 »
6041 842 13.938089720245 « 2013-09-01 00:00:00 »
6381 818 12.8193073186021 « 2013-10-01 00:00:00 »
3784 800 21.1416490486258 « 2013-11-01 00:00:00 »

Il est à noter qu’avec PostgreSQL, on pourrait convertir directement le résultat de « u.nb_connection > 3 » entier pour faire la somme de manière plus concise, mais pas forcément plus simple à lire. De plus, l’exemple ci-dessus montre qu’on peut mettre plusieurs conditions (avec plusieurs « when » à l’intérieur du case), ce que la conversion d’un boolean ne montre pas forcément. Néanmoins, il est possible d’écrire la ligne qui suit :


select
count(u.id) as total,
sum( (u.nb_connection > 3)::integer ) as subset_count,
...

Bref, on a vu dans cet articles 2 structures pratiques pour extraire des chiffres à partir de votre base, en utilisant group by date_trunc et sum (case …). Ca a l’air de rien car les sommes et les moyennes sont des outils simples, mais ce sont également des outils très pratiques pour étudier l’évolution d’un business, ou anticiper les évolutions de structure à prévoir.

Bien que j’utilise git depuis un bon moment, j’ai longtemps été effrayé par l’usage de git stash, par peur de perdre du travail. A tort, car elle se révèle finalement très simple d’utilisation, et très pratique. Cette commande (to stash = réserver) permet de mettre de côté toutes les modifications en cours sans les commiter, pour pouvoir les reprendre plus tard.

Dans ma manière de travailler, c’est utile quand je travaille seul comme quand je travaille avec des collègues. Voici deux cas d’utilisation récents :

  • Je travaille sur une fonctionnalité avec un collègue. Il a besoin d’un correctif sur un bout de code que j’ai écrit avant pour pouvoir continuer à travailler. Je travaille sur une branche ‘feature’, et doit donc revenir sur la branche ‘dev’, dernière version stable de ma branche de développement, pour y apporter un correctif. Dans ma branche ‘feature’ actuelle, je suis en plein milieu d’un développement, je ne peux absolument pas lui fournir ce nouveau code à moitié terminé, ni faire un commit du code au milieu de son développement pour changer de branche. Je dois donc mettre le code sur lequel je travaille actuellement de côté, pour le reprendre par la suite.
  • Je travaille sur une fonctionnalité seul, en javascript par exemple. Lors de mon développement, je me rends compte qu’avant de pouvoir finir ce que j’ai commencé, je dois changer quelque chose ailleurs dans le code (sur la partie PHP qui va exposer une API par exemple). Comme j’aime avoir des commits unitaires et fonctionnellement séparés, je préfère avoir les modifications de l’API en PHP dans un commit, et celles du JS qui l’utilise ensuite. Comme j’ai déjà commencé à travailler sur le JS, je dois mettre mon code de côté.

Ce que j’ai longtemps fait dans ces situations, c’est de faire un commit temporaire. J’ajoutais mes fichiers modifiés, et je laissais un commentaire du type « temporary commit, edit later ». Plus tard, lorsque je revenais sur le code en question, et que le travail terminé, j’éditais mon commit via git commit –amend. Cette commande permet d’éditer le dernier commit en ajoutant/supprimer des fichiers, et en changeant le message de commit. Attention, c’est à utiliser avec soin si vous pushez/pullez du code souvent.

Plutôt que de committer du code temporaire (qui a finalement tendance à pourir l’historique avec des commits que j’oublie de nettoyer), la solution préconisée par git pour ce genre de choses, c’est git stash.

Cette commande permet de mettre de côté ses modifications pour les récupérer ultérieurement. Une fois exécutée, on peut ensuite changer de branche, faire ses modifications dans la nouvelle branche et revenir, ou bien faire ses modifications directement dans la branche où l’on a réservé les modifications, cela n’est pas un problème.

Avec un exemple d’utilisation, vous allez voir qu’il est très simple de s’en servir. Le point de départ : vous êtes en train de travailler, vous avez ajouté certains fichiers, modifiés d’autres… en vous devez mettre les modifications de côté. C’est parti, on stash.


git stash
Saved working directory and index state WIP on feature_branch: d9e2bb5 merged v5.3.4
HEAD is now at d9e2bb5 merged v5.3.4

Une fois que c’est fait, votre git status est vide.


[clem@clem:~/dev/currProject] git status -s
## feature_branch
# -> rien, les modifications se sont ajoutées à la pile de la réserve
[clem@clem:~/dev/currProject] git stash list
# Sur un écran à part, on peut voir que la pile
# de la réserve contient un élément: nos modifications

Vous changez de branche, faites ce que vous avez à faire, puis vient le moment où vous revenez sur la branche courante pour reprendre votre travail là où vous l’avez laissé. C’est le moment d’utiliser git stash pop.


[clem@clem:~/dev/currProject] git stash pop
# On branch feature_branch
# You are currently rebasing branch 'bh' on 'fb7b1dc'.
# (all conflicts fixed: run "git rebase --continue")
#
# You are currently bisecting branch 'bh'.
# (use "git bisect reset" to get back to the original branch)
#
# Changes not staged for commit:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
# modified: app/Berthe/ServiceXHR/ServiceXHRQuote.php
# modified: lib/Evaneos/Berthe/JsonServer.php
#
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (21f9583cd4159df5627307eada103e29fe165431)
[clem@clem:~/dev/currProject] git status -s
## feature_branch
M app/Berthe/ServiceXHR/ServiceXHRQuote.php
M lib/Evaneos/Berthe/JsonServer.php

Comme vous pouvez le voir sur les 2 commandes ci-dessus, git stash pop enlève les modifications de la pile, les met dans la branche courante, et supprime le sommet de la pile.

Le stash fonctionnant comme une pile, il peut arriver que l’on ne veuille pas récupérer ses modifications. Pour cela, on peut supprimer le dernier élément de la pile via git stash drop. Voici à quoi cela ressemble :


[clem@clem:~/dev/currProject] git stash
Saved working directory and index state WIP on feature_branch: d9e2bb5 merged v5.3.4
HEAD is now at d9e2bb5 merged v5.3.4
# Je sauvegarde les modifications
[clem@clem:~/dev/currProject] git stash drop
Dropped refs/stash@{0} (befd3f17f416548c30624a01118b609ebb1bc0a8)
# Je n'en ai plus besoin, je les supprime du stash.
[clem@clem:~/dev/currProject] git status -s
## feature_branch
# Ma branche courante est vide. Dans cet exemple, c'est comme
# si j'avais fait git reset HEAD --hard.

Et voila, non seulement c’est plus élégant que des commit temporaires, mais c’est aussi très simple d’utilisation.

Depuis quelques temps, j’ai un serveur chez DigitalOcean, qui me sert principalement pour bricoler des petites applis, et jouer autour de nouvelles technos. DigitalOcean m’a séduit par le rapport pricing/résultat. Même s’il y a des choses à redire, où s’il existe des offres similaires moins chères, je suis très satisfait de pouvoir créer/détruire des machines virtuelles en 2 clics. Cela correspond en fait totalement à mon cas d’utilisation, qui consiste à monter une machine pour tester une techno, bidouiller dessus, et la détruire quand j’ai fait le tour de ce que je voulais voir.

Par contre, il est du coup un peu pénible de devoir réinstaller sa machine virtuelle à chaque fois. Installer un LAMP basique avc phpmyadmin se fait assez vite, ça tient en quelques ligne de bash :


#!/bin/sh
apt-get update
#install apache
apt-get install apache2
#install mysql
apt-get install mysql-server
mysql_secure_installation
#install php
apt-get install php5 php-pear php5-suhosin php5-mysql
service apache2 restart
# install phpmyadmin
apt-get install phpmyadmin
ln -s /usr/share/phpmyadmin/ /var/www

Mais dès qu’on veut faire quoi que ce soit de plus évolué, la complexité augmente rapidement. En plus d’installer des paquets il faut aller modifier des fichiers de configuration, ce qui devient rapidement chronophage, et me détourne de l’essentiel, le développement. Avec l’habitude on finit par connaitre les écueils à éviter pour aller vite, mais on y passe quand même du temps.

C’est là où Vagrant devient intéressant. Couplés à PuPHPet et DigitalOcean, j’ai personnellement atteint le nirvana de l’installation de VM en à peine 2H.

Vagrant sert à piloter des machines virtuelles. On lui file un répertoire de configuration pour définir l’environnement à installer sur une machine virtuelle. PuPHPet est une appli web qui permet de construire sa configuration pour Vagrant via une IHM, ce qui évite de devoir lire la doc. On est forcément plus limité que si on faisait tout soi même, mais dans mon cas (déployer un environnement apache ou nginx avec mysql ou postgresql, suivant l’humeur), j’ai de bonnes bases pour pouvoir faire ce qui m’intéresse sans m’embêter.

De l’intérêt de Vagrant

En fait, mon cas d’utilisation (déployer des VM kamikaze) est loin d’être le plus intéressant. Là où Vagrant prend tout son sens, c’est lorsqu’on travaille en équipe, et que l’on a le besoin de pouvoir avoir finalement de nouvelles machines identiques. Par exemple, lorsqu’un nouvel arrivant doit installer sa machine et qu’on veut une belle machine toute neuve, sans avoir à copier l’ISO de celle d’un copain, sans faire d’efforts, et sans surprises.
Et même s’il y a une valeur ajoutée à ce qu’un nouvel arrivant installe sa machine et comprenne ce qui s’y trouve, lorsqu’on plante sa bécane, on a pas forcément envie de passer du temps à tout réinstaller. De plus, une installation automatique et scénarisée permet d’assurer que tout le monde a le même environnement, ce qui n’est pas le cas d’un installation manuelle (« j’ai pas réussi à faire les choses telles qu’elles étaient décrites dans le manuel, donc j’ai googlé une alternative »), et qui peut souvent poser problème.

Autre avantage, toute la configuration de la machine à déployer tient en quelque fichiers de configuration. On peut donc la modifier, la versionner, la partager, la livrer… beaucoup plus facilement qu’on le ferait avec un manuel d’installation.

Ok, on a compris, on fait comment ?

PuPHPet vous permet de déployer des machines en local, chez DigitalOcean ou Amazon EC2.
Je vais décrire les étapes par lesquelles je suis passé pour partir de rien, et déployer une machine virtuelle sur Digital Ocean. Avoir un compte chez eux (associé à une carte bancaire) est nécessaire pour aller jusqu’au bout, mais la logique est la même si vous voulez déployer ailleurs.

Configuration de la machine locale

Nous aurons besoin d’une clé SSH pour nous authentifier par la suite, créons la.

cd ~/.ssh
ssh-keygen -t rsa -C ""

Je l’ai appelé digital_ocean au lieu d’id_rsa.

Il est nécessaire d’installer vagrant et virtual box. Vous pouvez récupérez les derniers paquets sur http://downloads.vagrantup.com/
https://www.virtualbox.org/wiki/Linux_Downloads

Sur Ubuntu/Debian, on installe les fichiers .deb avec sudo dpkg -i *.deb

Il également installer le plugin pour digital ocean :


vagrant plugin install vagrant-digitalocean
vagrant box add dummy https://github.com/smdahlen/vagrant-digitalocean/raw/master/box/digital_ocean.box

Créer un projet


mkdir vagrant_env

C’est dans ce répertoire que nous mettrons le code de configuration utilisé par vagrant. En général, on fait un vagrant init pour démarrer un nouveau fichier de configuration, mais nous allons utiliser PuPHPet qui va faire ça pour nous.

Configurer digital ocean

Enregistrez la clé SSH que vous avez créé : https://cloud.digitalocean.com/ssh_keys

Il faut ensuite récupérer votre identifiant utilisateur et votre clé API : https://cloud.digitalocean.com/generate_api_key

Notez la clé API quelquepart, car elle n est plus affichée quand on revient sur la page et le seul moyen d’en avoir une est de la regénérer.

Configurez votre machine avec PuPHPet

Je vous laisse configurer votre machine aux petits oignons avec PuPHPet. N’oubliez pas de renseigner toutes les informations sur la configuration Digitalocean (clés API), nom de la machine, et type de machine.
Vous pouvez choisir le type de serveur (apache2/nginx), postgresql ou mysql, installer ou non phpmyadmin, la configuration de PHP, les packages divers à installer… c’est très simple.

Une fois terminé, un immense bouton permet de télécharger vos fichiers de configuration. Décompressez les dans un répertoire, vous devriez avoir l’arborescence suivante :


.
├── files
│   └── dot
├── hiera.yaml
├── puppet
│   ├── hieradata
│   │   └── common.yaml
│   ├── manifests
│   │   └── default.pp
│   └── Puppetfile
├── shell
│   ├── initial-setup.sh
│   ├── librarian-puppet-vagrant.sh
│   ├── os-detect.sh
│   ├── self-promotion.txt
│   └── update-puppet.sh
└── Vagrantfile

Dans le fichier puppet/hieradata/common.yaml, vous trouverez toutes les informations que vous avez spécifiées sur comment configurer votre machine. Dans le répertoire files/dot, vous pouvez ajouter un fichier .bashrc, .vimrc… qui seront déployées lors de l’installation de votre machine.

Normalement, on est prêt à déployer sa machine :

vagrant up

Ca n’a pas marché pour moi (machine sous Ubuntu 12.04), il fallait encore faire quelques légères modifications. La clé ssh n’a pas été bien prise en compte lorsque je l’ai précisé dans PuPHPet, j’ai du modifier

config.ssh.private_key_path = "~/.ssh/digitalocean" dans le fichier VagrantFile

Il a également fallu ajouter dans mon fichier .bashrc

export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt

puis dans le fichier VagrantFile, ajouter la ligne

provider.ca_path = "/etc/ssl/certs/ca-certificates.crt"

dans vm.provider

Voici donc un exemple de configuration qu’on peut avoir. Vous pouvez voir que c’est au final très simple, on dit ce dont on a besoin et comment le configurer, et vagrant se charge de l’installer pour nous. Grâce au plugin DigitalOcean, il crée même la machine pour nous.

Fichier VagrantFile


ENV['VAGRANT_DEFAULT_PROVIDER'] = 'digital_ocean'

Vagrant.configure("2") do |config|
config.ssh.private_key_path = "~/.ssh/digitalocean"
config.ssh.username = "clem"

config.vm.box = "dummy"

config.ssh.private_key_path = "~/.ssh/digitalocean"

config.vm.hostname = "plop"
config.vm.provider :digital_ocean do |provider|
provider.client_id = "id_digital_ocean_que_vous_avez_fourni"
provider.api_key = "cle_api_digital_ocean_que_vous_avez_fourni"
provider.image = "Debian 7.0 x64"
provider.region = "Amsterdam 1"
provider.size = "512MB"
provider.ca_path = "/etc/ssl/certs/ca-certificates.crt"
end

config.vm.synced_folder "./", "/var/www", id: "webroot"

config.vm.provision :shell, :path => "shell/initial-setup.sh"
config.vm.provision :shell, :path => "shell/update-puppet.sh"
config.vm.provision :shell, :path => "shell/librarian-puppet-vagrant.sh"
config.vm.provision :puppet do |puppet|
puppet.facter = {
"ssh_username" => "clem"
}

puppet.manifests_path = "puppet/manifests"
puppet.options = ["--verbose", "--hiera_config /vagrant/hiera.yaml", "--parser future"]
end

config.ssh.shell = "bash -l"

config.ssh.keep_alive = true
config.ssh.forward_agent = false
config.ssh.forward_x11 = false
config.vagrant.host = :detect
end

Fichier common.yml


---
vagrantfile-digital_ocean:
vm:
box: digital_ocean
hostname: nom_de_votre_machine
network:
private_network: 192.168.56.101
forwarded_port: { }
provider:
digital_ocean:
image: 'Debian 7.0 x64'
region: 'Amsterdam 1'
size: 512MB
client_id: id_digital_ocean_que_vous_avez_fourni
api_key: cle_api_digital_ocean_que_vous_avez_fourni
provision:
puppet:
manifests_path: puppet/manifests
options:
- --verbose
- '--hiera_config /vagrant/hiera.yaml'
- '--parser future'
synced_folder:
DXt5BtQJjHh3:
id: webroot
source: ./
target: /var/www
ssh:
host: null
port: null
private_key_path: ~/.ssh/digitalocean
username: clem
guest_port: null
keep_alive: true
forward_agent: false
forward_x11: false
shell: 'bash -l'
vagrant:
host: ':detect'
server:
packages:
- vim
- git
dot_files:
-
bash_aliases: null
_prevent_empty: ''
nginx:
vhosts:
1Zbx9ZeVKOfF:
server_name: awesome.dev
server_aliases:
- www.awesome.dev
www_root: /var/www/awesome.dev
listen_port: '80'
index_files:
- index.html
- index.htm
- index.php
envvars:
- 'APP_ENV dev'
php:
version: '55'
composer: '1'
modules:
php:
- cli
- intl
- mcrypt
- curl
- common
- gd
- fpm
- memcache
- memcached
- xcache
pear: { }
pecl:
- pecl_http
ini:
display_errors: On
error_reporting: '-1'
session.save_path: /var/lib/php/session
timezone: Europe/Paris
xdebug:
install: 0
settings:
xdebug.default_enable: '1'
xdebug.remote_autostart: '0'
xdebug.remote_connect_back: '1'
xdebug.remote_enable: '1'
xdebug.remote_handler: dbgp
xdebug.remote_port: '9000'
mysql:
root_password: votre_mot_de_passe_root_bdd
phpmyadmin: '1'
databases:
T7hssen8Pk2a:
grant:
- ALL
name: clem
host: localhost
user: clem
password: votre_mot_de_passe_bdd
sql_file: ''

Bref, j’ai vraiment été séduit par le rapport effort/résultat. Il m’aura fallu environ 1h30 pour générer cette configuration de machine que je peux redéployer à volonté, ce qui est à peine plus que le temps que ça m’aurait pris si je l’avais fait à la main. Sauf que maintenant, tous les déploiements suivants peuvent se faire via vagrant up, et que si je souhaite travailler avec des collègues sur les projets pour lesquels j’ai construit cette machine, je peux leur fournir la configuration exacte de la machine à installer.

La semaine dernière, j’ai réécrit pour Sublime Text 3 l’extension FormatSQL, qui me servait régulièrement dans la version 2, mais qui n’est plus compatible. L’objectif : prendre une chaine SQL mal formatée, et la formater de manière lisible. Le code final de mon petit plugin est sur Github.

J’ai eu étonnamment beaucoup de mal à comprendre comment démarrer. La plupart des bouts de code que j’ai trouvé sur les net, y compris sur le site officiel, ne fonctionnent que sous Sublime Text 2, et n’étaient pas compatibles.

Démarrer

Pour démarrer, c’est en fait très simple, encore faut-il le savoir : en cliquant sur Tools -> new Plugin, on obtient le code minimal d’un plugin, point de départ que je ne trouvais pas.


import sublime, sublime_plugin

class ExampleCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.insert(edit, 0, "Hello, World!")

C’est du python, ST3 embarque la version 3.3, d’où l’origine de conflits pour de nombreux packages, qui utilisent des librairies qui ne fonctionnent plus en python 3.3.

Exécuter le plugin

Quand on enregistre ce fichier, on est déjà positionné dans le bon répertoire (chez moi : ~/.config/sublime-text-3/Packages/User). On sauvegarde example.py, et sans redémarrer, on peut utiliser le nouveau plugin, de 2 manières :

Depuis la console :
on l’ouvre avec ctrl+`, et on tape view.run_command(‘example’). Le plugin s’exécute !
Depuis un nouveau binding :
On crée un nouveau raccourci, en allant dans Preferences -> Key bindings – User, où l’on ajoute :

{ "keys": ["ctrl+alt+f"], "command": "example" }

Maintenant, à chaque fois que l’on appuiera sur ctrl+alt+f, notre plugin s’executera.

Et maintenant, à vous de jouer !

Bon courage !

Une fonctionnalité très sympa de Sublime Text est pouvoir partager son écran en 2 (View -> Layout -> Columns: 2 par exemple).

Quand on a un écran un peu grand, cela permet par exemple de comparer des bouts de code quand on débuggue, ou pour adapter un exemple trouvé sur le net. Si l’écran est petit, ctrl+k, ctrl+b permet de masquer ou afficher la barre latérale le temps de faire ce que l’on souhaite.

Sur un clavier Azerty, le raccourci pour partager l’écran ne fonctionne pas (avec les produits Apple, il semble que ce soit OK). Le coupable ? Le raccourci. Il faut appuyer sur alt + maj + 2. Hors pour faire 2… il faut appuyer sur Maj. Sur nos claviers, l’éditeur de texte le plus sympa que je connaisse est donc perdu. La solution ? Redéfinir les touches nécessaires pour le binding en question. Allez dans Preferences -> Key bindings – User, et configurez les touches de la manière suivante :


[
{ "keys": ["alt+2"],
"command": "set_layout",
"args":
{
"cols": [0.0, 0.5, 1.0],
"rows": [0.0, 1.0],
"cells": [[0, 0, 1, 1], [1, 0, 2, 1]]
}
},
{
"keys": ["alt+1"],
"command": "set_layout",
"args":
{
"cols": [0.0, 1.0],
"rows": [0.0, 1.0],
"cells": [[0, 0, 1, 1]]
}
}
]

Ici j’ai seulement redéfini les associations de touches pour découper l’écran en 2 verticalement avec alt + 1 et alt + 2. C’est le seul que j’utilise, mais si vous voulez la liste complète (pour découper en 3, 4 parties, horizontalement comme verticalement), vous pouvez chercher la commande set_layout dans Preferences -> Key bindings – Default.

One more thing…

Un autre raccourci que je vous recommande d’ajouter au même endroit que le précédent. Si comme moi vous travaillez sur des gros projets, et naviguez dans l’arborescence à grand coup de cltr+p, vous avez du vous rendre compte qu’on a vite fait de ne plus savoir dans quel répertoire se trouve le fichier avec lequel on travaille.


{ "keys": ["ctrl+alt+r"], "command": "reveal_in_side_bar" }

A tout moment, appuyez sur ctrl+alt+r pour sélectionner le fichier courant dans la barre latérale et le mettre en surbrillance, et ainsi le retrouver dans l’arborescence. Des packages (notamment SyncedSideBar) permettent de faire cela, mais leur comportement ne me plait pas.

Ha, et tant que vous y êtes, jetez un oeil dans Preferences -> Settings – Default, et prenez 10mn pour adapter les paramètres de configuration à vos besoins dans Preferences -> Settings – User. Ces quelques détails peuvent vous changer la vie !

Lorsque l’on souhaite écrire du code concis, l’héritage permet de gagner du temps en factorisant le code dans une classe dont plusieurs vont hériter, et se partager les fonctionnalités.

Dans une application AngularJS, il est donc régulièrement nécessaire de faire hériter dans un contrôleur les fonctionnalités d’un contrôleur de base. Je ne parle pas ici d’imbrication, c’est-à-dire d’un « gros » contrôleurs dont une sous partie du DOM est gérée par un ou plusieurs autres contrôleurs, en charge de fonctionnalités restreintes localement. Je vais parler d’héritage au sens objet du terme, bien que cette notion soit un peu floue en JavaScript (j’entends déjà les puristes du fond qui hurlent, mais ça n’est pas l’objet de cet article).

L’héritage de contrôleurs est par exemple utile pour une application de type CRUD, où les divers contrôleurs vont vouloir réaliser des opérations similaires sur différents modèles, avec des particularités pour chacun d’entre elles. On va donc vouloir mettre l’essentiel des fonctionnalités partagées dans un contrôleur de base, et fournir le comportement spécifique dans les fils.

Voici comment je fais pour faire hériter des propriétés, des méthodes, le scope et ce qu’il surveille d’un contrôleur de base vers des contrôleurs fils. Tout part d’un contrôleur de base.


var BaseController = function($scope, DataStorage) {
this.scope = $scope;
this.DataStorage = DataStorage;
this.scope.pageNb = 1;

var me = this;
this.scope.$watch ('pageNb', function() {
me.getList();
me.scope.isAllselected = false;
});

this.scope.displayCreationPopup = _.bind (this.displayCreationPopup, this);
this.scope.hideCreationPopup = _.bind (this.hideCreationPopup, this);

this.scope.showCreationPopup = false;
}

BaseController.prototype.displayCreationPopup = function (){
this.scope.showCreationPopup = true;
}

BaseController.prototype.hideCreationPopup = function (){
this.scope.showCreationPopup = false;
}

Plusieurs choses sont à noter :
– Je définis les propriété (objets et fonctions) à faire hériter dans le prototype. Le prototype définit toutes les propriété communes à toutes les instances de BaseController. C’est donc très bien pour nous dans le cas présent, mais il faut faire attention à ce que l’on met dedans, ce n’est pas toujours ce que l’on va vouloir.
– Bien qu’il s’agisse d’un contrôlleur, on ne l’enregistre pas via angular.module(…).controller. En effet, c’est notre classe abstraite, elle ne contient pas le code métier que l’on souhaite exécuter tel quel, et ne contient pas à proprement parler de code complet.
– D’ailleurs, dans $scope.$watch, on fait appel à la méthode getList() qui n’est pas définie ! Je la définis plus tard, dans les contrôlleurs fils. Mais afin d’éviter de la duplication de code, comme tous les controlleurs font appel à getList lors d’un changement de numéro de page, le code métier correspondant est écrit ici.
– Je fournis le $scope en paramètres du contrôlleur de base, et un service de stockage. Comme le code n’est pas exécuté tel quel, DataStorage n’a pas besoin d’être un vrai service (en fait, il n’a même pas besoin d’être défini). Tous ce dont on a besoin, c’est d’avoir une variable dans laquelle appeler des méthodes. En java ou autre, on aura un type plus précis sur cette variablee, afin d’éviter de pouvoir y mettre n’importe quoi. Dans les divers classes filles, nous injecterons un vrai service, dépendant du contrôlleur dont nous avons besoin afin d’exécuter la logique métier.

Et maintenant, le code métier d’un exemple de classe fille.


// On hérite le contrôleur de celui de base.
var NewsController = function($scope, NewsStorage, $injector) {
$injector.invoke(BaseController, this, {$scope: $scope, DataStorage:NewsStorage});

this.scope.getList = _.bind (this.getList, this);
this.getList();
}
NewsController.prototype = Object.create (BaseController.prototype);

// Une fonction quelconque de récupération de liste de news
// Exemple de ce qu'on pourrait avoir avec de l'asynchrone type requête Ajax :
NewsController.prototype.getList = function (){
var me = this;
this.scope.displayLoader = true;
this.NewsStorage.getList (this.scope.pageNb, function(result){
me.scope.news = result.data.news;
me.scope.nbPages = result.data.nbPages;
me.scope.displayLoader = false;
}
);
}

// On injecte le contrôleur dans l'application
var module = angular.module ('myApp.controllers', ['common.controllers', 'common.stores', 'myApp.stores']);
module.controller('NewsController', [
'$scope',
'NewsStorage',
'$injector',
NewsController
]);

Les 2 lignes importantes d’un point de vue de l’héritage sont
$injector.invoke(BaseController, this, {$scope: $scope, DataStorage:NewsStorage});
et
NewsController.prototype = Object.create (BaseController.prototype);

La première injecte les données présentes dans le $scope du père (en lui fournissant au passage les bons services). Elle fournit aussi tout ce qui s’y rattache : on conserve le $scope.$watch par l’héritage.
La seconde ligne étend le prototype, c’est à dire qu’elle duplique les méthodes dans le contrôlleur fils. Oui, c’est du javascript, pas du C++ ou du Java hein, l’héritage reste ici une notion un peu abstraite (haha).

Cette fois ci, le contrôleur News est bien injecté dans l’application. On le configure, et on lui donne les bons services, c’est eux qui sont fournis au père.
On implémente la méthode getList(), que l’on peut imaginer faire un appel ajax ou dans un cache quelconque (applicatif ou localstorage, soyons fous) pour récupérer les données nécessaires.

Vu que les méthodes sont héritées, depuis la vue, on peut donc appeler showCreationPopup() et hideCreationPopup(). On peut également utiliser les propriété du scope, et rajouter les nôtres.

Vous pouvez au passage remarquer que je bind régulièrement mes fonctions via la librairie underscore sur l’objet this. Cela permet de ne pas perdre l’objet contrôlleur lorsque les appels se font depuis angular, qui utilise comme objet d’appel le $scope local et non le contrôlleur. Dans cet exemple, tous les bindings ne sont pas nécessaires, mais quand je fais un hideCreationPopup() depuis une vue, c’est le binding dont je viens de parler qui fait qu’on peut changer la valeur de showCreationPopup, et que ce changement se répercute sur la vue.

Cela permet d’isoler le code récurrent dans une classe de base afin d’en faciliter sa réutilisation, ce qui permet, pour les contrôleurs fils, de n’implémenter que la logique spécifique.

Apache bench est un outil qui sert à faire des tests de performances sur une URL donnée.
On l’installe via apt-get install apache2-utils, puis on s’en sert (par exemple) de la manière suivante :


ab -n x -c y url

Avec :

  • x le nombre de requêtes à lancer
  • y le nombre de requêtes concurrentes
  • url l’url à tester, qui doit finir par un /

Exemple :

ab -n 1000 -c 100 www.google.com/

On obtient ensuite un rapport avec notamment, le temps moyen de réponse, la bande passante consommée, le nombre de requêtes traitées par secondes… D’autres options sont possibles, et vous les trouverez dans les pages man.

Il est important de savoir que cet outil effectue uniquement une requête HTTP GET sur l’url concernée : dans l’exemple donné il va donc uniquement télécharger le HTML, mais pas les fichiers images, js et css externes, et ne les utilisera pas. Cela ne représente donc pas le ressenti réel de l’utilisateur, mais sert pour du dimensionnement.

On peut s’en servir de plusieurs manières. Sur une machine locale (ou la notion de bande passante n’a aucun sens), cela peut servir à tester l’effet de la mise en place d’un système de cache type Memcached ou Redis, ou bien pour voir si la configuration du reverse proxy tient la route. En production ou préproduction, cela permet de voir si la stack fait bien son travail. Mais surtout, Après avec effectué ce genre de requêtes, on regarde les outils de monitoring, et peut surveiller l’état de nombreux éléments : load, accès disques, etat de la base de données…

Attention, comme cet outil fonctionne url par url, il est important d’effectuer des tests sur plusieurs pages différentes, surtout les plus critiques (en terme de temps d’exécution et de fréquentation). Bien que votre home soit probablement la plus visitée, c’est aussi surement une des pages les mieux optimisées. D’autres pages un peu moins visitées peuvent être dangereuses pour les performances (lorsqu’elles ont de lourds calculs par exemples), et la santé de votre serveur.

L’idée de ce genre de d’outil, c’est surtout de regarder à quel moment le serveur va tomber en terme de charge. On effectue plusieurs tests avec des valeurs de plus en plus élevées, et on regarde à partir de quel moment le serveur ne tient plus. On fait ce genre de tests bien avant que le serveur n’atteigne de tels chiffres de fréquentation, ce qui permet d’anticiper les évolutions d’architecture à prévoir.

Je travaille en permanence avec plusieurs machines différentes pour me connecter à des serveurs de développement, de préproduction, à ma machine virtuelle ou à des machines de productions. Après avoir oublié plusieurs fois certains mots de passe, puis m’être planté une fois ou deux de terminal et avoir lanché des scripts sur la mauvaise machine, j’ai commencé à utiliser des codes couleurs pour les différentes machines que j’utilise. Et comme il est pénible de changer les codes couleurs à chaque connection, je me suis finalement mis à utiliser Revelation pour résoudre les deux problèmes en une fois.

L'astuce : une couleur pour chaque machine

L’astuce : une couleur pour chaque machine

Révélation est en effet un gestionnaire de mots de passe, qui sert à les centraliser, et à lancer diverses applications qui les utilisent. Un mot de passe maitre sert à protéger l’accès à des personnes non désirées (dans une certaine mesure). Je m’en sers donc pour me stocker les mots de pass de connection SSH, et pour me connecter directement aux machines distantes en SSH.

L’astuce pour ne pas me tromper de machine, c’est que chacune d’entre elle a une couleur différente. Rouge (comme danger) pour la machine de production, vert pour une VM, et ainsi de suite.

Pour faire pareil, il suffit d’installer Revelation (apt-get install revelation), puis de faire 3 choses :

Créer un profil pour les couleurs des différentes machines :

N'ayez pas peur d'avoir autant de profils que de machine

N’ayez pas peur d’avoir autant de profils que de machine

Faire la manipulation suivante (sur Ubuntu) pour vos différentes machines :
Ouvrir une console
Edition -> profils -> nouveau
On lui donne un nom (notez le, il va servir plus tard), et le fait se baser sur un profil déjà plaisant
Dans l’onglet couleur, il suffit de décocher « utiliser les couleurs du thème système », et de choisir des couleurs (de texte et d’arrière plan) qui conviennent.

Quand vous avez terminé, vous devriez avoir autant de profils que vous utilisez de machine différente, et chacune d’entre elle aura une couleur différente. Vous pouvez remarquer que je nomme mes profils sous la forme profilXXX, histoire de m’y retrouver.

Configurer les raccourcis dans Revelation :

N'oubliez pas de mettre le nom du profil dans "Domaine"

N’oubliez pas de mettre le nom du profil dans « Domaine »

Une fois qu’on a les différents profils, il faut ensuite configurer les différentes connection SSH, comme dans l’image de droite.
On crée un nouveau raccourci de type shell, avec le nom de son choix, puis on remplit le nom d’hôte, le nom d’utilisateur et le mot de passe nécessaire à la connection à son shell.

L’astuce pour lancer la connection avec le profil désiré, c’est de mettre le nom du profil dans le champ « domaine ». On va ensuite configurer Revelation pour utiliser cette information comme nous le souhaitons.

N’oubliez pas de sauvegarder votre configuration Revelation (ctrl+S, ou le gros bouton enregistrer)

Configurer l’utilisation des profils au lancement des connections SSH :

Dernière étape, il faut préciser à Revelation d’utiliser nos profils lorsque l’on lance un terminal. Pour celà, il suffit d’aller dans Editer -> Préférences -> Commandes de lancement, et dans la ligne SSH, de mettre ce qui suit :


gnome-terminal %(--profile=%d%) -x ssh %(-l %u%) %h

Cette ligne parle d’elle même, on lance un terminal avec une connection ssh dont les options sont les paramètres de configuration de la machine donnée, et on choisit le profil correspondant pour le terminal.

Terminé !

L’utilisation est simple : on ouvre sa configuration dans revelation, choisit une machine, et le mot de passe est copié dans le presse papier. Sous Ubuntu, on peut le coller avec clic-molette, on appuie sur entrée, et c’est bon, on est connecté à sa machine. Et voila, fini les pertes de mot de passe, et fini les commandes lancées sur la mauvaise machine. Merci à Antho pour l’astuce !

Des alias git utiles

18 juin, 2013

Depuis quelques temps, j’utilise les alias git pour un certain nombre de taches récurrentes, pour lesquelles je trouve les commandes de bases peu performantes, trop longues à taper, ou parce que je préfère ne pas retenir les nombreuses options qu’elles proposent. Autre avantage : les alias décrits dans le fichier .gitconfig apparaissent dans l’autocompletion de git, ce qui n’est pas les cas si l’on fait un alias bash.

Voici quelques éléments contenus dans la section [alias] de mon fichier ~/.gitconfig:

[alias]
lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit

Personnellement, je n’ai jamais réussi à me servir de git log. Trop de place occupée pour trop peu d’informations… avec cet alias, chaque commit est sur 1 ligne, les branches sont affichées et colorées, on peut voir de manière pratique qui a réalisé le commit et quand… très pratique donc, comme vous pouvez le voir sur l’image ci-dessous :

screen_better_log

Avant un stand-up meeting, pensez à git lg –since=yesterday, qui vous permet de voir ce que vous avez commité depuis la veille.


s = status -sb

L’alias que j’utilise le plus, car il évite de taper git status en entier, ou d’avoir à appuyer sur tab pour compléter. Ca a l’air idiot mais je tape cette commande probablement plusieurs dizaines de fois par jours, donc je revendique ma fainéantise sur la question.
L’option s signifie short (la sortie est épurée), et b permet d’afficher la branche en cours, ce qui permet d’éviter d’avoir un commit sur la mauvaise branche…


tryagain = reset –hard HEAD

Parce que des fois, tout ce qui a été fait ne sert à rien et qu’il vaut mieux recommencer à zéro.


bv = branch -v

pareil que git branch, mais affiche également le hash court du dernier commit effectué, ainsi que le titre de celui ci.
Dans la même logique, si vous passez votre temps à jongler entre différents remote, vous pouvez penser à ajouter cet alias :

rv = remote -v

Si vous aussi vous avez des alias pour des taches récurrentes ou pour des commandes pratiques, n’hésitez pas à le dire dans les commentaires !

J’ai eu l’occasion de participer à un TEDx ce weekend. 15 conférences. Toutes n’étaient pas intéressantes et il serait inutile de chercher à toutes les résumer (le plus intéressant, c’est d’y être), mais il y a quand même un paquet de choses à raconter. Ted, c’est un rassemblement autour d’idées qui « méritent de se répandre », cette fois ci afin de réfléchir autour de la société, de ses transformations et de comment y vivre mieux en collectivité, en particulier avec le numérique et tout ce qu’il permet.

C’est aussi l’occasion de découvrir des concepts et des idées d’avant garde, et de rencontrer des gens qui font tout un tas de trucs dingues. Ca a également été une bonne leçon d’humilité, car je travaille à longueur de journées sur Internet, et ai l’impression d’en connaitre le fonctionnement, les codes, etc, mais pourtant j’ai rencontré des gens qui maitrisent le sujet bien mieux que moi et en ont des sujets en avance sur ce qui se fait aujourd’hui. C’est un style d’évènement marquant, car plusieurs après on repense encore à ce qui s’est dit lors d’une des conférences.

Parmi les idées à retenir:

  • @davanac et son garage. Pour enseigner les journalisme autour de quelque chose de marquant, il propose à des groupes d’étudiants de passer 4j dans un garage, ensemble. Il apport les bières, et ensembles il montent plusieurs projets. L’idée ? C’est en créant un environnement propice à des relations humains de qualité qu’il se passe des choses qui ont de la valeur, sur le plan professionnel comme sur le plan personnel.
  • Les MOOC. Massively Online Open Courses. De l’e-learning nouvelle generation, où dans l’avenir les universités ne présente que pour les interactions, et où les étudiants pratiquent la revue par les pairs pour corriger les copies.
  • Un mur des possibles, un mur en papier où chacun pouvait écrire ce qu’il ferait si tout était possible. Une idée intéressante mais sous-exploitée, et c’est dommage que beaucoup aient écrit des conneries ou des private joke.
  • Le concept de bio-hackerspace, des coins pour du bricolage collectif dédiés à la recherche en biologie, montés grâce au système D, et résolument tourné vers le partage des connaissances grâce à l’open source. Le moteur de leur naissance ? Un contre pouvoir citoyen qui travaille à des projets qui sont difficiles à faire dans des grands groupes, par exemple à cause des difficulté hiérarchiques. En france, direction La paillasse.
  • Une vidéo d’une gamine qui ira loin dans la vie.
  • Une vidéo vraiment inspirante sur comment prendre de nouvelles habitudes, en cherchant à faire quelque chose de nouveaux tous les jours pendant 30 jours. je l’avais vue un an plus tôt, et cela m’avait poussé à mettre en ligne jour28.fr, une application web dédiée sur le sujet (pas mal bugguée, et jamais terminée, mais c’est en train de changer ;)).
  • Plusieurs conférences inspirantes sur l’échec, l’innovation… impossible à retranscrire. Vous pouvez essayer de regarder des vidéos ted sur le sujet.
  • Un paquet de phrases, et d’exemples de types qui ont fait des trucs fous (Edison et ses 7000 ampoules qui ne marchent pas, Eiffel qui construit la tour éponyme envers et contre tous…)

Ça m’a donné envie de lire/voir/faire :

  • une contre histoire des internets, par la quadrature du net
  • surfer la vie, de Joel de Rosnay, que j’ai depuis des mois sur une étagère et que je n’ai pas ouvert
  • de jeter un oeil à phdcomics
  • bricoler un aéroglisseur
  • de réapprendre la bio
  • de me lancer dans des projets cools

Les trucs pas terribles :

  • le ton inutilement « pédant », en particulier avec l’usage répété et erroné du verbe hacker, ainsi qu’à cause d’anglicismes malheureux (le mot ‘talk’ pour ‘conférence’, malheureusement également très présent chez les développeurs). C’est la tendance qui veut ça, on croit qu’il est possible de hacker tout et n’importe quoi, et on est même convaincu qu’on innove quand on le fait. Par contre il est assez ridicule de croire qu’on hacke la politique quand on organise un forum de discussion, l’idée n’est pas révolutionnaire et sa présentation devient risible…
  • un choix de gens un peu trop ‘classique’, et assez peu varié. Normal qu’on voit plein de gens brillants mais j’aurais sûrement aimé plus de variété. La présence de plusieurs étudiants était dû au thème, mais bon…
  • plusieurs conférences qui se répétaient entre elles (que ce soit dans les thèmes ou les idées développées), ce qui est dommage étant donné la variété de choses qu’il était possible de raconter.
  • une ou deux conférences un peu brouillon, hors sujet ou bien où il était difficile de suivre là où le conférencier voulait nous amener. Mais globalement, dû au format très stéréotypé ted, les présentations étaient globalement très bien maitrisées et de bonne qualité.

Malgré ces quelques points noirs (je cherche un peu la petite bête), c’est un événement cool qui donne envie de se bouger, et de revenir l’année suivante.