Hier, un PHP wannabee (berk), Romain cherchait à faire un screenshot d’une page web en « ligne de commande ».
Plusieurs solutions lui ont été proposées. Pour ma part, ma préférée est Selenium ! Sachant bien qu’il ne ferait jamais la chose en Ruby, j’ai donc décidé de regarder cela d’un peu plus près.

Tout d’abord, vous devez avoir Selenium RC installé et lancé.
C’est simple. Téléchargez le, rendez-vous dans le dossier selenium-server-1.0 et entrez

java -jar selenium-server.jar

Votre serveur Selenium est lancé sur le port 4444. Y’a plus qu’à l’utiliser !
Vous devez également avoir la librairie selenium-client installée.

sudo gem install selenium-client

Votre disque dur étant maintenant un peu moins vide, on peut s’attaquer au code en lui même ! :)
Je vais faire un screenshot de mon tout nouveau portfolio (encore en chantier).

On commence par la magie et on explique après.

require 'rubygems'
require 'selenium'

# We load Selenium
@selenium = Selenium::SeleniumDriver.new("localhost", 4444, "*firefox", "http://42.dmathieu.com/", 10000);
@selenium.start
@selenium.set_context("test_new")

# We go to the main page and take the screenshot
@selenium.open "/"
@selenium.capture_entire_page_screenshot(File.expand_path(File.dirname(__FILE__)) + 'screenshot.png', '');

# We unload Selenium
@selenium.stop

On commence par charger les librairies requises. Pas compliqué, on a besoin que de Selenium.

require 'rubygems'
require 'selenium'

Puis on charge Selenium en lui renseignant l’URL que l’on désire visiter et le navigateur avec lequel on désire visiter celle-ci.
Et en définissant le contexte (ici, un firefox vierge).

@selenium = Selenium::SeleniumDriver.new("localhost", 4444, "*firefox", "http://42.dmathieu.com/", 10000);
@selenium.start
@selenium.set_context("test_new")

On charge la page, on prends le screenshot et on sauvegarde l’image créée :)

@selenium.open "/"
@selenium.capture_entire_page_screenshot(File.expand_path(File.dirname(__FILE__)) + 'screenshot.png', '');

Et on oublie pas de libérer la mémoire.

@selenium.stop

Et zou, c’est super magique ! Notre beau screenshot (de la page entière, pas seulement de l’écran) est alors généré.

Vous noterez que le javascript est exécuté (tentez de désactiver le javascript de votre navigateur et regarder ce qu’il advient de mon email affiché sur la page).
Et que le rendu est effectivement le même que ce que l’on a dans le navigateur.

A quand un outil open source utilisant Selenium et générant des thumbnails ? :p

Je vous ai présenté il y a quelques jours CouchDB.
La librairie que je vous ai présenté dans le précédent article était très bien dans un but éducatif. Mais en pratique, c’est vraiment pas puissant. Aujourd’hui nous allons donc parler de CouchREST.

Commencons par installer le gem. Si vous n’avez pas Rubygems configuré pour Gemcutter, faites le maintenant !

sudo gem install couchrest

Zou ! Maintenant on se connecte, on crée une base et un document dans celle-ci !

require 'rubygems"
require "couchrest"

@db = CouchRest.database!("http://127.0.0.1:5984/my_database")
response = @db.save_doc({:name => 'My Document', :content => 'The content'})
p @db.get(response['id']).inspect

Super, mais jusque la, ça ne va pas beaucoup plus loin que ce que nous avons fait précédemment.
Voyons donc comment créer une classe qui représentera un modèle de données CouchDB.

class Document < CouchRest::ExtendedDocument
    use_database 'my_database'

    property  :name
    property  :content

    timestamps!
end

Nous créons un modèle « Document », qui possèdera les champs name et content. Mais également created_at et updated_at (remplis automatiquement grâce à timestamps!).
Maintenant nous pouvons faire les choses d’une manière similaire de ce que nous faisons avec ActiveRecord.

Créer un nouveau document.

doc = Document.new(:name => 'My Doc', :content => 'The Content')
doc.save!

Récupérer tous les documents existants

docs = Document.all

Et également appeller n’importe quelle vue grâce à des méthodes virtuelles. Par exemple si vous avez une vue « by_name ».

docs = Document.by_name

CouchRest gère même la pagination des documents (avec les méthodes de pagination internes à CouchDB).

docs = Document.all.paginate(:page => 1, :per_page => 10)

Vous pouvez donc utiliser CouchDB de manière particulièrement avancée, comme vous le faites déjà avec ActiveRecord pour vos SGBDR de type SQL.
Cependant avec CouchREST il vous faut toujours gérer la connexion à votre base de données « manuellement », dans un fichiers que vous placez dans config/initializers/ par exemple. Pas super pratique.
Heureusement il existe CouchREST Rails, qui implémente CouchREST (et uniquement CouchREST) mais gère automatiquement la connexion au serveur.

