On se tutoie ?

Aller au contenu | Aller au menu | Aller à la recherche

mardi, novembre 11 2008

Installer ruby-qt sur Mac OS X

Voici un petit howto pour compiler Ruby-qt sur Mac OS X Leopard.

Installer Qt

Commence par télécharger le dmg de Qt et installe le.

Installer Ruby-Qt

Télécharge ensuite la dernière version de qt4-qtruby sur rubyforge (1.4.10 à ce jour) et extrait le tarball. Une fois réalisé on se place dans le répertoire nouvellement créé et on lance un petit coup de cmake :

$ cd qt4-qtruby-1.4.10
$ cmake .

S'il te manque des outils comme cmake, des headers ou des dépendances, tu peux tout résoudre en installant les packages qui vont bien via fink ou port.

Logiquement on devrait maintenant simplement lancer un make.
Le souci c'est qu'on aura cette erreur à la compilation :

Undefined symbols:
  "QWebHistoryItem::~QWebHistoryItem()", referenced from:
      QList<QWebHistoryItem>::node_destruct(QList<QWebHistoryItem>::Node*, QList<QWebHistoryItem>::Node*)in handlers.o

Le problème vient du QWebHistoryItem mais je ne sais pas ce qui cloche exactement.
On va donc mettre en commentaire deux lignes qui y font référence dans le fichier ruby/qtruby/src/handlers.cpp :

Ligne 2488 : // DEF_VALUELIST_MARSHALLER( QWebHistoryItemList, QList<QWebHistoryItem>, QWebHistoryItem ) Ligne 2661 : // { "QList<QWebHistoryItem>", marshall_QWebHistoryItemList },

et lancer respectivement un :

$ make
$ make check
# make install

Logiquement tout devrait bien se passer, on peut le vérifier avec irb :

$ irb
>> require 'Qt'
=> true
>> Qt.version
=> "4.4.3"

Et c'est parti pour réaliser des applications cross-platform avec notre langage préféré \o/

wizard-qt-ruby

jeudi, juillet 17 2008

Vertebra, un système intelligent de gestion de cloud, basé sur des standards

Les plateformes de type cloud computing (comme l'EC2 d'amazon) sont à la mode en ce moment, et d'après ezra, ça ne fait que commencer.

Je m'intéresse de plus en plus à ce type de solutions depuis que je travaille sur deescover, puisque le système a besoin de monter en charge.

Vertebra est donc un système de gestion de cloud computing (nuage de machines).
Il est vrai que le déploiement d'une application à forte charge, donc répartie entre plusieurs machines, peut vite s'avérer un casse tête, même avec des outils exceptionnels comme capistrano.

Vertebra se base sur XMPP afin de permettre aux différentes machines de communiquer entre elles.
XMPP, utilisé entre autre par jabber, est un standard qui peut être utilisé dans bien des situations.
L'idée de vertebra est de permettre à des machines (et plus seulement des personnes) de communiquer avec ce protocole.

Comme dans un système de messagerie, les machines disposent d'un statut, permettant de préciser leur état (disponible / indisponible / occupé).
Pour spécifier cet état un agent ruby tourne sur chacune des ressources du 'cloud'.
Il est important de spécifier que les agents tournent à deux niveaux :

  1. Au niveau node (noeud : groupe de machines) afin de connaître l'état des ressources du noeud.
  2. Au niveau slice (la resource ou machine) afin de lister les opérations disponibles.

Pour continuer dans les bonnes idées, le clud est Restful. Les opérations disponibles sont enregistrées au niveau de la ressource du cloud et sont accessibles au travers d'URL.
Sans ré-expliquer REST, le type d'opération dépend uniquement de l'URL et de la façon dont on y accède (GET : consultation, POST: création, PUT : modification, DELETE: suppression).

Chaque noeud dispose donc d'une API prête à l'emploi en fonction des opérations qu'il enregistre.
Ces opérations sont donc consultables à la demande par un simple message XMPP.

