Outils pour une nouvelle charte graphique

Pour un de nos projets web, nous allons changer la charte graphique. Le projet en question est codé avec le framework Ruby on Rails, ce qui implique de passer sur chacun des fichiers dans app/views pour vérifier le markup, de modifier les feuilles de styles et de s'assurer que le tout est cohérent sur toutes les pages du site. Comme le site est conséquent, cela risque de ne pas être tout simple. Aussi, ai-je écrit deux scripts pour nous aider.

Le premier script sert à extraire toutes les URL appelées depuis les fichiers de logs Rails :

Le second script permet d'ajouter des commentaires HTML au début et à la fin de chaque fichier dans app/views pour l'identifier depuis le code source de la page HTML générée :

Je pense que cela va nous aider dans notre approche, mais je me demande s'il n'existe pas d'autres trucs pour cela. Avez-vous déjà été confronté à cela ? Si oui, comment vous en êtes vous sorti ?

10 gems Ruby que nous utilisons sur nos projets

Ruby gems

af83 utilise Ruby on Rails pour certains de ses projets. De projet en projet, nous prenons des habitudes comme réutiliser certains gems. Je souhaite vous faire partager la liste de ces gems. Cette liste n'a rien d'extra-ordinaire. Ce sont principalement des gems très classiques mais qui méritent d'être mis en avant.

  1. Will Paginate est un plugin Rails pour gérer la pagination. Il est depuis longtemps la référence dans ce domaine et il continue de s'améliorer sans pour autant perdre sa simplicité. Indispensable. http://wiki.github.com/mislav/will_paginate/
  2. Authlogic est une solution complète pour l'authentification et la gestion des utilisateurs. C'est toujours assez délicat de gérer les mots de passe des utilisateurs, les activations de compte, les mots de passe perdus, mais ce gem apporte une manière propre de le faire en gardant une haute exigence de sécurité. http://rdoc.info/projects/binarylogic/authlogic
  3. Acts as state machine offre une solution très appréciable pour gérer des états sur les modèles, et les transitions entre ces états. Rails 3 offrira un module State Machine fortement inspiré de acts as state machine. En attendant, acts as state machine reste indispensable. http://github.com/rubyist/aasm
  4. Friendly_id est un gem bien pratique pour avoir des URL très parlantes. Il permet de créer des slugs et permalinks très facilement en vous évitant les surprises : historique de slugs, mots réservés et support d'unicode sont de la partie. http://norman.github.com/friendly_id/
  5. Thinking Sphinx est une bibliothèque pour connecter ActiveRecord au service de recherche fulltext Sphinx. Son DSL offre de nombreuses possibilités pour faire des recherches avancées sans difficultés. http://freelancing-god.github.com/ts/fr/
  6. HTML Entities est un moyen très simple d'encoder et décoder les entités HTML. http://htmlentities.rubyforge.org/
  7. FasterCSV est une bibliothèque pour manipuler des données au format CSV. Nous l'utilisons sur quasiment tous nos projets pour fournir des exports CSV des données présentes sur le site web. http://fastercsv.rubyforge.org/
  8. rspec est un framework incontournable pour spécifier le comportement de nos applications et nous assurer qu'il n'y ait pas de regressions. http://rspec.info/
  9. factory_girls permet de remplacer les fixtures de Rails, et d'avoir des données pour les specs qui soient faciles à maintenir au fur et à mesure des évolutions. http://github.com/thoughtbot/factory_girl
  10. Pour finir, af83 utilise également has_media, une bibliothèque maison pour la gestion des médias. http://github.com/AF83/has_media

Pourquoi Ruby et Ruby On Rails dans le développement d’application web ?

Cette question a été posé dernièrement sur la Mailling List de RailsFrance. Effectivement, si on veux promouvoir Ruby dans le monde informatique français, il faut pouvoir argumenter pourquoi on trouve que c'est une bonne idée.

Tout le monde est en droit de se poser la question. Voici donc, pour moi, l'avantage de Ruby par rapport aux autres technologies à l'heure actuelle.

Ruby c'est fun

