JHipster – boost your Spring and AngularJS app

Mon blog s’appelant Hype Driven Development, ça me semblait difficile
de ne pas faire un article sur JHipster, le générateur d’application full stack pour
les hipsters du Java (avouez, c’est difficile de résister).

Un générateur? Oui, un générateur Yeoman. Vous avez peut être entendu parler
de cet outil, fort pratique pour générer des squelettes de projets,
par exemple pour commencer votre prochain projet Angular (c’est d’ailleur le générateur le plus connu).
Yeoman fournit le moteur de base, et vous pouvez installer les générateurs qui vous intéressent.
Il y en a plein et JHipster commence à être fort populaire.

JHipster est un générateur d’application Spring/AngularJS, et donne un projet tout bien
configuré et prêt à démarrer en moins de 10 minutes,
qui se répartissent entre taper 2 commandes et répondre à quelques questions (2 minutes),
regarder NPM télécharger la partie paire de l’internet (3 minutes) et Maven télécharger la partie impaire (5 minutes).
Oui c’était jour de circulation alternée.
Les seuls pré-requis sont donc NodeJS et NPM, un JDK 1.7 et Maven 3.

Stack serveur

Alors qu’est-ce que ça nous a installé ?
Côté serveur, cela s’appuie sur Spring Boot, un projet assez récent
qui permet de créer rapidement une application fonctionnelle avec Spring 4.
C’est clairement un projet que les équipes Spring veulent mettre en avant, pour montrer
que la création d’une application Spring peut être très simple. Spring Boot va créer une application toute prête
(une CLI est disponible, ou un plugin pour votre outil de build préféré), un pom.xml configuré, un serveur Tomcat ou Jetty embarqué,
pas de XML ou de code généré. Vous avez une classe de démarrage de votre application
avec quelques annotations dessus, et ça roule. Spring Boot ramène des copains à la fête :
- Spring MVC pour les controllers REST.
- Jackson pour la sérialisation JSON.
- Spring JPA et Hibernate pour la partie accès aux données.
- HikariCP pour le pool de connexion JDBC.
- Spring Security pour la gestion des utilisateurs et de l’authentification.
- Joda Time pour la gestion des dates (ca devrait être inutile en Java 8).
- Metrics un framework de monitoring, développé par Yammer.
- Thymeleaf si vous voulez faire des templates côté serveur.
- les drivers de la base de données choisie (H2, PG ou MySQL).
- Liquibase pour gérer les changements de schéma (très pratique comme outil).
- JavaMail pour les… mails.
- EhCache ou Hazelcast selon le cache choisi.
- Logback pour la gestion des logs.
- JUnit, Mockito et
Awaitility pour les tests.

Pas mal de code est déjà généré également, notamment pour la partie sécurité,
avec une classe utilisateur, une pour les rôles, une autre pour les audits…
Et bien sûr les repositories, services et controllers associés.
Un service d’authentification est déjà prévu avec une configuration très solide.
L’organisation des sources est claire et très classique, on s’y retrouve facilement.
Les ressources contiennent le nécessaire pour la configuration Liquibase, les fichiers
YAML de paramétrage de Spring selon le profil (deux profils, prod et dev sont fournis).
C’est dans ces fichiers que vous pourriez configurer votre BDD ou votre serveur mail par exemple.
On trouve également les fichiers de messages pour l’i18n, dans plusieurs langues.
Enfin, figurent aussi les fichiers de config du cache et du logger.

Dans les tests, on trouve des exemples de tests unitaires et de tests de controllers REST.

Stack client

Passons côté client. On a bien sûr du AngularJS (what else?).
Grunt est l’outil de build choisi (pas encore de Gulp? c’est pourtant plus hype…), il est déjà bien configuré
pour surveiller les fichiers et les recharger à chaud au moindre changement sauvegardé, passer JsHint pour vérifier,
tout minifier et concaténer. Bower est là pour la gestion de dépendances, rien d’exotique dans ce qui est récupéré :
Angular et ses différents modules indispensables (routes, cookies, translate…). On retrouve bien sûr Twitter Boostrap
pour faire joli et responsive. Les tests unitaires (avec quelques exemples fournis) sont lancés grâce à Karma,
pas de tests end-to-end en revanche.

Le fichier `index.html` est basé sur HTML5 Boilerplate, les fichiers CSS et i18n sont là.
Les scripts JS sont regroupés par type (`controllers.js`, `services.js` …).
Les déclarations d’injection Angular sont faites en mode tableau de chaîne de caractères pour éviter les problèmes de minification.
C’est un bon point, le générateur Yeoman pour Angular propose d’utiliser le plugin ngmin, mais celui-ci a quelques limites.
La sécurité est aussi gérée côté client. Enfin un certain nombre de vues sont disponibles d’origine pour le login et des vues
d’administration : gestion des logs, des perfs.

Run

Lançons tout ça!
Le projet démarre sur le port 8080 avec un simple `mvn spring-boot:run`, sur un écran d’accueil. On peut se connecter en tant qu’admin
et voir des écrans de gestion de profil (mot de passe, settings, sessions), l’écran d’audit (avec la liste des événements), l’écran de configuration des (977!!) loggers par défaut,
dont on peut changer le niveau à chaud, et enfin l’écran exposé par Metrics, avec les différentes… métriques donc!
On peut y voir des ‘Health checks’ (est-ce que la base de données est up? le serveur de mail?), les stats de la JVM
(mémoire, threads, GC), les requêtes HTTP, les statistiques des différents services (nombres d’appel, temps moyen, min, max et différents percentiles)
et enfin les statistiques du cache. J’aime beaucoup cette intégration d’un outil de monitoring simple, c’est certainement
quelque chose que j’utiliserai sur mes projets.
Il est possible de changer dynamiquement le langage de l’application (merci angular-translate).

Développement

JHipster permet aussi de générer des entités pour nous,
depuis la base de données jusqu’à la vue Angular, en passant par le repository et le controller Spring,
la route, le service et le controller Angular. C’est un peu anecdotique (la génération de code, à force…), mais ça marche, et ça
donne un CRUD tout à fait utilisable pour des écrans d’admin. Le service Spring doit lui être généré à part, pour
ne pas encourager la production de code inutile (ce qui est un peu paradoxal pour un générateur de code, vous en conviendrez, mais
je suis sensible au geste).

Ce qui est plus impressionnant, c’est que le projet utilise Spring Loaded, que j’ai découvert très récemment
(merci Brian de l’équipe Spring au détour d’un café à la Cordée!), et que cet agent permet le rechargement de code à chaud, même
lors d’un ajout de bean Spring! Couplez ça à Grunt et son LiveReload, et chaque modification est prise en compte
instantanément. Très très cool! Je pense que Spring Loaded va se retrouver sur les projets Ninja Squad d’ici peu.

Il faut quand même reconnaître qu’entrer la commande

yo jhipster:entity campaign

Puis se rendre directement dans son navigateur à l’url ‘/campaign’ et avoir le CRUD fonctionnel devant les yeux
en moins d’une seconde alors que cela a créé des tables en base, des beans Springs, du JS, qu’on a rien relancé,
c’est quand même pas mal la classe internationale.

TL, DR;

La stack complète est au final extrêmement proche de celle que nous utilisons
sur certains projets chez Ninja Squad : Spring MVC, Spring JPA, Spring Data,
HikariCP, Liquibase, Bower, Grunt, NPM, AngularJS, à l’exception donc
de Spring Boot, Spring Loaded, Metrics, Spring Security et nous préférons LESS à Compass et FestAssert pour les tests.
Puis on fait du Gradle, parce que certains n’aiment pas Maven (je ne donnerai pas de noms).

Mais je crois que Spring Loaded et Metrics vont bientôt rejoindre mes outils favoris.

JHipster propose aussi de faire des WebSockets avec Atmosphere ou
de gérer le clustering des sessions HTTP avec Hazelcast.

Bref, plein de bonnes idées, même si vous connaissez bien toutes les technos, et de bonnes pistes de configuration
si vous les découvrez.