L'exploration des différents noeuds se fait de façon dynamique, par un système de broadcast des messages (basé sur XMPP donc).
En fonction des réponses il est possible de déterminer l'état des noeuds, mais aussi de choisir à qui adresser une opération (potentiellement le noeud qui répond le plus vite).
Ce système permet donc aussi de répartir la charge.

La présentation d'ezra montre bien que les jeux de questions - réponses ne sont pas limités.
Il est par exemple possible de déterminer l'état et la configuration des différentes machine du noeud depuis l'agent grâce à l'API.

Du côté des technologies utilisées on retrouve donc ruby, pour la partie agent, et erlang pour la partie serveur XMPP (ejabberd), mécanisme de découverte de services ...
Bref, que du bon.

Bien sûr on pourrait se poser la question de savoir comment sont gérées les autorisations entre les agents.
On n'imagine mal n'importe quel système envoyer un message XMPP à l'agent pour lui ordonner d'éxécuter une tâche.
Comme pour les systèmes de messagerie classiques, les agents sont en relation les uns avec les autres (un peu comme une liste de contact).
Pour 'se parler' chacun doit donc être dans la liste de l'autre.

La souplesse du système et de l'architecture choisie ouvre des tas de possibilités dont parle ezra : ne plus se limiter à de l'automatisation de tâches systèmes mais utiliser ce sytème en temps réel pour des applications web.

Ce principe est déjà possible grâce à fuzzed.
Écrit en Erlang, il permet de facilement créer un cluster et dispatcher les requêtes.
Ce système permet de monter en charge rapidement en ajoutant simplement des nodes au cluster.

Voilà, ça bouillonne pas mal en ce moment, et les grincheux qui crient sur tous les toits que rails ne monte pas en charge [1] et est complexe à déployer n'ont qu'à bien se tenir.

Notes

[1] Ce qui est idiot puisque ce n'est pas la technologie qui monte en charge mais l'architecture.

jeudi, mars 13 2008

Plugin highlight et pv pour weechat

Le besoin

Comme shingara, j'utilise weechat pour IRC et il nous manquait un plugin pour être notifié sur notre jabber à la réception d'un PV ou lors d'un highlight sur un channel.
Le but est de recevoir un message sur sa messagerie instantanée avec le nom de l'expéditeur et le contenu du message lorsque quelqu'un nous interpelle sur IRC.
Je me suis donc motivé pour écrire ce plugin in en me basant sur ses travaux en amont.

Qu'est ce qui change ?

Je suis donc parti de la révision 204 de son script (euh en fait y a d'autres choses dans le dépôt hein, vas faire un tour !) , basée sur celui de davux (c'est beau le libre hein ?), qui apparemment avait tendance à faire freezer weechat.

Au lieu d'utiliser xmpp4r j'utilise xmpp4r-simple qui est une lib plus légère et largement suffisante pour nos petits besoins.
Je pense avoir cerné ce qui potentiellement faisait planter weechat.
En effet lorsqu'on tente d'envoyer un message via XMPP, et que l'on ferme la connexion instantanément après, le message est non seulement perdu, et n'arrive donc jamais, mais a (semble t-il, je n'ai pas de certitudes) tendance à geler weechat.

Le script attend donc maintenant 3 secondes avant de fermer la connexion. D'une manière générale j'ai essayé d'améliorer la lisibilité et les perfs du script.
Au lieu d'instancier systématiquement un objet pour délivrer un message je passe par un singleton qui délivrera tous les messages pendant la durée de vie de weechat.

En terme de configuration il ne faut plus que le strict minimum : un couple jid / password qui désigne le compter jabber à utiliser pour envoyer les messages et un destinataire.
Il est possible de spécifier le port de connexion s'il est particulier, autrement le script utilise le port 5222.

Attention : Je n'ai pas testé mais il me semble logique qu'il ne faut pas utiliser le jid comme recipient, autrement le script risque de s'envoyer des messages à lui même.
En effet lorsque l'on utilise jabber c'est le dernier endroit depuis lequel on se connecte qui prend la main.

Au cas où ton compte destinataire ne serait pas connecté, les messages seront mis en file et tu les recevras à ta prochaine connexion.

Comment ça marche ?

L'usage est vraiment simple : il suffit de copier ce plugin dans ton .weechat/ruby/autoload et le plugin sera chargé au lancement de weechat. Ensuite tu édites ton fichier .weechat/plugins.rc et tu colles les informations te correspondant :

ruby.highlight_jabber_notify.jid = "jid@provider.tld"
ruby.highlight_jabber_notify.password = "toto42" # c'est pas ça ?
ruby.highlight_jabber_notify.port = "5222" # optionnel
ruby.highlight_jabber_notify.recipient = "recipient@provider.tld"

C'est tout. Le script est volontairement minimaliste mais on peut imaginer plus de configuration et de code pour n'être notifié que sur certains channels etc ...
J'ai aussi proposé ce script dans la section officielle du projet weechat (il est en attente d'approbation a été approuvé \o/).