Matz, quand il a créé Ruby, il a voulu faire un langage fun avec lequel il pourra s'amuser à coder. Aujourd'hui tous les développeurs Ruby diront à peu près la même chose. Coder en Ruby est plaisant. L'avantage de ce point est qu'un développeur Ruby est plus détendu. Un développeur détendu est un développeur plus heureux. Un développeur plus heureux est motivant pour une équipe. Un développeur heureux essayera de faire son travail proprement. L'ambiance d'une équipe de développeur Ruby peux ainsi être agréable. Moins de stresse sur un projet, c'est un projet qui pars avec un peu plus de chance de réussir.

Ruby aime les tests

La communauté Ruby actuelle est convaincu par l'utilité des tests unitaires. Ainsi, chaque librairie est poussé par la communauté à avoir des tests. Beaucoup de développeur moi y compris privilégierons plus une librairie avec des tests unitaires plutôt qu'une sans aucun tests.

Cette philosophie permet d'avoir un environement de développement de test simplifié directement dans Rails. Pas besoin de se prendre la tête pour faire des tests. Si vous faites pas de tests de base. Soit vous ne voulez pas en faire, ce que je ne recommande pas. Soit vous le faites exprès.

Ruby aime le cloud

En ce moment, l'évolution logique du net est le Cloud Computing. L'idée du Cloud Computing est simple. Nos sites internet ne sont plus hébergés sur une seule machine. Les services sont séparés pour améliorer la scallabilité. Au sein de la communauté Ruby, les développeurs sont très attaché à cette idée. Ainsi beaucoup d'utilitaires pour gérer et utiliser son cloud commence à voir le jour comme Chef. L'architecture même de Rails permet de facilement sortir de Ruby On Rails pour ainsi utiliser son Cloud. ActiveRessource en est la preuve.

Mais coder dans un Cloud implique de coder plusieurs briques. Le Ruby a des bindings pour tous ces utilitaires, comme les systèmes de queues ou les bases de Données Non-Relationnelle. Je ne dis pas que les autres ne peuvent pas le faire. Mais là encore c'est un effet de groupe. Chaque développeurs ruby a envie de jouer avec un Cloud. De plus en plus on pense en cloud.

Les plugins de Rails

Ruby On Rails possède une très grande quantité de plugins. On peux ainsi facilement trouver la petite brique que l'on cherche pour sa propre utilisation. C'est plus que l'utilisation d'un module Drupal. Car là nous sommes dans un environement facilement modifiable et sans aucune limitation. Chose que Drupal peux vite avoir.

Les Ressources

Le plus grand reproche que l'on fait actuellement au choix de Ruby On Rails, c'est le manque de ressources. Effectivement, les ressources de développeurs Ruby sont plus faible que pour d'autres langages. Mais ceci est selon moi un faux problème. L'apprentissage de Ruby et Ruby On Rails est assez simple si on connait la logique Objet. Ainsi, n'importe quel développeur Java peux tout à fait se mettre à Ruby et Ruby On Rails. Donc avec un expert Ruby/Rails, on peux former facilement une équipe complète et compétente.

Cet argumentaire n'engage bien sûr que moi et peux donc être sujet à caution.

Déployer une application Capcode dans un serveur d’application Java

Que cherche un developpeur dans une offre d’emploi ?

On m'a posé dernièrement cette question. Ça peux semblé bête au premier abord, mais après quelque seconde de réflexion on se rappelle que toutes les offres d'emploi sont pleine d'interrogation.