Si vous ne saviez pas comment démarrer votre prochain projet Web Java, vous savez maintenant par quoi commencer!
En plus JHipster propose un container Docker pour tester. Si ça c’est pas le top
de la hype 2014, je sais plus quoi faire…

Workflow, Pull Requests & Code Reviews

Si vous suivez le blog de Ninja Squad, vous savez que nous avons déjà parlé du workflow Git que nous utilisons en local. Mais on ne vous a jamais expliqué les différentes façons de collaborer à plusieurs sur un projet.

Il y a deux grandes façons de travailler à plusieurs :
- un repo partagé par toute l’équipe
- un repo pour chaque développeur

Workflow & Pull Requests

Open Source style

Cette dernière façon est la plus répandue dans le monde open source. Admettons que vous vouliez contribuer au projet AngularJS. Vous n’avez pas les droits d’écriture sur le repository Github utilisé par le projet tant que quelqu’un de l’équipe ne vous les donne pas. Ce qui semble assez logique vu qu’ils ne vous connaissent pas :)

Donc, si vous voulez contribuer au projet, il faut commencer par faire un fork. Il y a un gros bouton sur Github qui permet de faire ça et qui va copier le projet AngularJS dans votre espace utilisateur (par exemple `cexbrayat/angular.js`). Sur ce nouveau repository, j’ai les droits d’écriture. Je peux donc le cloner sur mon laptop :

git clone https://github.com/cexbrayat/angular.js

Une fois cloné, le repository Github est référencé comme le repo ‘origin’ dans nos remotes. Je peux faire des modifications dans une branche :

cd angular.js
git branch awesome-feature
git checkout awesome-feature
... commits

et pousser mon code sur mon repository :

git push origin awesome-feature

Il faut noter que ce repository n’est pas synchronisé automatiquement avec le repository forké : si les développeurs de la team Google ajoutent de nouveaux commits au repository `angular/angular.js`, nous ne les verrons pas dans notre repository. Si ces commits nous intéressent, il est possible d’ajouter ce repo come un autre de nos remotes. Par convention, ce repository est généralement appelé ‘upstream’.

git remote add upstream https://github.com/angular/angularjs

On peu coder notre fonctionnalité comme précédemment :

git branch awesome-feature
git checkout awesome-feature
... commits

Et l’on peut maintenant récupérer les modifications de la team Google et mettre à jour notre branche locale avec celles-ci :

# on récupère les derniers commits de upstream
git fetch upstream
git rebase upstream/master
# on pousse notre branche à jour
git push origin awesome-feature

Une nouvelle branche `updated-with-upstream` sera alors ajoutée sur notre repository Github, à jour avec les dernières modifications faites par l’équipe de Google.

Si cette branche contient une fonctionnalité que vous voulez contribuer, vous pouvez alors créer une pull request. En vous plaçant sur votre branche dans l’interface Github, vous pouvez choisir de créer une pull request vers le repository que vous avez forké, par défaut sur la branche `master`, mais rien ne vous empêche de changer de branche. Vous entrez alors un titre, une description du but de cette PR. Lorsque vous validez, les propriétaires du repository sur lequel vous avez fait la PR vont recevoir un mail leur indiquant l’arrivée d’une nouvelle requête.

Dans l’interface Github, ils pourront faire une revue de code, mettre des commentaires sur certaines lignes (vous ne vous attendiez pas à réussir du premier coup si?), puis enfin choisir de refuser la PR (dommage) ou de la merger (\o/). Dans ce dernier cas, votre code sera ajouté à la branche en question, bravo!

Github fournit maintenant un ensemble de guides fort pratiques pour débuter : vous pouvez regarder celui sur le workflow des PRs.

Enterprise style

En entreprise, le workflow est généralement différent. La plupart du temps, l’équipe collabore sur un seul repository sur lequel tout le monde possède les droits d’écriture. Il y a donc deux façons de procéder.

La première consiste à pousser ses modifications directement sur le `master` (si c’est votre branche de développement actif). On laisse alors le soin à l’intégration continue de repérer tout problème (il n’y a plus qu’à espérer avoir suffisamment de tests…).

git branch feature-one
git checkout feature-one
... commits
# on récupère les derniers commits de origin
git fetch origin
git rebase origin/master
# on pousse notre branche à jour sur le master
git push origin feature-one:master

La seconde consiste à pousser ses modifications sur une branche partagée, puis de créer une pull request entre cette branche et le master! L’avantage est de pouvoir faire une revue de code sur cette pull request, d’en discuter puis de l’intégrer.

git branch feature-one
git checkout feature-one
... commits
# on récupère les derniers commits de origin
git fetch origin
git rebase origin/master
# on pousse notre branche à jour sur une branche partagée pour faire une PR
git push origin feature-one

Code review, sweet code review

Le mécanisme de code review est vraiment intéressant à systématiser. Bien sûr, cela introduit une tâche supplémentaire, que l’on peut voir comme une perte de temps. Mais il est très difficile de coder juste tout le temps du premier coup (en tout cas moi je sais pas faire), et un oeil externe peut souvent voir certains problèmes qui nous ont échappé au moment de la réalisation.

Au-delà de ça, le niveau d’une équipe n’est jamais parfaitement homogène : certains sont d’excellents développeurs front-end, d’autres seront plus à l’aise sur le back-end, ou l’automatisation du projet. La code review est alors un excellent moyen de transmettre des compétences! Si la personne qui relit le code est plus expérimentée, elle sera à même de donner des conseils, des solutions plus élégantes et de repérer des problèmes. Si elle est moins expérimentée que celle qui a développé, elle pourra poser des questions, comprendre de nouvelles choses, s’inspirer. Et elle trouvera des erreurs aussi, c’est garanti!

C’est une procédure délicate, car il faut être capable de dire les choses sans blesser les autres (on est tous un peu sensible sur notre code), ou être capable d’admettre que l’on ne sait pas ce que fait cette méthode et de poser la question.

La code review permet aussi d’homogénéiser les développements, parce que cela force à relire le code des autres, ce que l’on ne fait que rarement si on est la tête dans le guidon. Et surtout, surtout, cela partage les connaissances fonctionnelles. Si vous avez connu le phénomène de "j’ai pas codé cette partie, je sais pas comment ça marche", vous savez de quoi je parle. Faire une revue de code force à comprendre les nouvelles fonctionnalités de l’application même si l’on a pas codé directement dessus. Cela limite ensuite le ‘Bus Effect’ (autrement appelé ‘Scooter Effect’ chez Ninja Squad, grâce aux aventures de Cyril).

Selon les projets et les clients, nous utilisons actuellement ces 3 façons de travailler. La dernière (un seul repo, avec une branche partagée par fonctionnalité, puis pull request pour intégration sur master) est vraiment agréable à utiliser.

Nous avons récemment commencé un projet avec ce workflow et notre intégration continue (Jenkins) se charge de construire chaque pull request indépendamment et d’inscrire sur Github un commentaire avec ‘Tests pass’ ou ‘Tests fail’. De plus nous pouvons déployer un container Docker pour tester une Pull Request de façon isolée. Je trouve ça génial : en 35 secondes l’application est buildée complètement, un container Linux isolé est démarré avec le nécessaire (serveur, bases et données), et l’application est exposée sur un port aléatoire. On se rend alors sur cette adresse pour tester la fonctionnalité et éventuellement remonter des bugs à l’auteur. Sans ouvrir un IDE, sans taper une seule ligne de commande, il est possible de lire le code, le commenter, voir si des tests échouent, et tester la fonctionnalité dans un environnement similaire à la prod. Magique.

Vous l’aurez compris, faire une pull request n’est pas très compliqué. Si vous démarrez un projet et hésitez sur la façon de travailler, pensez à considérer un workflow basé sur des pull requests et des revues de code : votre équipe finira par ne plus pouvoir s’en passer!

Angular, Express et Socket.io

Vous savez, si vous lisez le blog de Ninja Squad régulièrement, que nous donnons des cours dans différents établissements, de la fac aux écoles d’ingé, en passant par l’IUT. Avec cette nouvelle année scolaire, nous voici repartis!