jeudi, février 28 2008

Validations optionnelles en rails

Les validations

En rails les validations sont un concept particulièrement intéressant et pratique. Effectuées au niveau du modèle elles constituent le dernier rempart avant la modification de la base de donnée.

Cependant, suivant le contexte de l'application les vérifications ne sont pas forcément les même, certaines parties d'une application peuvent requérirent plus d'informations que d'autres, ce qui nous oblige à valider uniquement le nombre de champs minimal par défaut.

Prenons un cas concret : on a un modèle Contact. On veut que notre contact puisse laisser un avis sur le site mais aussi prendre un rendez vous.
Afin de dispatcher le rendez vous on a besoin de connaitre le code postal de ce contact.
Toutefois ce serait décourageant de demander le code postal d'une personne souhaitant uniquement laisser un avis.

Dans ce cas les seuls validateurs par défaut seront le nom et le prénom :

 # Dans le modèle
validates_presence_of :firstname
validates_presence_of :lastname

Coté vue :

 # Dans la vue
<%= error_messages_for :contact %>

Comment faire alors pour valider le code postal, si et seulement si nous somme en présence d'une demande de rendez vous ?

La mauvaise idée serait de penser faire le test dans notre contrôleur :

 # Dans le contrôleur
@contact.errors.add(:zip_code, "n'est pas valide.") unless params[:contact][:zip_code] =~ /^\d{5,6}$/
return render :action => 'form' unless @contact.save

Tout d'abord, ce code ne fonctionne pas (du moins en 1.2.6) : il est toujours possible d'insérer un enregistrement sans, ou avec un mauvais, code postal.
En effet la méthode save écrase le hash d'errors (tout comme la méthode valid?).
La méthode save ne teste donc pas si son modèle contient déjà des erreurs ou non. Ce qui signifie que si nos champs lastname et firstname sont remplis, le hash d'erreurs sera vide et l'enregistrement correct.

Pour avoir un code qui fonctionne voilà le genre d'insanités qu'il faudrait écrire :

 # Dans le contrôleur
if params[:contact][:zip_code] =~ /^\d{5,6}$/
      return render :action => 'form' unless @contact.save
else
      @contact.valid?
      @contact.errors.add(:zip_code, "n'est pas valide.")      
      return render :action => 'form'
end

Le fait d'insérer l'erreur après le valid? garantit à l'utilisateur de voir toutes les erreurs. Quoi de pire que de découvrir les champs requis au fur et à mesure de la saisie, après plusieurs soumissions.

Toutefois imagines la qualité du code au cas ou il y aurait plusieurs champs optionnels à vérifier. Quid aussi du côté DRY si on se retrouve à avoir le cas pour plusieurs formulaires ? Dupliquer ce morceau de code serait vraiment une mauvaise idée.

La bonne solution :validate ... :if

La meilleure idée c'est toujours de mettre nos validations dans le modèle :

 # Dans le modèle
def validate_zip_code
    errors.add(:zip_code, "n'est pas valide.") unless zip_code =~ /^\d{5,6}$/
end 

À ce niveau on pourrait choisir d'overrider validate dans notre contrôleur comme ceci :

 # Dans le contrôleur
def submit
    @contact = Contact.new(params[:contact])
  
    def @contact.validate
      validate_zip_code
    end

    ...
