24
Nov/09
0

Simplification des appels à erlang depuis ruby

Dans les articles précédents, les appels au processus erlang via la ligne de commande étaient écrits ainsi :

system "erl -pa #{EBIN_DIR} #{ROOT}/deps/*/ebin -boot start_sasl -s reloader -s inets -s xxx -mnesia dir 'db'"

On améliore l’écriture de cette commande afin de la rendre plus lisible et maintenable. Pour cela, on crée une fonction wrapper comme ci dessous :

def erl(args, options)
  dirs = "-pa #{options[:dirs].join(' ')}" if options[:dirs]
  run = "-s init stop" if options[:init]
  noshell = "-noshell -noinput" if options[:noshell]
  func = "-s #{options[:func][0]} #{options[:func][1]}" if options[:func]
  mods = options[:mods].map { |e| "-s #{e}" }.join(' ') if options[:mods]
  boot = "-boot #{options[:boot]}" if options[:boot]
 
  puts "erl #{dirs} #{boot} #{noshell} #{mods} #{args} #{func} #{run}"
  system "erl #{dirs} #{boot} #{noshell} #{mods} #{args} #{func} #{run}"
end

La commande peut être ré-écrite ainsi :

erl "-mnesia dir 'db'",
    :boot => "start_sasl",
    :mods => ["reloader", "inets", "xxx"],
    :dirs => [EBIN_DIR, "#{ROOT}/deps/*/ebin"]

Ce qui, me semble, est plus compréhensible que précédemment. On peut remarquer que le cas de la base mnesia n’a pas encore été pris en compte.

Filed under: Erlang, Rake, build
5
Nov/09
0

rush, un peu plus d’OOP dans les scripts

Problématique

Le lancement de l’application se fait par une tâche Rake, comme décrit précédemment dans le premier article concernant les builds.

task :start => :build do ...

La tâche de lancement est dépendante d’un build complet.

Cependant, on ne prend pas en compte l’environnement, et différents processus doivent être lancés avant de démarrer l’application. Il s’agit d’une dépendance sur des processus externes, et cela est généralement réalisé par des appels système.

Rush

Cependant, les commandes à créer sont un peu complexes et lourdes à maintenir. Pour cela, nous utiliserons donc une application Ruby qui s’inscrit dans la direction des shell basés sur de la programmation objet, comme le powershell de Microsoft.

Bienvenue à Rush. Rush a été créé pour les tâches d’administration liées au projet Heroku comme expliqué dans un article de l’auteur.

Vous pouvez également consulter une présentation de rush, qui expliquera bien mieux que moi les intérêts d’une telle technologie.

On l’installe comme tout autre gem :

gem install rush

A noter que je suis passé à gemcutter pour la gestion des modules (nouveau site officiel de gestion des gems).

ATTENTION ! Rake n’est pas vraiment compatible avec les systèmes de shell (et ceci m’a coûté beauuucoup de temps). En effet Rake redéfini les entrées et sorties standard, ce qui fait qu’il n’est plus possible de lire sur l’entrée standard, et donc qu’il est impossible de rediriger des commandes par pipe. C’est pourquoi les commandes shell seront écrites dans un fichier séparé.

Si vous insistez, vous vous retrouverez avec cette erreur peu compréhensible :

rake aborted!
Broken pipe

Start

On peut étendre la tâche de démarrage pour ajouter une dépendance à l’environnement :

task :start => [ :build, 'init:env' ] do 
end

et créer une tâche dans l’espace init :

require 'rush'
 
namespace 'init' do
  desc "Init servers"
  task :env do
    app = Rush[ROOT]
    app["*.dump"].each{ |f| f.destroy }
    app["*.swp"].each{ |f| f.destroy }
    system "#{ROOT}/process.sh"
  end
end

Ici, on efface des fichiers temporaires à chaque nouveau démarrage en utilisant la syntaxe rush (ROOT est une constante qui représente le chemin racine du projet).

Un point important, comme indiqué précédemment, c’est que la gestion des processus est délélguée à un fichier externe, et lancée dans un shell externe par la commande ruby directement. Ceci car rake et la gem session ne sont pas compatibles.

En tout cas, il n’est pas possible de démarrer une session bash dans une tâche rake, l’inverse étant sûrement possible.

Gestion des processus

La tâche d’initialisation a pour but de vérifier que les processus serveurs sont lancés, et si non de les lancer. Les serveurs sont actuellement Nginx et CouchDB.

On peut écrire le code ainsi (dans process.sh) :

#!/usr/bin/ruby -rubygems
 
require "rush"
 
if Rush::box.processes.filter(:cmdline => /nginx/).empty?
  puts "Starting nginx"
  Rush.box.bash "nginx", :user => "root"
end
 
if Rush::box.processes.filter(:cmdline => /couchdb/).empty?
  puts "Starting couchdb"
  Rush.box.bash "couchdb -b", :user => "root"
end

Et les processus ne seront démarrés que s’ils n’existent pas.

La commande indiquée dans le site est la suivante :

if Rush::box.processes.filter(:cmdline => /nginx/).alive?