Je donne depuis quelques années un cours sur les web services à l’INSA, à l’invitation du Docteur Ponge : c’est l’occasion de parler SOAP (beurk), REST (yay!) et Websocket (re-yay!).

Cette année, je veux faire une démo des websockets pour que cela soit un peu moins mystérieux. Faire un peu de code live est toujours un exercice périlleux, mais c’est un bon moyen pour échanger avec les étudiants et leur présenter un certain nombre de technologies, qu’ils connaissent parfois de nom ou pas du tout.

Les websockets sont l’occasion idéale de faire une application web basique, mais permettant d’introduire quelques concepts du Web parfois mal connus (HTML, CSS, Javascript) et un peu de code plus innovant (Node.js, Express, Socket.io, AngularJS, Bootstrap), de discuter sur l’histoire du Web, le fonctionnement des navigateurs (Chrome, Firefox, V8…) et quelques outils pratiques (npm, Git). Bref, d’apporter un peu de culture générale de notre métier, chose que j’appréciais beaucoup lors des interventions de professionnels lorsque j’étais étudiant.

Après cette remise en contexte, passons aux choses sérieuses!

Il me fallait donc un exemple d’application, qui me prenne une trentaine de minutes à réaliser au maximum. J’ai choisi de faire simple : une application de vote qui permet de choisir votre framework Javascript préféré, et de voir les résultats en temps réel. Nous allons voir les différentes étapes pour y parvenir.

Vous trouverez le nécessaire pour utiliser l’application sur le repo Github de Ninja Squad.

Express/Angular

La première branche Git, nommée `express` met en place une application Node.js/Express minimale.

    var express = require('express')
      , app = express()
      , server = require('http').createServer(app);
    
    app.use(express.static(__dirname + '/'));
    
    server.listen(9003);

L’application Node.js va servir les ressources statiques sur le port 9003. On peut alors ajouter le fichier HTML de notre application, qui contient basiquement :

    <div class="row">
      <div class="col-xs-4 vote"> {{ vote.choice }} </div>
      <div class="col-xs-4 vote"> {{ vote.votes }} </div> 
      <div class="btn btn-primary col-xs-4">+1</div>
    </div>

Pour chaque vote de la collection `votes`, le choix (VanillaJS, AngularJS, BackboneJS ou EmberJS), le nombre de vote pour ce choix et un bouton pour ajouter un vote seront affichés. Cela est réalisé en utilisant la directive `ng-repeat` d’Angular.

Le bouton de vote comporte l’attribut `ng-click` qui permet de lui lier une fonction à exécuter. Cette fonction `voteFor` est définie dans le controller :

	function VoteCtrl($scope){
      $scope.votes = [ { choice: 1, label: 'VanillaJS', votes: 0 }, { choice: 2, label: 'AngularJS', votes: 0 }, { choice: 3, label: 'BackboneJS', votes: 0 }, { choice: 4, label: 'EmberJS', votes: 0 }];

      $scope.voteFor = function(choice){ $scope.votes[choice-1].votes++; }
    }

Le controller Angular initialise les votes et définit la fonction de vote, qui ajoute simplement 1 aux votes du choix cliqué.

Etape 1 terminée! Passons maintenant à l’intégration de Socket.io.

Socket.io

Socket.io est l’une des librairies les plus utilisées pour les websockets dans Node (même si elle est maintenant concurrencée par d’autres comme SockJS). Elle a l’avantage de gérer le fallback si les websockets ne sont pas disponibles, et d’être très simple à utiliser à la fois côté client et côté serveur.

La branche `websocket` contient le code correspondant. Outre l’installation de socket.io (`npm install` is your best friend), il nous faut modifier un peu le serveur :

    io.sockets.on('connection', function (socket) {
      socket.emit('votes', { votes: votes });
      socket.on('vote', function(msg){
      	votes[msg.vote-1].votes++;
      	io.sockets.emit('votes', { votes: votes });
      })
    });

L’implémentation est naïve mais suffit à la démonstration : à la connexion d’un nouveau client (`socket.on(‘connection’, …)`), on envoie les votes dans l’état du moment (`socket.emit`). Puis, lorsque l’on recevra un vote (`socket.on(‘vote’, …)`), on incrémente les votes du choix correspondant et on informe tous les participants avec les nouvelles valeurs (`io.sockets.emit`).

Reste à mettre à jour le client pour communiquer avec les sockets :

    var socket = io.connect('http://localhost:9003');

    $scope.voteFor = function(choice){
      socket.emit('vote', {vote : choice })
    }

    socket.on('votes', function(msg){
      $scope.votes = msg.votes;
      $scope.$apply();
    });

On commence par se connecter aux websockets (`io.connect`). La fonction `voteFor` est modifiée pour maintenant envoyer un évenement de vote au serveur (`socket.emit`). Enfin, à chaque fois que les votes sont reçus, les votes du `$scope` Angular sont mis à jour. Petite subtilité : comme cette mise à jour intervient en dehors de la boucle d’exécution d’Angular, il nous faut appeler la fonction `$apply()` pour que le framework prenne les nouvelles valeurs en compte et rafraîchisse les vues.

Nous sommes prêts : si vous ouvrez cette application dans deux onglets, vous pourrez la voir se mettre à jour en temps réel!

A noter que le brillant Brian Ford de l’équipe Angular propose un service Angular pour Socket.io, afin de simplifier son utilisation et notamment les appels à `$apply()`.

Vous pouvez voir une démo en ligne, déployé sur Heroku (le code nécessaire pour cette partie est également sur le repo Github).

Espérons que ce petit essai vous plaise et plaise à nos étudiants!

Bitbucket vs Github

Vous savez peut-être que Bitbucket vous permet depuis fin 2011 d’héberger vos projets Git, comme Github, après avoir été un temple de Mercurial. Est-ce pour autant une bonne alternative ? Passons en revue leurs particularités !

Repository

Les interfaces du site sont extrêmement similaires, même si les designers de Github sont probablement légèrement meilleurs.

Chacun permet de créer des repositories publics et privés (mais pratique des prix différents, voir plus bas). Une fois le repository créé, on obtient une URL de remote, sur laquelle il est possible de pousser du code.

La navigation dans les sources se fait de façon identique sur le site, en parcourant les répertoires ou en utilisant les raccourcis claviers (par exemple `t` pour rechercher un fichier dans Github et `f` dans Bitbucket). Une fois le fichier trouvé, il est possible de le visionner, de faire un `blame`, de voir son historique ou de le modifier depuis le navigateur dans les deux cas.

Il est également possible de voir l’activité du projet, et les deux concurrents utilisent le fichier README de façon similaire pour décrire le projet.

L’historique des commits est un peu plus fonctionnel dans Bitbucket avec l’affichage possible de toutes les branches. La visualisation des branches est aussi intéressante, plus graphique que celle proposée par Github.

Fork/Pull Request/Code review

On trouve également le même mécanisme de fork (copie d’un repository dans votre espace utilisateur sur lequel vous avez tous les droits), de pull requests (demande d’intégration d’une fonctionnalité que vous avez codé dans un repo qui ne vous appartient pas) et de code reviews (possibilité de voir les différences introduites et de commenter le code).

Bitbucket ajoute quelques features "nice to have" : il est possible d’afficher un diff avec le fichier dans son ancienne version et dans sa nouvelle version côte à côte, d’affecter les code reviews à certains collaborateurs pour approbation et de ne pouvoir merger que si la pull request a été approuvée un certain nombre de fois. Ce petit workflow d’intégration n’est pas sans intérêt, même s’il est souvent pratiqué informellement sur Github. Autre petit avantage : lorsqu’une pull request ne peut être mergée pour cause de conflit, Bitbucket affiche clairement quels sont les fichiers et lignes en cause.

Administration

Peu de différence là encore : possibilité de créer des équipes et de leur affecter certains droits sur un repository. Bitbucket innove avec la possibilité de donner certains droits sur une branche spécifique.

