Description et utilisation de minirails
une version simplissime de Ruby on Rails

Guy Lapalme

Contexte

Ruby on Rails (RoR) est un framework de développement d'applications Web qui permet de développer rapidement des applications qui intègrent l'accès aux bases de données dans un contexte d'interaction basé sur le paradigme Model-View-Controller (MVC).

Il y a plusieurs (presque trop) sources d'information sur RoR sur le web, dont les suivantes:

Toutefois, l'utilisation efficace de RoR me semble assez difficile pour un débutant en informatique qui ne connaît pas Ruby ou qui n'est pas familier avec l'accès à une base de données. Comme un des arguments de vente de RoR est la convention au lieu de la configuration, le programmeur se retrouve trop souvent à suivre des recettes dans les tutoriaux sans toujours comprendre tout ce qui se passe. Et pourtant la séparation entre le modèle, les vues et le contrôleur est une excellente idée pour le développement des applications web.

Afin de mieux faire ressortir le modèle MVC sans avoir à configurer un accès à une base de données qui, pour les débutants, relève un peu de la magie, j'ai développé, dans un but pédagogique seulement, une version de RoR grandement simplifiée avec les caractéristiques suivantes:

Concepts de base de Ruby on Rails

Avant de commencer à écrire une application minirails ou RoR, il faut déterminer trois types d'information qui forment le framework Modèle-Vue-Contrôleur (MVC):

Configuration de l'espace de travail

Il faut créer un répertoire de travail initialisé avec le fichier minirails et le contenu du répertoire common. Le plus simple est de dézipper ce fichier en le renommant au besoin. Pour les curieux, vous pouvez consulter les fichiers sources de minirails ou via son API.

Les éléments du modèle MVC seront installés dans un cadre relativement rigide qui permet au système de les retrouver. Ce cadre est un répertoire, à l'intérieur du répertoire minirails, créé par l'appel suivant:

ruby minirails nom de l'application

Par exemple, ruby minirails bibliotheque créera un répertoire bibliotheque contenant les répertoires suivants:

Développement de l'application

La plupart du temps, le développement d'une application minirails consiste à éditer des fichiers dans les sous-répertoires app.

Démarrage du serveur web

RoR peut en principe fonctionner sur tout serveur web configuré de façon approprié. Il est toutefois suggéré de développer une application web sur son propre serveur local afin d'éviter les problèmes de communication, de ne pas avoir à relancer un serveur public en cas de plantage et d'avoir un accès complet aux traces et messages d'erreurs qui sont souvent cachés sur les serveurs publics par souci de confidentialité.

minirails a plutôt été conçu pour une utilisation locale et l'implantation du serveur, basée sur WEBrick, est minimaliste.

Pour lancer, le serveur web d'une application sur le port 3000 de votre machine locale, il faut se déplacer dans le répertoire de l'application (p.e. cd bibliotheque) et faire la commande

ruby script/server

L'usager peut utiliser ce serveur en tapant l'adresse: http://localhost:3000/nom_du_modèle

Définition d'un modèle

Un modèle minirails est un objet avec des attributs. Toutes les instances des modèles sont conservés dans une base de données qui en minirails est un simple fichier en format CSV dont la première ligne donne les noms des colonnes qui serviront de noms d'attributs visibles et modifiables pour les instances. Ce fichier est conservé dans le répertoire db.

Par convention, le nom de la classe est le nom d'un type d'objet au singulier (p.e. livre) alors que le nom du fichier CSV conservant les instances de cette classe est le nom de la classe au pluriel (p.e. livres.csv). Pour chaque objet, minirails définit une méthode d'instance correspondant à chaque nom de colonnes de la base de données. Chaque objet possède aussi un attribut id qui est un entier généré automatiquement et qui sert de clé pour cet enregistrement. Dans minirails, les id débutent à 0 et correspondent initialement aux lignes du fichier de modèle (i.e. le premier enregistement a le numéro 0 et ainsi de suite). Les instances crées par l'application auront des id consécutifs qui peuvent changer d'une exécution à une autre.

Une application peut typiquement comporter plusieurs modèles. Une des contraintes les plus fortes de minirails est le fait qu'il n'y a pas de liens implicites entre les différents modèles, ce que fait admirablement le vrai RoR.

Le lien entre une instance d'un objet et la base de données est géré par la classe ActiveRecord qui est une super-classe directe ou indirecte de chaque modèle. Par exemple, la classe Livre dans le fichier models/livre.rb sera défini de la façon suivante:

require "../common/active_record.rb" class Livre < ActiveRecord end

Ce code n'a pas être tapé par l'usager mais est plutôt généré en utilisant la commande

ruby script/generate controller modèle

modèle est livre dans notre exemple. Cette commande doit être exécutée dans le répertoire de l'application.

Le fait d'hériter d'ActiveRecord permet aux instances d'une classe de fournir les méthodes d'accès et de modification ayant pour nom le nom des colonnes de la base de données. La classe hérite également des méthodes de classe load_db et save_db permettant de lire et de réécrire les instances du modèle. La méthode de classe find permet de chercher des objets pour lesquels le bloc associé retourne vrai et la méthode de classe delete(ids) détruit des enregistrements dont les ids sont donnés en paramètre.

On trouvera ici une description plus complète de l'API où en cliquant sur un nom de méthode, on obtient le code source Ruby.