Commencez par installer le plugin.

script/plugin install git://github.com/hpoydar/couchrest-rails.git

Puis générez les divers fichiers de configuration relatifs à celui-ci.

script/generate couch_rest_rails

Vous allez maintenant notamment avoir un fichier config/couchdb.yml, qui contient la configuration relative à CouchDB en fonction de votre environnement.
Voici le contenu par défaut de ce document.

base: &base
 database_prefix:
 database_suffix: _<%= RAILS_ENV %>

development:
  host: localhost
  port: 5984
  <<: *base

test:
  host: localhost
  port: 5984
  <<: *base

A vous de configurer celui-ci comme vous le désirez en fonction de vos divers environnements et de vos besoins :)
Attention : vos modèles CouchREST ne doivent maintenant plus dépendre de ExtendedDocument mais de CouchRestRails::Document

Notre modèle précédent sera donc maintenant ainsi

class Document < CouchRestRails::Document

Redémarrez votre application et accédez directement à chacun de vos documents CouchDB via le modèle approprié :)
Si vous regardez le contenu de votre base de données, vous verrez que CouchREST fonctionne d’une manière similaire aux Single Table Inheritance de ActiveRecord.
Chaque document a un champ « couchrest-type », qui définit le modèle relatif au document.
Vous pouvez donc particulièrement aisément transformer un document en un autre document.

Supposons que vous ayez des documents de type « File » et d’autres de type « Image », vous transformerez toutes vos images en fichiers de la manière suivante.

File.all.each do |file|
    doc['couchrest-type'] = 'Image'
    doc.save!
end

Ne faites pas cela sans avoir intégralement testé toutes les conséquences possibles bien évidemment (sauf si avez le désir de mourir dans d’affreuses souffrances ;) ).

Comme vous le constatez, avec CouchREST, (même si le projet a quelques problèmes de communication), vous pouvez gérer vos données stockées dans CouchDB de manière aussi aisée que toute donnée stockée dans un SGBDR geré par ActiveRecord.

Dans un prochain (et dernier) article à propos de CouchDB, nous ferons un peu de javascript avec les vues, comment les implémenter (avec CouchREST) et comment s’en servir afin de profiter de toutes les fonctionnalités du SGBD.

De gros changements à venir à l’occasion du 200e article de ce blog.
J’ai posé jeudi matin ma démission de chez O2Sources.
A partir du 18 décembre, je ne serai donc plus salarié de la meilleure agence web Lyonnaise.

Cette année chez O2Sources a été formidable. Ce fut ma première réelle expérience en entreprise.
Je n’en regrette pas une seule journée.

Malheureusement, je ne peux faire de Ruby/Rails avec ce poste. Et développer mes compétences avec Django n’est pas mon objectif.
J’ai donc décidé de changer.

Le choix a été difficile. J’ai apparemment un profil particulièrement intéressant :mrgreen:
A ce propos, si vous êtes un développeur web avec une bonne expérience Rails et intéressé pour aller travailler en Suisse, à Lausanne, la société Eboutic cherche à étoffer son équipe (la boite est sympa et le poste est plutôt intéressant).

A partir du 4 janvier prochain, je commencerai donc à travailler pour la société LIM (excusez leur site web).
Toujours une PME (bien que plus grosse, environ 25 personnes), il ne s’agit par contre pas d’une agence web.
L’activité principale de la société est la conception et la vente de machine de forage.
Ma tâche sera de participer au développement d’une application web interne qui permettra aux machines possédant une puce GPRS de synchroniser les données statistiques prélevées en ligne.
Et bien évidemment l’application est développée en Ruby avec Rails.

Le poste est sur Vaulx en Velin. Les Lyonnais auront donc toujours le plaisir de profiter de ma présence :mrgreen:

Pour une raison que j’expliquerai dans un prochain billet, je m’intéresse actuellement à CouchDB.
Il s’agit d’un serveur de base de données orienté document.

Plus clairement, chaque serveur va avoir plusieurs bases.
Et chacunes de ces bases va avoir plusieurs documents. Chaque document pourra avoir un ou plusieurs champs et ces champs pourront varier en fonction du document.

Du coup pour une base « fichiers » par exemple, vous pourrez avoir un document qui représente une image et qui aura les champs « nom », « tumbnail » et « size ».
Et un document représentant un fichier texte et qui aura uniquement les champs « nom » et « size ». Chaque document ne possède que les champs dont il a besoin.