Bug tracking

Chacun des concurrents propose un bug tracker intégré. Les fonctionnalités sont à peu près identiques :
- création d’anomalie avec assignation possible et version cible de correction.
- description au format markdown, avec images jointes.
- recherche des anomalies.
- lien possible entre anomalies (mais pas d’autocomplétion sur Bitbucket…).
- surveiller les anomalies.
- résolution automatique par commit.

Bitbucket propose en plus une criticité, une priorité et une gestion du workflow intégrée alors que Github compense en permettant la création dynamique de labels comme vous l’entendez. Le système de Github est flexible mais demande un peu plus de travail.

Une autre fonctionnalité intéressante réside dans le vote sur les issues. Là où Github ne permet toujours pas de voter, et où les commentaires ‘+1′ sont le seul moyen de manifester son intérêt, Bitbucket intègre directement le vote sur les issues, ce qui permet de jauger l’intérêt de la communauté pour une feature particulière.

La synchronisation avec d’autres bug trackers est généralement possible. L’intégration de Bitbucket avec Jira est mise en avant mais le même connecteur est utilisé pour Github et Bitbucket dans JIRA, les fonctionnalités sont donc possiblement équivalentes (mais je n’ai pas testé).

Wiki

Un wiki minimaliste est disponible pour les deux sites, avec syntaxe markdown, code highlighting et téléchargement possible (avec Git bien sûr) pour consultation offline.

Money, Money

Bitbucket mise sur un bon argument pour attirer les développeurs : les repositories privés. Alors que sur Github, le plan gratuit ne vous donne accès qu’à des repositories publics, Bitbucket autorise la création gratuite et illimitée de repositories privés. La restriction, car il faut bien une incitation à passer à la version payante, concerne le nombre maximum d’utilisateurs d’un repository privé : 5. Vous ne pouvez donc donner les droits d’accès à ces repo privés qu’à 5 de vos collègues : au-delà, il faudra mettre la main au portefeuille.

Les stratégies sont donc différentes en terme de marketing :
- Github limite le nombre de repositories privés en fonction du prix (0 en gratuit, puis 5 pour 7$, 10 pour 12$…), le nombre de collaborateurs étant illimité.
- Bitbucket permet de créer un nombre illimité de repositories privés mais limite le nombre de collaborateurs (5 en gratuit, puis 10 pour 10$, 25 pour 25$…).

Bitbucket a donc un argument intéressant pour une petite équipe créant un projet privé. A noter également la possibilité d’héberger vous même un Github Enterprise ou la suite professionelle de Bitbucket, nommée Stash, si la perspective d’avoir vos sources sur des serveurs américains vous trouble (mais franchement on ne voit pas pourquoi…). Ces outils vous donnent toutes les fonctionnalités de base plus la possibilité de s’intégrer avec votre système d’authentification interne.

Les prix sont tout de suite plus … entreprise! Github Enterprise démarre à 5000$ par an pour 20 utilisateurs, et est à peu près linéaire avec 250$ par utilisateur (100 utilisateurs donnent donc 25000$ par an, aïe). Bitbucket utilise là aussi une stratégie incitative avec une offre à seulement 10$ par mois pour 10 utilisateurs. La pente est ensuite plus raide mais les prix restent beaucoup plus abordables que ceux de Github avec 100 utilisateurs à 6000$ par an. A noter que Stash offre quelques fonctionnalités intéressantes comme une intégration poussée avec Jira (le bugtracker de la même société), ou les merges automatiques en cascade (un bugfix sur une ancienne release peut être automatiquement mergé sur les releases plus récentes).

Extras

Tous deux proposent une très bonne API REST, et des "hooks" qui permettent de s’intégrer avec tout ce que votre écosystème comporte d’important (les intégrations continues, dashboards, issue trackers…).
Bitbucket ne pose aucune limite sur la taille des fichiers, là où Github restreint à 100Mb par fichier.
Dans les petits bonus de Github, il ne faut pas oublier Github Pages, un support de nouveaux formats (fichier STL 3D, fichier GeoJSON) et une application mobile (même si c’est un peu anecdotique).

Communauté

Difficile de concurrencer Github, leader historique, dans le domaine. Avec près de 5 millions d’utilisateurs contre 1.5 million pour Bitbucket, la marge est encore grande. Les projets OSS phares hébergés sur Github sont très connus : Twitter Bootstrap, Node.js, Rails, JQuery, Angular.js, MongoDB, Linux Kernel. Bitbucket de son coté héberge les projets Atlassian, quelques projets de l’écosystème Python/Django et… pas grand chose d’autre de renommé. Mais surtout très difficile de trouver l’information, qui n’est pas mise en avant. A croire donc que les projets open source boudent le produit.

Les deux sites ont un petit aspect social, avec la possibilité de suivre des utilisateurs, de voir leur flux d’activité public, de mettre en favoris certains projets…

TL; DR;

Bitbucket a bien rattrapé son retard et ne souffre d’aucune lacune flagrante, au-delà de sa communauté moins nombreuse. Il possède même quelques fonctionnalités que l’on retrouverait avec plaisir sur Github.

Pour résumer :
- vous avez un projet open source ? Github sans réfléchir. L’exposition sera un ordre de magnitude supérieure.
- vous avez beaucoup de repositories privés, une petite équipe et peu d’argent ? Bitbucket est la solution économique. Vous pouvez même envisager Stash, leur solution pro.
- vous avez peu de repo privés et/ou de grandes équipes ? Github a un pricing plus intéressant.
- vous voulez héberger la solution chez vous ? Stash est beaucoup moins cher et ajoute quelques fonctionnalités intéressantes. Mais vous pouvez également regarder du côté des projets open source gratuits comme Gitlab par exemple.

Forms in AngularJS

Si vous lisez ceci, vous savez probablement créer un formulaire en HTML. Toute application web en contient son lot. Un formulaire contient un ensemble de champs, chaque champ étant une manière pour l’utilisateur de saisir des informations. Ces champs groupés ensemble dans un formulaire ont un sens, que ce soit une page d’enregistrement pour vos utilisateurs, ou une formulaire de login.

Les formulaires en Angular étendent les formulaires HTML en leur ajoutant un certain nombre d’états et en donnant aux développeurs de nouvelles façon d’agir.

Les formulaires, comme beaucoup de composants en Angular, sont des directives. Chaque fois que vous utilisez un formulaire en Angular, la directive ‘form’ va instancier un controller nommé ‘FormController’. Il est possible d’accéder au controller dans votre code en utilisant comme nom l’attribut ‘name’ de votre formulaire.

Ce controller expose un ensemble de méthode et de propriétés :
- ‘$pristine’ : permet de savoir si l’utilisateur a déjà touché au formulaire. On pourrait traduire en français par vierge. Ce booléen sera donc vrai tant qu’il n’y aura pas eu d’interaction, puis faux ensuite.
- ‘$dirty’ : à l’inverse de ‘$pristine’, ‘$dirty’ sera vrai si l’utilisateur a commencé à interagir avec le formulaire. Pour résumer : $dirty == !$pristine.
- ‘$valid’ : sera vrai si l’ensemble des champs (et éventuellement des formulaires imbriqués) sont valides.
- ‘$invalid’ : sera vrai si au moins un champ (ou formulaire imbriqué) est invalide.
- ‘$error’ : représente un dictionnaire des erreurs, avec comme clé le nom de l’erreur, par exemple ‘required’, et comme valeur la liste des champs avec cette erreur, par exemple login et password.

Ces états sont disponibles sur le formulaire global mais également sur chaque champ du formulaire. Si votre formulaire se nomme ‘loginForm’, et contient un champ ‘password’ alors vous pouvez voir si ce champ est valide grâce au code suivant :

    loginForm.password.$valid

Vous vous voyez déjà ajouter du code JS pour surveiller ces états et changer le CSS en cas d’erreur. Pas besoin! Angular ajoute ces états comme classe CSS directement sur chaque champ et le formulaire. Ainsi lorsque le formulaire encore vierge s’affiche, il contient déjà la classe CSS ‘ng-pristine’ :

    <form name='loginForm' class='ng-pristine'>
      <input name='login' ng-model='user.login' class='ng-pristine'/>
      <input name='password' ng-model='user.password' class='ng-pristine'/>
    </form>