Malheureusement, cela ne fonctionne pas du fait que les processus sont lancés en tant que root, et je n’ai pas trouvé comment contourner ce problème.

Filed under: Rake, build
26
Oct/09
0

Internationalisation des vues

Moteur de transformation

L’application est bien évidemment internationalisée et donc disponible en plusieurs langues.

Le principe est le suivant :

  • On ne travaille que sur un seul modèle de vue
  • Tous les textes sont traduits dans un fichier de propriétés
  • Les vues sont uniquement statiques, pas de traduction à la volée

Le premier point est d’utiliser un mécanisme permettant de traduire les clés des fichiers modèles en traductions. Il est possible de définir son propre mécanisme de traduction assez simplement… cependant, ici, il semble plus intéressant de se reposer sur l’API fournie par la dernière version de Rails, qui est disponible en tant que gem et utilisable de façon autonome.

2
Oct/09
0

Vim et Rake

La structure de notre projet implique des phases de pré-processing permettant soit de compiler des sources en binaires, soit de transformer des modèles de vues en fichiers cibles.

Ces actions sont réalisées via le gestionnaire de tâches rake. Ces actions sont effectuées pour l’instant en ligne de commandes. Par exemple :

$ rake -T
rake build      # Build the sources
rake clean      # Clean workspace
rake compile    # Compile the sources
rake db:create  # Initialise a database
rake edoc       # Create documentation
rake front      # Preprocess views and styles
rake release    # Packaging
rake start      # Start application
rake test       # Unit tests
 
$ rake front
Creating style /d/apps/gns/priv/www/assets/styles/application.css
 
$

Nous allons intégrer ces commandes au sein de Vim afin de simplifier le processus (ne pas avoir à jongler entre les fenêtres) et l’automatiser (ne rien oublier afin de ne pas s’énerver parce que ça ne marche pas !).

25
Sep/09
0

Automatisation des microtests Erlang via Rake

Les fonctions de tests unitaires commencent à apparaitre dans plusieurs fichiers, et il devient nécessaire de pouvoir régulièrement rejouer l’ensemble de ces tests. Ceci ne pouvant se faire que de manière automatique pour être efficace.

Attention on rappelle qu’il ne s’agit ici que de vérifier simplement et rapidement des comportements écrits directement dans les fichiers sources. Il est évident que pour des opérations plus complexes, un réel outil de tests unitaires tel que eUnit devra être mis en place.

Pour cela, on va réutiliser ce que l’on a déjà mis en place précédemment concernant l’exécution de la machine virtuelle Erlang via Rake (voir posts précédents).

Le but est de parcourir l’ensemble des fichiers sources .erl et déterminer pour chacun d’eux si une fonction tes/0 a été définie, auquel cas on exécute la méthode du module en question.

L’analyse du fichier sera une bête expression régulière, et pour cela on patch la classe File de Ruby afin de conserver une certaine harmonie dans l’écriture :

class File
  def self.contains?(filename, regexp)
    text = File.read filename
    return text.match(regexp) != nil
  end
end

Pas très efficace comme programmation mais nous ne sommes concernés que par des fichiers sources qui sont, par définition, des fichiers textes de petite taille.

On crée alors une tâche Rake :

desc "Unit tests"
task :test => :compile do
  sources.each do |source|
    if File.contains? source, /^test\(\) ->$/
      mod = File.basename(source, ".erl")
      puts "Unit testing #{mod}"
      system "erl -pa #{EBIN_DIR} #{ROOT}/deps/*/ebin -noshell -noinput -s inets -s crypto -s #{mod} test -s init stop"
    end
  end
end

Cette tâche dépend de la compilation afin de pouvoir jouer les fonctions.

On a repris la commande système utilisée pour lancer l’application. Certains paramètres ont été ajoutés :

-noshell -noinput # Exécution en mode console non interactive
-s inets -s crypto # Démarrage de modules nécessaires, ici crypto est lié aux cookies
-s #{mod} test # Exécution de la fonction test() du module mod
-s init stop # Arrêt de la machine virtuelle
24
Jul/09
0

Le retour de Ruby : Haml

On ne change pas les bonnes habitudes, et une des premières chose qui me manque sur ce projet est l’utilisation des outils tels que Haml et Sass1.

Heureusement, il est très simple de les rajouter !


  1. Même lien que pour Haml 

Filed under: build
22
Jul/09
0

Build des applications Erlang

Mochiweb crée un squelette applicatif en copiant les sources vers le répertoire destination. Ces sources doivent être compilés pour obtenir un byte code exécuté par le shell Erlang.

Dans le cas d’un fichier simple, la commande erlc peut être appelée directement. Dans le cadre d’une application plus évoluée (et comme pour tous les autres langages compilés), il est nécessaire d’avoir des processus de build un peu plus complexes.

Pour Mochiweb, et comme vu précédemment, un makefile est fourni. Cependant, l’outil make est assez peu intuitif à utiliser, et étant un grand utilisateur de Ruby je me dirige naturellement vers rake.

La première étape sera de recréer les commandes de makefile en Rake.

Filed under: build