Pour l’installation du serveur, je vous invite à vous référer à la documentation officielle.
Une fois que cela est fait, passons à la suite de cet article.

CouchDB implémente un serveur HTTP acceptant des requêtes JSON. Mettons en place une librairie Ruby simple permettant de faire ces appels (et honteusement récupérée de la documentation CouchDB officielle).

require 'net/http'
module Couch
    class Server
        def initialize(host, port, options = nil)
            @host = host;
            @port = port;
            @options = options;
        end

        def delete(uri)
            request(Net::HTTP::Delete.new(uri))
        end

        def get(uri)
            request(Net::HTTP::Get.new(uri))
        end

        def put(uri, json)
            req = Net::HTTP::Put.new(uri)
            req["content-type"] = "application/json"
            req.body = json
            request(req)
        end

        def post(uri, json)
            req = Net::HTTP::Post.new(uri)
            req["content-type"] = "application/json"
            req.body = json
            request(req)
        end

        def request(req)
            res = Net::HTTP.start(@host, @port) {|http|
                http.request(req)
            }
            handle_error(req, res) if (not res.kind_of?(Net::HTTPSuccess))
            res
        end

        private
        def handle_error(req, res)
            e = RuntimeError.new("#{res.code}:#{res.message}\nMETHOD:#{req.method}\nURI:#{req.path}\n#{res.body}")
            raise e
        end
    end
end

Nous implémentons une méthode pour chacune des actions faisables avec tout serveur de base de données : listage des données, ajout d’un nouveau document, modification d’un document et suppression de celui-ci.

Utilisons cette librairie.

require 'json'
require 'digest/md5'

# Connexion au serveur
server = Couch::Server.new("localhost", "5984")

# Création de la base "foo"
server.put("/foo/", "")

# Ajout d'un document
doc = Hash.new(:name => 'Document Name', :content => 'The Content')
id = Digest::MD5.hexdigest(doc[:name] + '/' + doc[:content])
server.put("/foo/#{id}", doc.to_json)

# Lecture du document créé
p JSON.Parse(server.get("/foo/#{id}").body)

Détaillons ce que nous faisons.

server = Couch::Server.new("localhost", "5984")

Pour commencer, nous instancions la librairie CouchDB en précisant l’adresse du serveur et son port.

server.put("/foo/", "")

Puis nous créons une base de données nommée « foo ».
Une requête « PUT » sur l’url de la nouvelle base crée celle-ci.

doc = Hash.new(:name => 'Document Name', :content => 'The Content')
id = Digest::MD5.hexdigest(doc[:name] + '/' + doc[:content])
server.put("/foo/#{id}", doc.to_json)

Ici cela devient plus intéressant.

Nous commençons par créer un hash contenant tous les champs qui constitueront le document.
Puis nous définissons l’identifiant de ce document.

Nous définissons celui-ci en une chaine md5 et non pas un simple entier.
En effet si vous faites de la réplication de bases, vous risquez d’avoir des duplicats d’identifiants en se contentant d’un identifiant.
Ici avec une chaine md5 contenant le contenu originel du document, il y a extrêmement peu de chances que nous ayons des duplicats.

Puis nous transmettons une requête permettant de créer le document ayant l’identifiant que nous venons d’attribuer avec le contenu donné.

p JSON.Parse(server.get("/foo/#{id}").body)

Pour finir nous lisons le contenu du document que nous venons de créer.
Une requête GET sur l’url du document permet d’en retourner le contenu au format JSON, que nous parsons grâce à la librairie JSON Ruby. Il ne nous reste plus qu’à afficher le contenu de la manière dont nous le désirons.

Bien évidemment il existe de multiples librairies bien plus évoluées que ce que nous venons de faire.
Par exemple CouchREST, ActiveCouch ou RelaxDB.
Mais nous avons vu dans cet article les bases de l’utilisation de CouchDB et son fonctionnement. A vous de lire la documentation de ces projets si vous désirez les utiliser :)

Rassurez vous, je ne vais pas parler de religion.
Depuis quelques semaines déjà, j’utilise God afin de monitorer efficacement les divers processus sur mon serveur.

Ca fonctionne bien. Mais visualiser rapidement l’état de ceux-ci n’est pas évident car il faut pour cela se connecter en SSH et faire un « god status ». Pas super cool.
Après avoir cherché et pas trouvé d’interfaces web pour God, j’ai donc décidé de m’attaquer à en faire une.

Voici donc aujourd’hui la première version de l’interface entre God et les humains, j’ai nommé Jesus.
Il s’agit d’une application Ruby utilisant Sinatra.

Pour le moment, une seule action est possible : visualiser les processus gerés par God et voir si ils sont exécutés ou non.
Mais à l’avenir, il sera également possible de visualiser les logs pour chacun des processus.
Et de démarrer ou arrêter en « live » des processus.