Dès la saisie du premier caractère dans le champ ‘input’, le formulaire devient dirty :

    <form name='loginForm' class='ng-dirty'>
      <input name='login' ng-model='user.login' class='ng-dirty'/>
      <input name='password' ng-model='user.password' class='ng-pristine'/>
    </form>

A noter que le formulaire, ainsi que l’input, reste dirty une fois modifié, même si la modification est annulée.

Si l’un des champs présente une contrainte (comme par exemple ‘required’ ou ‘url’), alors le formulaire sera invalide tant que cette contrainte ne sera pas satisfaite. Le champ comme le formulaire aura donc la classe ‘ng-invalid’ ainsi que le champ en question. Une fois la condition satisfaite, la classe ‘ng-valid’ remplace la classe ‘ng-invalid’.

Vous pouvez donc très facilement customiser votre CSS pour ajouter un style selon l’état de votre formulaire. Cela peut être un bord rouge en cas de champ invalide :

    input.ng-dirty.ng-invalid {
      border: 1px solid red;
    }

Ou encore afficher un message d’erreur dans votre HTML :

    <form name='loginForm'>
      <input name='login' type='email' ng-model='user.login'/>
      <span ng-show='loginForm.login.$invalid'>Your login should be a valid email</span>
      <input name='password' ng-model='user.password'/>
    </form>

Ajouter un type ‘email’ sur un champ place une contrainte sur celui-ci, obligeant l’utilisateur à entrer un login sous la forme d’une adresse email valide. Dès que l’utilisateur commencera sa saisie de login, le champ deviendra invalide, rendant l’expression ‘loginForm.login.$invalid’ vraie. La directive ‘ng-show’ activera alors l’affichage du message d’avertissement. Dès que le login saisi sera un email valide, l’expression deviendra fausse et l’avertissement sera caché. Plutôt pas mal pour une ligne de HTML non?

Vous pouvez bien sûr cumuler les conditions d’affichage de l’avertissement, ou faire un avertissement par type d’erreur :

    <form name='loginForm'>
      <input name='login' required type='email' ng-model='user.login'/>
      <span ng-show='loginForm.login.$dirty && loginForm.login.$error.required'>A login is required</span>
      <span ng-show='loginForm.login.$error.email'>Your login should be a valid email</span>
      <input name='password' ng-model='user.password'/>
    </form>

Ainsi si l’utilisateur, après avoir entré un login (rendant ainsi le champ "dirty"), l’efface, un message d’avertissement apparaîtra pour indiquer que le login est nécessaire. Les combinaisons ne sont limitées que par votre imagination!

Plusieurs méthodes sont disponibles sur le controller :
- ‘addControl()’, ‘removeControl()’ permettent d’ajouter ou de supprimer des champs du formulaire. Par défaut tous les inputs "classiques" (input, select, textarea) que vous utilisez avec un ‘ng-model’ sont ajoutés au formulaire pour vous. Ils ont tous un controller nommé ngModelController, qui gère justement les états ($pristine, $valid, etc…), la validation, l’ajout au formulaire pour vous ainsi que le binding des vues au modèle. Les méthodes ‘addControl()’ et ‘removeControl()’ peuvent être intéressantes si vous voulez ajouter un autre type de champ à votre formulaire.
- ‘setDirty()’, ‘setPristine()’ vont respectivement mettre le formulaire dans un état ‘dirty’ou ‘pristine’ avec les classes CSS associées positionnées. A noter que les méthodes se propagent vers les formulaires parents si ils existent. En revanche, seule la méthode ‘setPristine()’ s’applique sur chaque champ du formulaire. Vous pouvez donc faire un ‘reset’ du formulaire et de ses champs grâce à ‘$setPristine()’.
- ‘setValidity(errorType, isValid, control)’ va, comme son nom l’indique, passer un champ de votre formulaire comme $valid ou $invalid pour le type d’erreur précisé, affectant par le même coup la validité du formulaire ainsi que celles des éventuels formulaires parents.

La dernière chose à retenir sur les formulaires concerne la soumission. Angular étant conçu pour les "single page applications", on cherche à éviter les rechargements non nécessaires. Ainsi l’action par défaut du formulaire sera désactivée, à moins que vous le vouliez explicitement en précisant une ‘action’ dans la balise ‘form’. Angular cherchera plutôt à vous faire gérer vous même la soumission du formulaire, soit en utilisant la directive ‘ngSubmit’ dans la balise ‘form’ soit en utilisant la directive ‘ngClick’ sur un ‘input’ de type ‘submit’ (mais pas les deux à la fois, sinon vous avez une double soumission!).

Vous avez maintenant toutes les clés pour faire une bonne validation côté client de vos formulaires en Angular. Mais que cela ne vous empêche de vérifier côté serveur! ;-)

Envie d’en savoir plus? Je donne une formation sur AngularJs!

Git config – les options indispensables

Si vous utilisez Git, vous connaissez probablement la commande ‘git config’ qui permet de paramétrer Git. Vous savez peut être qu’il existe 3 niveaux possibles de stockage de ces paramètres :
- system (tous les utilisateurs)
- global (pour votre utilisateur)
- local (pour votre projet courant)

Si un paramètre est défini localement et globalement avec des valeurs différentes, ce sera la valeur locale qui sera utilisée (le niveau le plus bas surcharge les niveaux supérieurs).

Il est possible d’afficher sa configuration avec :

    git config --list

On peut ajouter une valeur très simplement, par exemple mon user.name (utilisé pour l’auteur des commits) :

    git config --global user.name cexbrayat

Et l’on peut supprimer une valeur avec –unset :

    git config --unset user.name

Savez vous que l’on peut également définir des alias de commande? Par exemple, j’utilise souvent l’alias ‘lg’ pour une version de ‘log’ améliorée :

    git config alias.lg “log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)%Creset' --abbrev-commit”

Mais revenons à nos paramétrages. La doc de Git est très riche et il n’est pas simple de savoir quelles options peuvent être utiles. Le fichier ‘.gitconfig’ dans votre ‘home’ contient tous vos paramétrages (lorsque vous les avez ajouté en global). Ce fichier regroupe les clés par section, par exemple user.name et user.email sont regroupées dans une section [user]. Vous allez voir, rien de compliqué!

Voici les paramètres que j’aime utiliser :

    [user]
        email = cedric@ninja-squad.com
        name = cexbrayat

Ces deux paramètres sont nécessaires pour faire un commit et apparaîtront dans le log de vos collègues, à compléter dès l’installation donc!

    [core]
        editor = vim
        pager = less
        excludesfile = ~/.gitignore_global

La section core contient beaucoup d’options possibles. Parmi elles, ‘editor’ vous permet de choisir quel éditeur de texte sera utilisé (j’aime bien vim, si si je vous assure, mais vous pouvez très bien mettre SublimeText par exemple, avec ‘subl -w’), idem pour le pager utilisé par Git (pour afficher le log par exemple). Il faut savoir que Git pagine dès que l’affichage ne rentre pas dans votre écran : certains détestent ça et préfèrent utiliser ‘cat’ plutôt.

L’option `excludesfile` permet d’ignorer de façon globale certains fichiers en les précisant dans un `.gitignore` global. Le mien est inspiré de celui de Github.

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

Cette section contient tous les alias que vous ajouterez. Comme j’utilise oh-my-zsh et son plugin Git, j’ai déjà beaucoup d’alias disponibles (par exemple ‘gc’ pour ‘git commit’). On retrouve donc ‘lg’ pour un meilleur log et ‘wdiff’ pour un diff en mode ‘mot’ et pas ‘ligne’. Exemple avec ‘git diff’ :

    -:author:    Ninja Squad
    +:author:    Ninja Squad Team

