mardi 22 septembre 2009

mlBlocks

J'ai vérifié de nouveau que mon vieux clone de Tetris en Ocaml marchait encore. Ce fut également l'occasion pour mettre quelques captures sur la section Web de la forge Ocaml.

Ocaml n'est certainement pas prêt pour les blockbusters en 3D, mais pour coder de petits jeux, il est très certainement à la hauteur!



lundi 21 septembre 2009

Le système de tags

Voilà, le système de tags est ajouté. L'on peut insérer de nouveaux tags, ou les supprimer d'un clic. Pour les tags qui n'existent pas encore, il est possible de taper leur nom directement.



Ce fut une belle plongée dans les CSS, et je suis maintenant à peu près satisfait de l'apparence générale de l'application. Il me reste maintenant à améliorer l'ajout de nouveaux documents, qui repose pour l'instant sur un script un petit peu fragile.

vendredi 11 septembre 2009

Redirections à travers Ocaml

Mes remerciements les plus vifs à un japonais inconnu dont le post(?) sur un blog(?) m'a débloqué! J'essayais de gérer les redirections entre des programmes démarrés à travers Ocaml, et j'avais un mal fou à rediriger les flux. Je n'ai rien compris à son post, juste vu le code, et j'ai compris que je m'y prenais comme un pied.

Maintenant, j'ai une fonction toute bête pour prendre un JPEG en entrée et ressortir le texte en sortie:


let gocr file =
let i_file = Unix.openfile file [Unix.O_RDONLY] 0 in
let gocr_in, jpg_out = Unix.pipe() in
let result, gocr_out = Unix.pipe() in
let pid0 =
Unix.create_process
"jpegtopnm"
[|"jpegtopnm"|] i_file jpg_out Unix.stderr in
Unix.close jpg_out;
let pid1 =
Unix.create_process
"gocr"
[|"gocr"; "-"|] gocr_in gocr_out Unix.stderr in
Unix.close gocr_out;
ignore(Unix.waitpid [] pid0);
ignore(Unix.waitpid [] pid1);
let buffer = Buffer.create 4096 in
let read_buffer = String.create 512 in
let r = ref true in
while !r do
let len = Unix.read result read_buffer 0 512 in
if len > 0 then
Buffer.add_substring buffer read_buffer 0 len
else
r := false
done;
Buffer.contents buffer


L'on ouvre donc le fichier d'entrée, un pipe entre jpegtopnm et gocr, et un pipe entre gocr et notre sortie (si quelqu'un a une idée pour se débarrasser du deuxième pipe, je suis preneur!). L'on chaîne les processus avec les descripteurs de fichiers ainsi récupérés, et, très important, on ferme le pipe d'output après l'avoir donné à un processus. Pour la fin, je lis bêtement depuis mon pipe jusque dans un buffer, jusqu'à ce que le pipe soit vide, et je renvoie le contenu du buffer.

Ceci m'approche d'autant plus du moment où je vais enfin pouvoir faire la reconnaissance de caractères directement depuis le serveur web.

mardi 1 septembre 2009

Ocsigen - Images dynamiques

Voilà un sujet sur lequel il m'a fallu batailler ferme! C'est tout l'intérêt d'Ocaml: la difficulté est d'arriver à écrire du code qui compile. Une fois que le compilo est content, le code fonctionnera comme convenu. C'est exactement ce qui s'est passé avec mes images dynamiques.

Les documents et miniatures sont tous sauvés dans la base de données de Médoc. Quand bien même balancer ce genre de gros blobs à travers du SQL n'est pas franchement recommandé, je pense qu'il s'agissait là d'un des cas de figure autorisés. En effet, sauver ces images dans la base permet de centraliser toutes les données, ce qui simplifie la sauvegarde et la restauration et permet de laisser à la base le soin de chiffrer les données.

Cela veut dire qu'il faut pouvoir générer une image à partir d'un buffer binaire récupéré de la base. Ça tombe bien, Ocsigen sait envoyer du binaire via un Streamlist. Écrivons par exemple un service Ocsigen qui affiche une image à partir d'une chaîne binaire:


(* Petites déclarations ennuyeuses *)
open Lwt
open XHTML.M
open Eliom_services
open Eliom_parameters
open Eliom_sessions
open Eliom_predefmod.Xhtml

(* Déclaration du service, appelé "view", et qui prend *)
(* l'identifiant entier de l'image en paramètre *)
let view = Eliom_services.new_service ["view"] (int "id") ()

(* Initialisation du service *)
let _ = Eliom_predefmod.Streamlist.register view
(fun sp id () ->
return
([fun () ->
return (Ocsigen_stream.of_string (get_data id))],
"image/png"))

Il suffira d'écrire la fonction get_data qui prend dans notre cas l'identifiant entier de l'image à afficher, et qui retournera la chaîne contenant les données binaires de l'image.

Ce service peut ensuite être utilisé dans la déclaration d'un élément img:
 <img src="view?id=2"/>
pour afficher des images à l'intérieur d'une page (attention aux performances quand même, quand on va taper dans la base pour chaque image!).