Donc qu'est ce que j'attends dans une offre d'emploi ?

  • Le nom de l'entreprise:

    Le nom de l'entreprise est toujours pratique ne serait-ce si on aime pas cette entreprise on peux zapper l'annonce. Ou alors rigoler encore plus en lisant l'offre d'emploi.

  • Le cœur de métier de l'entreprise:

    Toujours bien de savoir si on va travailler pour une entreprise qui fait du beurre ou des sites internet. On peux ainsi éviter certain entretien inutile

  • Le nombre de développeur dans l'entreprise:

    Cette information là est vite inhabituel pour un recruteur, mais avoir cette information permet de savoir tout de suite si on sera le geek dans son coin qui sera interrompu pour faire de l'assistance Words alors qu'il a plus toucher à un windows depuis 3 ans. Au contraire, si on voit qu'il y a une majorité de développeur, on pourra se dire que ce sera la fête au troll. Un développeur aime les Trolls \o/.

  • Les personnes connus:

    Comme dans tous métiers, on connait certaine personne de renommé. Cela peut facilement aider un développeur à choisir cette offre d'emploi si une personne connu y est. Par contre c'est aussi à double tranchant. Certain feront l'entretiens juste pour voir cette personne. D'autre zapperont encore plus l'offre d'emploi

  • L'environnement de la station de travail:

    L'OS utilisé en majorité par les développeurs de la société peux être un très bon indicateur parfois. Typiquement chez AF83 où je travaille encore en ce moment, tous les développeurs sont sur UNIX. soit des Linux soit des MacOSX. Pour beaucoup, cela prouve la qualité des développeurs. Et là encore la guerre au troll pourra facilement être ouverte.

  • La contribution Open source possible et faite par l'entreprise:

    Avoir du temps pour faire de l'open source selon notre désire est un plus non négligeable selon moi. J'ai énormément de projet open source en cours que je fais en parallèle de mon emploi, mais si je pouvais les faire aussi durant une partie de celui-ci, je serais vraiment heureux. Ensuite le fait que la société produise du code open source, peux facilement permettre au développeur de jaugé de la qualité du code de cette société. En effet, pourquoi est-ce toujours la société qui doit vérifier le code de ses futurs employés ?

Selon, moi c'est 6 points ne sont hélas pas toujours renseigné dans une offre d'emploi et cela serait un plus non négligeable.

Dix-septième Apéro Ruby de Paris.rb / Ruby France - spécial Rails 3

Paris.rb, groupe local de l’association Ruby France, organise son dix-septième Apéro Ruby, ouvert à tous, rubyistes débutants ou confirmés. Cette rencontre se déroulera jeudi 28 janvier 2010, à partir de 20h, à Dune , 18, avenue Claude Vellefaux 75010 Paris, Métro Colonel Fabien ou Métro Goncourt (voir plan ou voir sur Google Maps Street View ).

À l’occasion de la sortie prochaine d’une release candidate de Rails 3 , c’est l’occasion de nouveau de faire le point sur les dernières nouveautés du principal framework web en Ruby. Si vous voulez présenter un aspect de Rails 3 lors de cette soirée, vous pouvez en discuter actuellement sur la liste Ruby Fr Public ou la liste Rails France . Consultez ces mailing-listes pour vous tenir au courant du programme final.

Vous pouvez me signaler votre venue si vous le souhaitez : mon adresse jftran (at) rubyfrance (point) org ou twitter @underflow_.

À bientôt !

Jean-François.

Dix-septième Apéro Ruby de Paris.rb / Ruby France - spécial Rails 3

Paris.rb, groupe local de l’association Ruby France, organise son dix-septième Apéro Ruby, ouvert à tous, rubyistes débutants ou confirmés. Cette rencontre se déroulera jeudi 28 janvier 2010, à partir de 20h, à Dune , 18, avenue Claude Vellefaux 75010 Paris, Métro Colonel Fabien ou Métro Goncourt (voir plan ou voir sur Google Maps Street View ).

À l’occasion de la sortie prochaine d’une release candidate de Rails 3 , c’est l’occasion de nouveau de faire le point sur les dernières nouveautés du principal framework web en Ruby. Si vous voulez présenter un aspect de Rails 3 lors de cette soirée, vous pouvez en discuter actuellement sur la liste Ruby Fr Public ou la liste Rails France . Consultez ces mailing-listes pour vous tenir au courant du programme final.

Vous pouvez me signaler votre venue si vous le souhaitez : mon adresse jftran (at) rubyfrance (point) org ou twitter @underflow_.

À bientôt !

Jean-François.

Twitter/HTML5 Websockets with Cramp

After HTML5 Web Storage, I'm gonna show a use case of HTML5 Websockets. It's quickly becoming a hype solution for bidirectional communication between client and server (think TCP for browsers). Hype but not really usable in production because of the compatibility with the browsers.

Compatibility of Websockets (as of January 2010) : nightly builds of WebKit and Chrome.

Because I'm lazy and there are some examples out there I decided to convert an existing Twitter Stream example to Cramp (a new Ruby async framework by @lifo from the Rails Core team). So first read the article from the author of the initial example. Finish? Now we can convert it to Cramp.