Un seul mot ajouté, mais Git considère que la ligne entière est changée. Alors qu’avec ‘git wdiff’ :

    :author:    Ninja Squad{+ Team+}

Il reconnaît bien le seul ajout de mot sur la ligne!

    [color]
        ui = auto

Git est tout de suite plus convivial en ligne de commande avec un peu de couleur. Il est quasiment possible de définir pour chaque commande quelle couleur vous voulez, mais le plus simple est d’utiliser la clé ‘color.ui’ qui donne une valeur par défaut à toutes les commandes. A vous les branches en couleur, le log en couleur, le diff en couleur…

    [credential]
        helper = osxkeychain

Si vous utilisez un repo distant protégé par mot de passe (à tout hasard Github), il est possible de stocker votre mot de passe pour ne pas avoir à le saisir à chaque fois. Sur MacOS, le keychain se charge de le stocker pour vous.

    [clean]
        requireForce = false

Git possède une commande ‘clean’ pour supprimer les fichiers non suivis. Mettre requireForce à false permet d’éviter d’avoir à ajouter le flag ‘-f’.

    [diff]
        mnemonicprefix = true

Par défaut lorsque vous faites un ‘diff’, Git vous affiche par exemple :

    --- a/Git/git.asc
    +++ b/Git/git.asc

Ces préfixes a/ et b/ ne sont pas très parlants n’est ce pas? En passant ‘diff.mnemonicprefix’ à true, Git va afficher des préfixes plus logiques, par exemple ‘i’ pour index et ‘w’ pour le work tree (votre dossier de travail). Exemple :

    --- i/Git/git.asc
    +++ w/Git/git.asc

Pratique.

    [help]
        autocorrect = -1

L’une de mes fonctionnalités préférées! Si comme moi vous avez tendance à entrer ‘git rzset’ comme commande, vous avez déjà vu Git vous dire :

Did you mean this?
    reset

Mais ne rien faire pour autant! Et bien en activant l’autocorrection, git va remplacer votre commande par ‘git reset’ automatiquement. Il est possible de laisser un délai en donnant un entier positif comme valeur, ici, avec -1, la correction sera immédiate.

    [rerere]
        enabled = true

Si vous avez lu mon dernier article sur Git, vous savez déjà tout le bien que peut vous apporter ‘rerere’!

    [push]
        default = upstream

Actuellement (Git < 2.0), Git pousse toutes les branches modifiées qui existent sur le repo distant lors d’une commande ‘git push’ (valeur ‘matching’). Je trouve toujours ça un peu dangereux, car on a parfois oublié que l’on a des modifications sur une autre branche que celle en cours, et que l’on ne voudrait pas forcément partager immédiatement. A partir de Git 2.0, la nouvelle valeur sera ‘simple’ : seule la branche courante sera poussée si une branche du même nom existe. La valeur que je préfère est ‘upstream’, qui comme ‘simple’, permet de pousser seulement la branche locale, mais ce même si la branche distante ne possède pas le même nom.

    [rebase]
        autosquash = true
        autostash = true

Lorsqu’on effectue un rebase, Git propose la liste des commits concernés avec un verbe d’action pour chacun (en l’occurence ‘pick’). Ce verbe d’action peut être modifié pour devenir ‘reword’, ‘edit’, ‘squash’ ou ‘fixup’. Les deux derniers compressent deux commits en un seul. Parfois je sais dès que je commite qu’il devra fusionner avec le précédent. En préfixant le message de commit par ‘fixup!’, lors du rebase qui viendra, le commit sera automatiquement précédé par le verbe ‘fixup’ plutôt que ‘pick’!

L’option ‘rebase.autostash’ est toute récente (release 1.8.4.2 de Git) et permet d’automatiser une opération un peu pénible. Lorsque l’on lance un rebase, le répertoire de travail doit être sans modification en cours, sinon le rebase ne se lance pas. Il faut alors commiter ou stasher les modifications avant de recommencer le rebase. Avec cette option ‘rebase.autostash’, le rebase mettra dans automatiquement les modifications courantes dans le stash avant de faire le rebase, puis ré-appliquera les modifications ensuite.

    [pull]
        rebase = true

Par défaut, ‘pull’ effectue un ‘fetch’ suivi d’un ‘merge’. Avec l’option ‘rebase’, ‘pull’ effectuera un ‘fetch’ suivi d’un ‘rebase’.

Allez, pour votre culture, un petit tour des sections moins utiles mais parfois intéressantes.

    [advice]

Il est possible de désactiver tous les conseils que vous affiche Git en cas de problème. C’est un peu comme activer le mode difficile de Git, c’est vous qui voyez…

    [commit]

Il est possible de customiser le template de message de commit avec l’option ‘commmit.template’. Cela peut être pratique si vous avez des pratiques partagées par toute l’équipe (comme celles de l’équipe AngularJS).

Vous pouvez trouver mon ‘.gitconfig’ complet sur mon repo Github.

Et vous, quelles sont vos options préférées?

Git rerere – ma commande préférée

J’adore faire des rebases. Vraiment. C’est d’ailleurs une part très importante du workflow que nous vous conseillons. L’une des choses qui énerve parfois ceux qui font des rebases vient de la résolution de conflit répétitives qui peut survenir.

Pour rappel, un rebase va prendre tous les commits sur votre branche courante et les rejouer un à un sur une autre. Si vous avez une branche ‘topic1’ avec 5 commits et que vous effectuer un rebase sur la branche ‘master’ alors Git rejoue les 5 commits les uns après les autres sur la branche ‘master’.

Parfois, ces commits ont un conflit et le rebase s’arrête, le temps de vous laisser corriger. Mais il arrive que dès que Git passe au commit suivant, vous ayez à nouveau le conflit! Ce qui est très frustrant et pousse parfois à l’abandon du rebase. Cette situation arrive aussi lorsque l’on doit merger une modification en série sur des branches plus récentes. Par exemple, un bugfix sur la version 1.1, qui doit être mergé sur les branches 1.2 et 2.0. Si un conflit de merge apparaît lors du merge sur 1.2, il est frustrant de l’avoir à nouveau sur la branche 2.0 une fois celui-ci résolu.

Mais, vous vous en doutez, je vous parle de tout ça car Git propose une option à activer une seule fois pour être débarrassé de ces soucis.

Cette commande a le nom le plus improbable de toutes les commandes. rerere signifie reuse recorded resolution : cette commande permet à Git de se rappeler de quelle façon un conflit a été résolu et le résoudre automatiquement de la même façon la prochaine que ce conflit se présente.

Pour activer rerere, la seule chose à faire est de l’indiquer en configuration :

$ git config --global rerere.enabled true

Une fois activé, Git va se souvenir de la façon dont vous résolvez les conflits, sans votre intervention. Par exemple, avec un fichier nommé bonjour contenant sur master :

hello ninjas

Une branche french est créée pour la version française :

bonjour ninjas

Alors que sur master, une modification est appliquée

hello ninjas!

Si la branche french est mergée, alors un conflit survient :

Auto-merging bonjour
CONFLICT (content): Merge conflict in bonjour
Recorded preimage for 'bonjour'
Automatic merge failed; fix conflicts and then commit the result.

Si l’on édite le fichier, on a bien un conflit :

<<<<<<< HEAD
hello ninjas!
=======
bonjour ninjas
>>>>>>> french

Vous pouvez voir les fichiers en conflit surveillés par rerere :

$ git rerere status
bonjour

Vous corrigez le conflit, pour conserver :

bonjour ninjas!

Vous pouvez voir ce que rerere retient de votre résolution avec :

$ git rerere diff
--- a/bonjour
+++ b/bonjour
@@ -1,5 +1 @@
-<<<<<<<
-bonjour ninjas
-=======
-hello ninjas!
->>>>>>>
+bonjour ninjas!

Une fois terminée la résolution du conflit (add et commit), vous pouvez voir la présence d’un nouveau répertoire dans le dossier .git, nommé rr-cache, qui contient maintenant un dossier correspondant à notre résolution dans lequel un fichier conserve le conflit (preimage) et la résolution (postimage).