Alors n’hésitez pas à y jeter un coup d’oeil et à me transmettre vos remarques :)

L’annonce a été faite il y a de cela un an, Rails 3 est en cours de développement.
Cela sera la prochaine version du meilleur framework web (sauf possibles releases de sécurité).

A l’heure actuelle, la 3.0 est une 3.0.pre. L’utiliser sur une application en production est donc fortement déconseillé. Et même en développement, c’est pas évident.
En revanche il est tout à fait possible d’installer cette nouvelle version dans un but de test.

Allons y pour cette installation.
Vous devez commencer par récupérer la version edge du framework. Pour cela il faut que GIT soit installé sur votre machine.
Faites un git clone du repository rails dans le répertoire de votre choix.

> cd ~/
> git clone git://github.com/rails/rails.git

A partir de ce moment, vous pouvez créer une nouvelle application rails3.

~/rails/railties/bin/rails test3

Cependant il va nous manquer quelques dépendances pour pouvoir exécuter cette application.
Tout d’abord il nous faut installer bundler, le nouveau gestionnaire de dépendances développé par Yehuda Catz.

sudo gem install bundler

Puis rendez vous dans votre application et éditez le fichier Gemfile
Placez-y les dépendances suivantes :

gem "rack",          "1.0.1"
gem "rack-mount",    :git => "git://github.com/rails/rack-mount.git"
gem "rack-test",     "~> 0.5.0"
gem "erubis",        "~> 2.6.0"
gem "arel",          :git => "git://github.com/rails/arel.git"
gem "sqlite3-ruby"
gem "rails", "3.0.pre", :git => "git://github.com/rails/rails.git"

Toutes ces dépendances seront installées automatiquement lorsque le framework passera en Beta1 et qu’il sera disponible sous la forme d’un gem.
En attendant il vous faut les installer de manière semi automatique.

Après avoir placé ces dépendances, vous pouvez les installer avec un

gem bundle

Et voila ! Vous avez votre application rails3 fonctionnelle !
Un petit

ruby script/server

Et en vous rendant à l’adresse http://localhost:3000/, vous verrez votre nouvelle application lancée.

Pour commencer à tester, faites comme d’habitude.
Générez un contrôleur et un modèle.

ruby script/generate controller index
ruby script/generate model page

Et commencez à développer.
Vos contrôleurs, modèles et vues ne changent pas de place. Mais certaines fonctionnalités peuvent ne plus être disponibles.
A vous de découvrir les nouveautés au fur et à mesure. Je ferai peut-être quelques articles ici pour certaines d’entre elles.

Ce matin je suis tombé sur un gem sympa. Il s’agit de gem stats.
L’idée est cool. Les gem hébergés sur gemcutter (et vu que bientôt, gemcutter.org deviendra rubygems.org, c’est 99.99% des gems ruby) ont des statistiques publiques. Il est donc possible à n’importe qui, via l’API gemcutter, de connaitre la version actuelle d’un gem et son nombre de téléchargements.

Et c’est ce que fait gem stats. Après l’avoir installé, si vous tapez

gem stats rails

Vous verrez la version la plus récente du gem ainsi que son nombre de téléchargements. Sympa :)

Mais ce dont je veux parler ici, c’est la manière dont ça fonctionne … Comment ajouter la commande « stats » à gem.
Il suffit pour cela d’utiliser Command Manager.

Dans le répertoire lib de votre gem, créez un fichier que vous nommerez rubygems_plugin.rb.
Puis dans ce fichier vous devrez déclarez la commande que vous souhaitez créer.

require 'rubygems/command_manager'
Gem::CommandManager.instance.register_command :neat

Maintenant il faut créer le code approprié à cette commande.
Pour cela, créer le fichier lib/rubygems/commands/neat_command.rb
Ou, bien évidemment « neat » est le nom de votre commande (déjà indiqué dans le register_command plus haut).

Puis déclarons la classe qui va bien avec cette commande.

class Gem::Commands::NeatCommand < Gem::Command
    def initialize
        super 'neat', 'A useless but particularly neat example command'
    end
    def arguments
        ''
    end
    def usage
        "#{program_name} #{arguments}"
    end

    def execute
        puts "Isn't my new gem plugin cool ?"
    end
end

Ayé, vous n’avez plus qu’à installer votre gem et votre nouvelle commande sera effective.
Ce n’est pas le sujet de cet article. Mais je vous invite fortement à utiliser jeweler pour compiler le gem.

Une fois que ceci est fait, vous pouvez tester votre nouvelle commande par vous même :

gem help neat
gem neat
 
Fork me on GitHub