Cramp for Websockets

Lifo introduced it on his blog. A few lines later we have the application configured for Cramp instead of EventMachine in socket.rb:

require 'vendor/gems/environment'
require 'cramp/controller'
require 'uuid'
require 'mq'

Cramp::Controller::Websocket.backend = :thin

class TweetsController < Cramp::Controller::Websocket
end

Rack::Handler::Thin.run TweetsController, :Port => 3000

on_start

Now we want to convert the open event to Cramp. For that, Cramp has a on_start method to call the method executed for this event. So the open event will be like that:

require 'vendor/gems/environment'
require 'cramp/controller'
require 'uuid'
require 'mq'

Cramp::Controller::Websocket.backend = :thin

class TweetsController < Cramp::Controller::Websocket
  @@uuid = UUID.new

  on_start :display

  def display
    puts "WebSocket opened"

    twitter = MQ.new
    twitter.queue(@@uuid.generate).bind(twitter.fanout('twitter')).subscribe do |t|
      render t
    end
  end
end

Rack::Handler::Thin.run TweetsController, :Port => 3000

To send a message to the user, Cramp has a render method.

on_finish

When the user ends the execution of the socket, there's a close event. When this event is fired, the method passed to on_finish will be executed.

require 'vendor/gems/environment'
require 'cramp/controller'
require 'uuid'
require 'mq'

Cramp::Controller::Websocket.backend = :thin

class TweetsController < Cramp::Controller::Websocket
  @@uuid = UUID.new

  on_start :display
  on_finish :close

  def display
    puts "WebSocket opened"

    twitter = MQ.new
    twitter.queue(@@uuid.generate).bind(twitter.fanout('twitter')).subscribe do |t|
      render t
    end
  end

  def close
    puts "WebSocket closed"
  end
end

Rack::Handler::Thin.run TweetsController, :Port => 3000

We just have to start the server and test with client.html to see the tweets.

Conclusion

If you read the code you'll see that Websockets make more sense to a developer than Comet. And it's the same for a lot of HTML5 features. Having more logical things and less hacks will may finally be a reality soon. You can see the whole code of this example in my fork of the original one.

Twitter/HTML5 Websockets with Cramp

After HTML5 Web Storage, I’m gonna show a use case of HTML5 Websockets. It’s quickly becoming a hype solution for bidirectional communication between client and server (think TCP for browsers). Hype but not really usable in production because of the compatibility with the browsers.

Compatibility of Websockets (as of January 2010) : nightly builds of WebKit and Chrome.

Because I’m lazy and there are some examples out there I decided to convert an existing Twitter Stream example to Cramp (a new Ruby async framework by @lifo from the Rails Core team). So first read the article from the author of the initial example. Finish? Now we can convert it to Cramp.

Cramp for Websockets

Lifo introduced it on his blog. A few lines later we have the application configured for Cramp instead of EventMachine in socket.rb:

on_start

Now we want to convert the open event to Cramp. For that, Cramp has a on_start method to call the method executed for this event. So the open event will be like that:

To send a message to the user, Cramp has a render method.

on_finish

When the user ends the execution of the socket, there’s a close event. When this event is fired, the method passed to on_finish will be executed.

We just have to start the server and test with client.html to see the tweets.

Conclusion

If you read the code you’ll see that Websockets make more sense to a developer than Comet. And it’s the same for a lot of HTML5 features. Having more logical things and less hacks will may finally be a reality soon. You can see the whole code of this example in my fork of the original one.

Speed up Ajax with HTML5 Web Storage

I'm beginning a series of articles about the modern features of Web browsers. This article will be about HTML5 Web Storage and particularly localStorage. And because I like it, another twitter example folks!

What is localStorage?

Compatibility of localStorage (as of January 2010) : IE8, Firefox 3.5, Safari 4, Chrome 4, Opera 10.50

Basically it's like cookies but restricted to the client (the server cannot get data from localStorage), it can store more data (the limit depends of the browser but it's generally 5MB) and it doesn't have a time expiration. It's time for the example!