Maintenant, vous vous rendez compte que vous vous préféreriez un rebase plutôt qu’un merge. Pas de problème, on reset le dernier merge :

$ git reset --hard HEAD~1

On se place sur la branche french et on rebase.

$ git checkout french
$ git rebase master
...
Falling back to patching base and 3-way merge...
Auto-merging bonjour
CONFLICT (content): Merge conflict in bonjour
Resolved 'bonjour' using previous resolution.
Failed to merge in the changes.
Patch failed at 0001 bonjour ninjas!

Nous avons le même conflit que précédemment, mais cette fois on peut voir "Resolved bonjour using previous resolution.". Et si nous ouvrons le fichier bonjour, le conflit a été résolu automatiquement!

Par défaut, rerere n’ajoute pas le fichier à l’index, vous laissant le soin de vérifier la résolution et de continuer le rebase. Il est possible avec l’option ‘rerere.autoupdate’ de faire cet ajout automatiquement à l’index (je préfère personnellement laisser cette option à ‘false’ et vérifier moi-même)!

A noter qu’il serait possible de remettre le fichier avec son conflit (si la résolution automatique ne vous convenait pas) :

$ git checkout --conflict=merge bonjour

Le fichier est alors à nouveau en conflit :

<<<<<<< HEAD
hello ninjas!
=======
bonjour ninjas
>>>>>>> french

Vous pouvez re-résoudre automatiquement le conflit avec :

$ git rerere

Magique!

My essentials OSX applications

Difficile de blogger sur les frameworks hypes avec autre chose qu’un MacBook Pro. Récemment j’ai sauté le pas de passer au SSD : j’aurais du faire ça depuis longtemps! Alors pour tous ceux qui, comme moi, aiment payer 3000€ leur machine et 60€ par adaptateur-supplémentaire-non-fourni-mais-indispensable, voici la liste de toutes les applications que je me suis empressé de ré-installer sur mon disque tout neuf avec une installation de OSX Mountain Lion toute fraîche dessus. J’avais toutes ces applications sous Snow Leopard, donc cela conviendra à la majorité des gens intéressés.

Général

Alfred l’utilitaire hyper pratique pour lancer une application ou rechercher un fichier.
TotalFinder le finder MacOS est pas terrible, TotalFinder permet le multi onglet, copier/coller…
Flycut pour un buffer de copier/coller.
Dropbox pour synchroniser mes fichiers (il y a aussi GDrive pour les Google addicts).
Audacity pour les enregistrements de podcasts.
Dashlane pour gérer mes mots de passe. Mais 1Password semble pas mal, je vais peut être migrer.
Skype (ou Google Hangout) pour les réunions de télétravail ou organiser Mix-it.
Transmit comme client FTP.
Chrome pour navigateur.
Disk Inventory X pour avoir la heat map de l’utilisation du disque dur.
Zipeg pour unzip/untar les fichiers.
AppCleaner pour désinstaller proprement.
Mira pour controller toutes les applications à la télécommande (pratique pour les slides HTML par exemple).
KeyCastr pour afficher à l’écran vos frappes de clavier (pratique pour les talks, screencasts…).

Bureautique

Keynote : on dira ce que l’on veut, cela permet de faire des petites présentations classes rapidement. Même si je suis plus slides en HTML ces derniers temps.
OmniGraffle permet de faire des graphs sympas. Pratique pour les documentations.

Développement

iterm2 le terminal bien au dessus de celui par défaut. Avec zsh installé, il ne manque plus que oh my zsh pour avoir une configuration opérationnelle. L’autojump avec z m’est devenu indispensable et httpie pour faire des requêtes HTTP, sans connaître les 39 options nécessaires de curl. Je n’ai pas encore sauté le pas des .dotfiles, mais cela va être l’occasion.
Homebrew qui permet d’installer depuis la ligne de commande bien des choses nécessaires :
- asciidoc pour faire de magnifiques documents,
- git et git-extras et hub bien sûr,
- maven, gradle, sbt pour builder,
- play, groovy, ruby, python3,
- mongodb, redis, postgresql, elasticsearch pour les bases de données,
- phantomjs pour les tests,
- heroku-toolbelt, cloudbees-sdk pour le cloud.

nvm pour gérer les version de nodejs
Intellij IDEA pour développer. What else? Ha si, Sublime Text qui fait un très bon éditeur de texte ponctuel et a l’avantage de se trouver sur tous les OS.
Virtual Box pour les machines virtuelles.
Dash pratique pour avoir de la doc en offline.
Github l’application sert parfois, même si le combo ligne de commande/IDE me suffit le plus souvent.

Loisir

iTunes et Google Music pour ma musique et les podcasts vidéos.
µtorrent pour récupérer les … distribs linux (et les nouvelles versions de LibreOffice, pour se rappeler régulièrement comment c’est chiant une application bureautique pourrie.)? …
VLC pour regarder les vidéos avec le bien pratique Subtitles pour récupérer automatiquement les sous titres.
Calibre pour gérer mes e-books.
Twitter l’application officielle pour Mac s’est bien améliorée.
Steam pour les jeux. Y’en a peu mais des bons.

Voilà l’essentiel, je serais ravi d’entendre toute bonne application que vous utilisez et qui ferait cruellement défaut à cette liste!

Update : Vos contributions

Vous avez été nombreux à conseiller certaines applications, les voici (crédit au premier à l’avoir cité)!
Cafeine (@alexishassler) pour désactiver la mise en veille ponctuellement.
ControlPane (@alexishassler) pour gérer les configurations réseaux, les applications à lancer selon l’endroit où vous êtes.
PixelMator (@xlepaul) pour éditer les images.
Boom (@xlepaul) pour booster le son.
CrashPlan (@xlepaul) pour le backup de données.
Diffmerge (@xlepaul) pour comparer et merger vos fichiers.
Synergie (@Cedric_Gatay) pour gérer plusieurs machines avec un seul clavier/souris.
Cloudapp (@Cedric_Gatay) pour partager facilement des fichiers.
Pocket (@Cedric_Gatay) pour lire vos articles plus tard.
Colloquy (@Cedric_Gatay) comme client IRC.
Clusters (@Cedric_Gatay) qui compresse les dossiers en tâche de fond pour gagner de l’espace.
Monolingual (@slandelle) pour désinstaller les langues inutiles de MacOSX.
Evernote (@k33g_org) pour gérer vos listes, notes entre devices.
SourceTree (@k33g_org) comme client Git graphique.
VMWare Fusion (@k33g_org) comme alternative à VirtualBox.
XMind (@romaintaz) pour faire du mind mapping.
XtraFinder (@happynoff) comme alternative gratuite à TotalFinder.
Mou (@happynoff) pour la rédaction de document en Markdown.
Slate (@dgageot) comme gestionnaire de fenêtre (ou Moom (@morlhon), payant).
F.lux (@glours)pour gérer la luminosité de votre écran selon l’heure de la journée.

Angular filter – Part 1

L’une des fonctionalités les plus appréciables et méconnues d’Angular réside dans les filtres disponibles. Dans toutes applications web, il est courant de devoir filtrer ou réordonner une collection pour l’afficher. Les filtres Angular sont applicables à la fois en HTML ou en Javascript, à un seul élément ou un tableau. En HTML, la syntaxe se rapproche du style Unix, où l’on peut chaîner les commandes à l’aide du pipe. Par exemple, on peut appliquer un filtre nommé ‘uppercase’ sur une expression de la façon suivante :

   {{ expression | uppercase }}

On peut également chaîner plusieurs appels de filtre :

    {{ expression | uppercase | trim }}

Certains filtres prennent des paramètres : c’est le cas du filtre ‘number’, qui nécessite de préciser le nombre de chiffres suivant la virgule. Pour passer les paramètres au filtre, il suffir de procéder comme suit :

    {{ expression | number:2 | currency:'$'}}

Il est également possible d’invoquer les filtres depuis le code Javascript :

    $filter('uppercase')

Angular va alors retrouver la fonction de filtre correspondant à la chaîne de caractères passée en paramètre.