end

En effet, rien ne nous empêche d'ajouter des méthodes à notre objet au sein même de l'action, mais ça reste une mauvaise idée.
La notion d'ajout de méthodes dans un objet “à chaud” est un concept à manipuler avec précaution, qui peut s'avérer un casse tête à débugger sur du code volumineux.
De plus, dans le cas d'autre formulaires il serait dommage d'avoir à surcharger systématiquement la méthode validate.

Nous allons donc (quasiment) tout réaliser dans le modèle. Pour cela on rajoute un champ à notre modèle : optional_validations ...

 # Dans le modèle
  def optional_validations
    @optional_validations ||= []
  end

... ainsi qu'un validateur standard, doté du symbole :if :

 # Dans le modèle
validates_format_of :zip_code, :with => /^\d{5,6}$/, :if => Proc.new {|c| c.optional_validations.include?(:zip_code)}

Nous venons de mettre en place un getter qui renvoie notre propriété ou un tableau vide. Le validateur prend en paramètre une regex qui teste que le code postal contient bien 5 ou 6 chiffres au maximum.
Proc prend en argument un bloc de code. La variable c correspond à l'instance de contact courante.
La validation du code postal ne s'effectuera donc que si le symbole :zip_code est présent dans les validations optionnelles.

Il ne nous reste donc plus qu'à modifier légèrement notre contrôleur :

 # Dans le contrôleur
@contact.optional_validations << :zip_code

Le fait que notre getter renvoie un tableau vide permet d'éviter de faire un push sur nil dans le cas du premier ajout de symbole.

Voilà, nous avons maintenant à disposition un système de validations optionnelles souple et évolutif, qui impacte nos contrôleurs au minimum.

mercredi, janvier 30 2008

Pas encore bilingue, translate est là pour toi !

Si comme moi tu passes pas mal de temps sur des sites de traduction, ce post est fait pour toi.

En effet 99% des bonnes docs étant écrite dans la langue de Shakespeare il n'est pas toujours évident de tout comprendre sans assistance.
C'est pourquoi je me retrouve assez souvent sur wordreference dans une journée.

Globalement wordreference est un très bon service. Toutefois après avoir cherché un peu je me suis rendu compte qu'il n'offrait pas d'API.
Je me suis donc résolu à créer mon propre script (en ruby) qui attaque le site en GET et qui parse le résultat à l'aide de l'excellente lib Hpricot.

Actuellement le script permet de faire les traductions suivantes :

  • Anglais vers français
  • Anglais vers italien
  • Français vers anglais
  • Italien vers anglais

Bizzarement, et c'est dommage, le site n'utilise pas le même format d'URL ni d'affichage pour les traductions en espagnol ; que ce soit depuis ou vers l'espagnol.
Toutefois rien n'empêche de patcher le script pour y remédier.

Pour utiliser ce script il te faut rubygems et hpricot. Rubygems est disponible sur la plupart des distributions ainsi que sous Mac OS X avec fink ou port.
Une fois rubygems installé il faut installer Hpricot à partir de ce dernier :

# gem install hpricot

ou

# gem install hpricot --source http://code.whytheluckystiff.net

Personnellement j'ai choisi la deuxième version pour avoir la build la plus récente.
Si tu as une erreur du genre : `require': no such file to load -- mkmf (LoadError) c'est sûrement qu'il te manque le paquet ruby1.8-dev Une fois installé tu peux utiliser le script de manière suivante :

./translate.rb house

Pour avoir un panel de ce que peut faire le script :

./translate.rb -h

Pour avoir quelques exemples :

./translate.rb -e

Note : Tu peux très bien mettre le script directement dans ton path pour l'utiliser depuis n'importe où, comme une commande normale :

$ sudo ln -s /path/to/translate.rb /usr/local/bin/translate

Le script est disponible sur ce post en annexe, mais la version la plus à jour se trouve dans mon dépôt mercurial.
J'essaierai de mettre une documentation un peu plus complète sur le script dans mon espace dédié sous peu.

Tout est dit, j'attends tes commentaires, retours ou patchs !