WARNING: You must test the above example with a real URL (not file://). Otherwise it won't work in IE and Firefox (but seems to be fine in Safari and Chrome)

The slow way

First we get the tweets and display them. It is slow because each time we access the page the Javascript will request Twitter. And it's ugly because we have a loading message before displaying the tweets.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
  <script type="text/javascript">
    function displayTweets(data) {
      $("#loading").remove();
      $.each(data.results, function(i, item) {
        $("#tweets").append($("<li/>").text(item.text));
      });
    }

    $(function() {
      $("#tweets").before($("<div id='loading'/>").text("Loading..."));
      $.getJSON("http://search.twitter.com/search.json?q=from:nmerouze&rpp=5&callback=?", function(data) {
        displayTweets(data);
      });
    });
  </script>
</head>
<body>
  <ul id="tweets"></ul>
</body>
</html>

To display the tweets instantly we're gonna store them.

Setter

After each request to Twitter we store the whole JSON response to localStorage. It is just one line:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
  <script type="text/javascript">
    function displayTweets(data) {
      $("#loading").remove();
      $.each(data.results, function(i, item) {
        $("#tweets").append($("<li/>").text(item.text));
      });
    }

    $(function() {
      $.getJSON("http://search.twitter.com/search.json?q=from:nmerouze&rpp=5&callback=?", function(data) {
        displayTweets(data);
        localStorage.setItem("tweets", JSON.stringify(data));
      });
    });
  </script>
</head>
<body>
  <ul id="tweets"></ul>
</body>
</html>

localStorage.setItem(key, value) take a key for the first argument ("tweets" here) and the value for the second argument (the Twitter JSON response here). WARNING: The value must be a string so if we want to store an object, we stringify it with JSON.stringify(data).

Getter

Then we write the code to get the tweets from localStorage.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
  <script type="text/javascript">
    function displayTweets(data) {
      $("#loading").remove();
      $("#tweets").children().remove();
      $.each(data.results, function(i, item) {
        $("#tweets").append($("<li/>").text(item.text));
      });
    }

    $(function() {
      $("#tweets").before($("<div id='loading'/>").text("Loading..."));
      var tweets = localStorage.getItem("tweets");
      if (tweets != null) displayTweets(JSON.parse(tweets));
      $.getJSON("http://search.twitter.com/search.json?q=from:nmerouze&rpp=5&callback=?", function(data) {
        displayTweets(data);
        localStorage.setItem("tweets", JSON.stringify(data));
      });
    });
  </script>
</head>
<body>
  <ul id="tweets"></ul>
  <button onclick="javascript:localStorage.clear();">Clear localStorage</button>
</body>
</html>

To get the data it's localStorage.getItem(key). If there's something in it we display them. The first time we access the page there won't be anything in localStorage so the process will be the same as "The slow way". The other times the tweets will be displayed instantly. The Twitter request is running too, so it will refresh the list a little bit after the first display in case there's any new tweets. The rendering will be way prettier than the loading message! You can see the example here.

If we want to clear the localStorage it's as simple as localStorage.clear().

Old browsers compatibility (progressive enhancement)

If you do that in production, the Javascript will fail for the users who have old browsers. To be safe, we will download Modernizr (I will write an article about Modernizr soon) and check if the browser has a localStorage:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
  <script src="modernizr.js" type="text/javascript"></script>
  <script type="text/javascript">
    function displayTweets(data) {
      $("#loading").remove();
      $("#tweets").children().remove();
      $.each(data.results, function(i, item) {
        $("#tweets").append($("<li/>").text(item.text));
      });
    }

    $(function() {
      $("#tweets").before($("<div id='loading'/>").text("Loading..."));
      if (Modernizr.localstorage) {
        var tweets = localStorage.getItem("tweets");
        if (tweets != null) displayTweets(JSON.parse(tweets));
      }
      $.getJSON("http://search.twitter.com/search.json?q=from:nmerouze&rpp=5&callback=?", function(data) {
        displayTweets(data);
        if (Modernizr.localstorage) localStorage.setItem("tweets", JSON.stringify(data));
      });
    });
  </script>
</head>
<body>
  <ul id="tweets"></ul>
</body>
</html>

Result

As a result I made a fork of jQuery tweet with localStorage support, so check it out! You won't see a difference with a server-side rendering.

Links