Angular propose par défaut certains filtres communs :

- number : permet de préciser le nombre de chiffres après la virgule à afficher (arrondi au plus proche).

    {{ 87.67 | number:1 }} // 87.7 
    {{ 87.67 | number:3 }} // 87.670

- currency : permet de préciser la monnaie.

    {{ 87.67 | currency:'$' }} // $87.67

- date : permet de formatter l’affichage des dates, en précisant un pattern. On retrouve l’écriture classique de pattern :

    {{ today | date:'yyyy-MM-dd' }} // 2013-06-25

Un certain nombre de patterns sont disponibles (avec un rendu différent selon la locale) :

    {{ today | date:'longDate' }} // June 25, 2013

- lowercase/uppercase : de façon assez évidente, ces filtres vont convertir l’expression en majuscules ou minuscules.

    {{ "Cedric" | uppercase }} // CEDRIC   
    {{ "Cedric" | lowercase }} // cedric

- json : moins connu, ce filtre permet d’afficher l’objet au format JSON. Il est également moins utile, car, par défaut, afficher un object avec la notation ‘{{ }}’ convertit l’objet en JSON.

    {{ person | json }} // { name: 'Cedric', company: 'Ninja Squad'} 
    {{ person }} // { name: 'Cedric', company: 'Ninja Squad'} 

- limitTo : ce filtre s’applique quant à lui à un tableau, en créant un nouveau tableau ne contenant que le nombre d’éléments passés en paramètre. Selon le signe de l’argument, les éléments sont retenus depuis le début ou la fin du tableau.

    {{ ['a','b','c'] | limitTo:2 }} // ['a','b'] 
    {{ ['a','b','c'] | limitTo:-2 }} // ['b','c'] 

- orderBy : là encore un filtre s’appliquant à un tableau. Celui-ci va trier le tableau selon le prédicat passé en paramètre. Le prédicat peut être une chaîne de caractères représentant une propriété des objets à trier ou une fonction. Le prédicat sera appliqué sur chaque élément du tableau pour donner un résultat, puis le tableau de ces résultats sera trié selon les opérateur ‘. Une propriété peut être précédée du signe ‘-’ pour indiquer que le tri doit être inversé. A la place d’un simple prédicat, il est possible de passer un tableau de propriétés ou de fonctions (chaque propriété ou fonction supplémentaire servant à affiner le tri primaire). Un second paramètre, booléen, permet quant à lui d’indiquer si le tri doit être inversé.

    var jb = {name: 'JB', gender: 'male'};    
    var cyril = {name: 'Cyril', gender: 'male'};     
    var agnes = {name: 'Agnes', gender: 'female'};     
    var cedric = {name: 'cedric', gender: 'male'};     
    $scope.ninjas = [jb, cyril, agnes, cedric];  
    
    // order by the property 'gender' 
    {{ ninjas | orderBy:'gender'}} // Agnes,JB,Cyril,Cédric 

    // order by a function (lowercase last) 
    $scope.lowercaseLast = function(elem){ 
      return elem.name === elem.name.toLowerCase() 
    }; 
    {{ ninjas | orderBy:lowercaseLast }} // Agnes,JB,Cyril,cedric 

    // order by an array of properties or functions 
    {{ ninjas | orderBy:['-gender','name'] }} // cedric,Cyril,JB,Agnes 

Dans le prochain billet, nous verrons comment créer nos propres filtres.

Getting started with lambdas – Part 2

Après une première partie dédiée à comprendre les interfaces fonctionnelles, nous allons entrer dans le vif du sujet avec cet article dont le but est de vous présenter les différentes façons d’écrire une lambda.

Supposons que nous ayons une interface Concatenator (histoire d’avoir un nom qui fait peur comme on sait faire en Java), qui prend un int et un double pour les concaténer :

interface Concatenator {
    String concat(int a, double b);                    
}

La première façon d’écrire une lambda implémentant cette interface sous forme de lambda sera :

Concatenator c = (int a, double b) -> { 
    String s = a + " " + b; 
    return s;
};

Voilà notre première lambda! Cette syntaxe est le fruit de nombreux mois de réflexion, après débat entre 3 propositions principales. On retrouve nos paramètres entre parenthèses avec leur type, suivi d’une flèche et d’un bloc de code entre accolades. Le bloc de code représente le corps de la fonction, c’est l’implémentation concrète de la méthode concat() de notre interface Concatenator. Ce corps de fonction est tout à fait classique avec un return en fin de bloc pour renvoyer le résultat.

Une autre façon, plus concise, d’écrire cette lambda est possible avec la syntaxe suivante :

Concatenator c = (int a, double b) -> return a + " " + b;

On note cette fois l’omision des accolades autour du bloc représentant la fonction. Cette syntaxe n’est bien sûr possible que si le corps de votre fonction tient sur une seule ligne, vous l’aurez compris.

Nous pouvons encore simplifier un peu l’écriture en utilisant un return implicite :

Concatenator c = (int a, double b) -> a + " " + b;

Le return a disparu ! En effet, par défaut, la valeur de l’expression à droite de la flèche est retournée par la lambda : les syntaxes avec et sans return sont donc parfaitement équivalentes.

Nous pouvons également nous reposer sur l’inférence de type et laisser le compilateur (lorsque c’est possible) déterminer les types de nos paramètres :

Concatenator c = (a, b) -> a + " " + b;

Vous voyez qu’une lambda peut être une expression très concise !

Si notre interface fonctionnelle avait une méthode avec un seul paramètre, par exemple :

interface UnaryOperator {
    int op(int a);
}   

Alors une lambda implémentant UnaryOperator pourrait être :

UnaryOperator op = (a) -> a * a;

Mais la lambda pourrait même ici se passer des parenthèses autour des paramètres :

UnaryOperator op = a -> a * a;

En revanche, une interface avec une méthode ne possédant pas de paramètres comme NumberSupplier :

interface NumberSupplier { 
   int get();
}

devra s’écrire :

NumberSupplier supplier = () -> 25;

Enfin, lorsque la lambda est un appel à une fonction de l’objet passé en paramètre, il est possible d’utiliser une syntaxe légérement différente. Ainsi, pour une interface fonctionnelle :

interface StringToIntFunction {                        
    int toInt(String s);
}

qui transforme une chaîne de caractères en entier, on peut écrire une lambda comme suit :

StringToIntFunction f = s -> s.length();

ou encore écrire :

StringToIntFunction f = String::length;

Ce :: est un nouvel opérateur Java : il agit comme un appel à la méthode length de l’argument de la méthode toInt. Un peu surprenant au début, mais on s’habitue vite. Cet opérateur peut également s’appliquer à un constructeur. Notre méthodetoIntpourrait donc également s’écrire :

StringToIntFunction f = Integer::new;

ce qui équivaut à

StringToIntFunction f = s -> new Integer(s);

L’opérateur peut aussi s’appliquer à une méthode statique, comme parseInt pour la classe Integer. La lambda :

StringToIntFunction f = s -> Integer.parseInt(s);

est donc identique à :

StringToIntFunction f = Integer::parseInt;

Enfin, il est aussi possible de faire référence à une méthode d’un autre objet. Si nous supposons l’existence d’une HashMapstringToIntMap dans notre code, nous pouvons écrire une lambda comme suit :

StringToIntFunction f = stringToIntMap::get;

qui signifie la même chose que :

StringToIntFunction f = s -> stringToIntMap.get(s);

Voilà, nous avons fait un inventaire exhaustif des façons d’écrire une lambda. La possibilité d’omettre les parenthèses, types, accolades et le mot clé return est appréciable et donne une syntaxe très peu verbeuse. L’ajout de l’opérateur :: introduit de nouvelles possibilités dans l’écriture comme vous avez pu le constater. Son utilisation demande un peu de pratique, mais cela viendra vite naturel !

La prochaine fois nous regarderons une petite subtilité sur la portée des variables utilisables dans une lambda. Teasing teasing…

Suivre

Recevez les nouvelles publications par mail.

Joignez-vous à 864 followers