Voici quelques illustration de créations et de manipulation d'objets instances de la classe Livre qui pourrait servir de modèle (dans les deux sens du mot!).

require 'app/models/Livre' # lire la base de données pour initialiser quelques instances Livre.load_db(File.join("app","db","livres.csv")) # créer une nouvelle instance et initialiser ses champs un à un l=Livre.new() l.titre="La Peste" l.auteur="Camus" l.isbn="4567890" l.save # créer une nouvelle instance et initialiser ses champs dans un bloc Livre.new do |l| l.titre= "Le Chat" l.auteur="Geluck" l.isbn="000333666" l.save end # créer une nouvelle instance en donnant une liste d'associations qui forment un Hash l=Livre.new(:titre=>"Les misérables", :auteur=>"Victor Hugo", :isbn=>"343-2145") l.save # insérer dans la bd # créer une nouvelle instance et l'ajouter à la base Livre.create(:titre=>"Le bourgeois gentilhomme", :auteur=>"Molière", :isbn=>"567843") # créer plusieurs instances à partir d'un tableau de Hash Livre.create([{:titre=>"Le Matou",:auteur=>"Yves Beauchemin",:isbn=>"345689"}, {:titre=>"Le Chien-chien",:auteur=>"Jean Narrache",:isbn=>"9878654"}]) # chercher un enregistrement et modifier son isbn l=Livre.find(){|l|l.isbn=='343-2145'} l[0].isbn="514-343-2145" # chercher et imprimer tous les livres puts "**Tous les livres",Livre.find(:all) # chercher les livres dont le titre débute par le mot "Le" puts "**Livres dont le titre débute par 'Le'",Livre.find{|l|l.titre.match(/^Le\b/)} # détruire 3 livres Livre.delete(2,6,3) # recopier sur la BD Livre.save_db

Définition d'un contrôleur

Le contrôleur définit l'ensemble des opérations, chacune définie par une méthode du contrôleur, qu'un utilisateur peut effectuer sur un modèle. L'usager appelle une de ces opérations en l'indiquant dans l'url à la suite du nom de modèle, séparé par un oblique (slash). Les paramètres de la méthode sont aussi indiqués dans l'url, chacun séparé par un oblique. Par exemple, pour afficher un livre dont l'identificateur est 2, on pourrait taper

http://localhost:3000/livre/montrer/2

Le plus souvent une telle action sera le résultat d'une action de l'usager dans une page, par exemple en remplissant un formulaire. Il est aussi possible de récupérer des paramètres de façon plus classique via l'URL (c'est-à-dire des couples attributs valeurs suivant un ?) dans le hash params qui est mis à jour dans l'environnement par la servlet qui gère les url.

Il est préférable de prévoir une action index qui sera appelée lorsque l'usager ne spécifie pas d'action. Typiquement, une action modifie le modèle et fait en sorte que la vue reflète cette modification. La vue (un fichier avec extension .rhtml) qui sera appelée a le même nom que l'action à moins que que le contrôleur n'appelle explicitement la méthode

render(:action => nom de la vue)

Définition d'une vue

Une vue, définie pour chaque action du contrôleur d'un modèle, est associée à un fichier .rhtml c'est-à-dire un fichier html mais qui peut contenir des instructions Ruby qui sont évaluées dans le contexte du modèle. Ces instructions Ruby peuvent donc faire référence au méthodes du modèle. Les actions Ruby sont insérées entre <% ... %> ou <%= ... %>. Dans ce dernier cas, le résultat de l'évaluation Ruby est transformé en chaîne de caractères (en appelant la méthode to_s si nécessaire) et inséré dans le source HTML qui sera envoyé au browser.

Les vues (c-à-d les fichiers .rhtml) sont placées dans un sous-répertoire du répertoire views. Le nom du sous-répertoire est le nom du modèle (p.e. livre). Par défaut, une vue est affichée dans le contexte d'une mise en page commune pour le modèle; cette mise en page est définie par un fichier rhtml du nom du modèle (p.e. livre.rhtml) dans le sous-répertoire layouts de views. Le contenu spécifique pour la vue est indiquée par

<%= @content_for_layout %>

dans la mise en page générale. Comme la vue spécifique est évaluée avant la mise en page générale, il est possible de définir des variables d'instances du modèle qui seront utilisées dans la vue générale.

Une vue peut générer des liens vers d'autres pages ou appeler d'autres actions en appelant la fonction

link_to(name, options = {}, html_options = nil)

name sera le texte du lien à cliquer; options est un hash qui peut fournir des valeurs pour :controller, :action et possiblement :id; les valeurs de html_options seront ajoutées comme valeurs des attributs au lien html généré. minirails ne traite qu'un infime sous-ensemble de la foultitude d'options de link_to de RoR.

Typiquement le résultat d'une action a d'un contrôleur sera suivie de l'affichage de la vue a.rhtml. Toutefois, une action peut se terminer par un appel à

render(:action => "b")

b est le nom d'une autre action. Le contrôleur appelle alors cette dernière action b et ce sera la vue b.rhtml qui sera appelée. La seule autre option de render supportée par minirails est :layout=>false qui indique que la vue sera affichée telle quelle et non dans le contexte de la mise en page commune du répertoire layouts.

Vous trouverez ici un exemple de création d'une petite application en Minirails