Création d'un premier projet avec Sinatra
2010-07-27 #mvc#ruby#sinatra
Ceci est la traduction du tutoriel Sinatra "Project 1: Reverse" de Darren Jones.
Après avoir brillamment suivi pas à pas mon premier tutoriel pour installer Ruby et Sinatra sur mon PC Windows 7, je continue sur ma lancée avec la réalisation du deuxième tutoriel proposé par Darren Jones.
Le but de ce tutoriel est de programmer une première application très très simple qui va se contenter d'afficher un texte à l'envers, d'où son nom : Projet 1 : Reverse. À travers cette mini-application, on peut déjà apprendre quelques trucs sur le fonctionnement de Sinatra, voire sur Ruby si on débute comme moi.
C'est parti
Pour commencer, j'ai créé un répertoire C:\Ruby\projets\reverse puis un fichier main.rb à l'intérieur :
require 'rubygems'
require 'sinatra'
get '/' do
"I did it my way!"
end
Toutes les applications Sinatra ont besoin des deux premières lignes. Il y a ensuite une ligne blanche pour faire plus joli puis 3 lignes de code où se situe toute l'action :
get
indique quelle méthode HTTP on souhaite gérer : un GET dans le cas présent,'/'
correspond à la route à gérer, soit la racine de l'application dans ce cas,do ... end
est un bloc de code pour définir ce qui se passe quand quelqu'un demande la racine du site.
La dernière ligne à l'intérieur du bloc do ... end
contient
toujours (je pense) ce qui sera affiché dans la page, soit "I did it my way!"
dans ce premier exemple.
Pour tester ce code, on peut directement double-cliquer sur le fichier main.rb dans le répertoire C:\Ruby\projets\reverse ou faire ça à la main dans une invite de commande :
C:\Ruby\projets\reverse>ruby main.rb
Et Sinatra entre en scène :
== Sinatra/1.0 has taken the stage on 4567 for development with backup from WEBrick
[2010-07-27 21:44:57] INFO WEBrick 1.3.1
[2010-07-27 21:44:57] INFO ruby 1.9.1 (2010-07-02) [i386-mingw32]
[2010-07-27 21:44:57] INFO WEBrick::HTTPServer#start: pid=3604 port=4567
On peut alors lancer un navigateur pour appeler l'URL http://localhost:4567/ :
Tant qu'on est là, si on essaie d'aller sur une URL qui n'existe pas, comme http://localhost:4567/reverse, on obtient alors la page d'erreur 404 de Sinatra pour indiquer qu'il ne connait pas ce morceau :
Cette page d'erreur nous conseille même sur la route ajouter dans notre fichier pour que cela fonctionne, en l'occurrence :
get '/reverse' do
"Hello World"
end
Avec Sinatra, c'est pas plus compliqué que ça pour créer des actions correspondant à différentes routes.
Ajouter une vue
Pour l'instant, on va rester sur notre route '/'
et essayer
de faire un peu mieux que de seulement renvoyer une ligne de texte. Pour cela,
il faut créer une vue en modifiant le fichier main.rb de la façon
suivante :
require 'rubygems'
require 'sinatra'
get '/' do
erb :home
end
__END__
@@ home
<h1>Reverse</h1>
<p>Welcome to the home page of my very first Sinatra app.</p>
Ce coup-ci, au lieu d'utiliser la dernière ligne du bloc pour dire à Sinatra
ce qu'il doit afficher, nous lui avons demandé d'utiliser la vue "home" que
nous avons codé en erb (embedded ruby). Cette vue est enregistrée à la fin du
fichier, après la ligne __END__
et elle est repéré par le code
@@ home
.
Pour voir ce que donne cette vue, il faut revenir à l'invite de commande et
arrêter le serveur s'il est toujours en cours d'exécution. Ctrl-C => Sinatra
has ended his set (crowd applauds). Puis on relance le serveur avec ruby main.rb
et on réaffiche la page http://localhost:4567 :
Créer une vue externe
En fait, on n'est pas obligé de stocker les vues dans le même fichier. Il est bien plus pratique de les enregistrer dans un sous-répertoire "views" de notre projet (soit C:\Ruby\projets\reverse\views dans mon cas).
Là, il suffit de créer le fichier "home.erb" avec le code ci-dessous :
<h1>Reverse</h1>
<p>Welcome to the home page of my very first Sinatra app.</p>
Il est alors possible de simplifier le fichier "main.rb" de la façon suivante :
require 'rubygems'
require 'sinatra'
get '/' do
erb :home
end
Il ne reste plus qu'à contrôler que tout est ok : Ctrl-C, ruby main.rb, rafraichir la page et vérifier que rien n'a changé.
Créer un layout
D'un point de vue visuel, on peut faire encore mieux en définissant un "layout" qui servira de gabarit pour englober toutes les vues. Cela permet d'éviter de répéter le même code dans toutes les vues de l'application.
Pour cela, on doit juste créer un fichier "layout.erb" dans le sous-répertoire "views" et y saisir le code html ci-dessous :
<!DOCTYPE html>
<html lang="en">
<head>
<title>Reverse!</title>
<meta charset=utf-8 />
</head>
<body>
<h1>Reverse</h1>
<%= yield %>
<p>The first Sinatra project for I Did It My Way</p>
</body>
</html>
Tout ce code html sera toujours affiché à chaque fois qu'une vue sera
affichée, à part la ligne <%= yield %>
qui sera remplacée
par le contenu spécifique de la vue.
Ainsi, si on modifie légèrement le code de la vue "home.erb" :
<h2>Home</h2>
<p>Welcome to the home page. This app is going to be amazing....</p>
Le fait de relancer le serveur et de ré-afficher l'URL http://localhost:4567 doit donner le résultat suivant :
Comme vous pouvez le constater, le sous-titre "Home" et le message
"Welcome..." en provenance de la vue "home.erb" apparaissent entre le titre
"Reverse" et le paragraphe "The first app...", soit exactement là où se situait
la ligne <%= yield %>
dans le fichier "layout.erb".
ERB
La balise <%= yield %>
est un exemple d'embedded
ruby (ou erb en abrégé). On peut ainsi ajouter du code ruby dans les
fichiers html en l'insérant à l'intérieur de blocs <% ... %>
. C'est très utile dans le cas de conditions
if
:
<% if something_happens %>
<h1>Something happened</h1>
<% else %>
<h1>Nothing happend</h1>
<% end %>
Si le code ruby est placé dans un bloc <%= ... %>
, alors
ce code est évalué et son résultat est affiché. Comme par exemple dans le code
suivant :
<% title = "Reverse" %>
<h1>
<%= title %>
</h1>
Le premier bloc de code <% ... %>
défini une variable
appelée "title" et le second bloc de code <%= ... %>
évalue
cette variable et affiche sa valeur. Même si cet exemple est ultra simple, dans
la vrai vie on peut faire des tas de chose très utile grâce à l'embedded
ruby.
Définir une variable
Plus concrètement, nous allons utiliser embeded ruby pour définir le titre de notre page. Dans un premier temps, on met à jour le code de "main.rb" :
require 'rubygems'
require 'sinatra'
get '/' do
@title = "Home"
erb :home
end
Celui-ci initialise une variable session nommée @title
(c'est une variable
session parce que son nom débute par un @
). Les variables session sont
disponibles dans les autres parties du code, y compris dans les vues. On peut
donc maintenant faire référence à notre variable session @title
dans notre vue
ou même notre layout.
Par conséquent, nous pouvons modifier "layout.erb" pour qu'il utilise notre variable session :
<!DOCTYPE html>
<html lang="en">
<head>
<title>Reverse!</title>
<meta charset=utf-8 />
</head>
<body>
<h1>Reverse</h1>
<h2><%= @title %></h2>
<%= yield %>
<p>The first Sinatra project for I Did It My Way</p>
</body>
</html>
Puis nous pouvons alors supprimer le titre qui était en dur dans la vue "home.erb" :
<p>Welcome to the home page. This app is going to be amazing....</p>
Par acquit de conscience, on peut redémarrer le serveur et contrôler que rien n'a changé, ce qui signifie que notre variable session a bien été prise en compte et que nous pouvons maintenant définir le titre de la page au niveau de l'action.
On va vérifier cela en créant une nouvelle route dans le source "main.rb" :
require 'rubygems'
require 'sinatra'
get '/' do
@title = "Home"
erb :home
end
get '/frank' do
@title = "My Way"
erb :home
end
Après avoir encore une fois redémarré le serveur, on peut aller à la page http://localhost:4567/frank pour constater que le sous-titre n'est plus "Home" mais "My Way" :
Mais on continue à voir presque la même chose, étant donné qu'on utilise la même vue dans les deux cas. On va donc créer une vue différente qui s'affichera pour la route "/frank". Pour cela, on saisi le code suivant dans un fichier "frank.erb" à créer dans le sous-répertoire "views" :
<p>
And now, the end is here
And so I face the final curtain
My friend, I'll say it clear
I'll state my case, of which I'm certain
I've lived a life that's full
I traveled each and ev'ry highway
And more, much more than this, I did it my way
</p>
Il faut aussi changer la ligne erb:home
par
erb:frank
dans le cas de la route "/frank" avant de redémarrer le
serveur et de ré-afficher la page http://localhost:4567/frank pour voir ce que
cela donne.
Poster quelque chose
Jusqu'à présent on n'a pas fait grand chose d'autre que des pages statiques. Mais quand on crée une application web, c'est quand même pour avoir un peu d'interaction avec le visiteur. On va donc rendre les choses un peu plus intéressante en commençant par créer un formulaire dans notre page d'accueil. Pour cela, on remplace tout le code de "home.erb" par le code suivant :
<form action="/reverse" method ="post" accept-charset="utf-8">
<input type="text" id="phrase" name="phrase" value="Write something...">
<input type="submit" value="...and reverse it!">
</form>
Une fois que c'est fait, on a aussi besoin d'une nouvelle action pour prendre en compte le formulaire lorsqu'il est envoyé. Si vous observez le code html de "home.erb", vous pouvez voir que le formulaire va être posté à l'URL "/reverse" avec une méthode POST.
Normalement, c'est le moment où vous devriez vous rendre compte que la syntaxe de Sinatra est bien faite. Et vous devriez même avoir une idée ce que nous allons ajouter dans le fichier "main.erb" :
require 'rubygems'
require 'sinatra'
get '/' do
@title = "Home"
erb :home
end
post '/reverse' do
params.inspect
end
L'avant-avant dernière ligne (l'antépénultième pour les érudits) défini la
nouvelle action qui va gérer la route "/reverse". Cette action est définie pour
une route de type POST, ce qui signifie qu'elle ne sera activé que pour une
requête POST (ce qui correspond à l'envoi d'un formulaire). Ça c'est bon ?
Mais par contre, c'est quoi cette ligne params.inspect
?
"params" est une collection qui contient toutes les informations qui ont été
envoyées en tant que paramètre (aussi bien à travers un formulaire que via
l'URL) Par conséquent, params.inspect
affiche les paires
clé/valeur correspondantes à tous ces paramètres.
Allez. On relance le serveur, on accède à la page http://localhost:4567/, on saisi une phrase au hasard et on clique sur le bouton [... and reverse it] :
Cela signifie que la clé "phrase" contient la valeur "I Did It My Way". Et
nous avons une clé "phrase" parce que le formulaire dans "home.erb" contient
une balise input dont l'attribut name est "phrase". Il est possible d'accéder à
n'importe quel paramètre stocké dans la collection "params" en employant la
syntaxe params[:key]
. Par exemple, params[:phrase]
renverra "I Did It My Way".
Supposons que l'on ait le formulaire suivant :
<form action="/reverse" method ="post" accept-charset="utf-8">
<input type="text" name="name">
<input type="text" name="email">
<input type="text" name="password">
<input type="submit" value="submit">
</form>
On va pouvoir accéder aux valeurs de ce formulaire en utilisant
params[:name]
, params[:email]
et
params[:password]
.
Maintenant que nous savons comment accéder aux données d'un formulaire, nous allons pouvoir faire quelque chose du texte saisi dans notre vue. Pour cela, nous ajoutons le code ci-dessous au fichier "main.rb" :
require 'rubygems'
require 'sinatra'
get '/' do
@title = "Enter Your text here"
erb :home
end
post '/reverse' do
@title = "Here's Your Reversed Text:"
params[:phrase].reverse
end
On re-démarre le serveur, on ré-accède à la page http://localhost:4567/, on re-saisi une phrase au hasard et on re-clique sur le bouton [... and reverse it]. Cette fois-ci, on doit voir la phrase saisie affichée à l'envers, de la droite vers la gauche :
On obtient ce résultat parce que la dernière ligne de la méthode est
params[:phrase].reverse
. Et si vous vous souvenez bien, la
dernière ligne d'une méthode est ce qui est renvoyé pour l'URL demandée.
params[:phrase]
correspond au texte entré dans le formulaire et le
.reverse
à sa suite correspond à la méthode "reverse" pour les
chaines de caractères. Et cette méthode fait exactement ce que son nom laisse
supposer, soit inverser l'ordre des caractères d'une phrase. Coup de bol, c'est
aussi ce à quoi notre application était destinée !
Faire un postback
On peut pousser le bouchon encore plus loin et utiliser la même URL pour nos deux pages (l'affichage du formulaire et l'affichage du résultat de notre application). C'est possible parce que pour afficher le formulaire on fait une requête GET et que pour inverser le texte on envoie le formulaire avec une requête POST. Par conséquent, on peut donc gérer ces deux actions avec la même route, mais deux actions différentes.
On peut donc réécrire "main.rb" :
require 'rubygems'
require 'sinatra'
get '/' do
@title = "Enter Your text here"
erb :home
end
post '/' do
@title = "Here's Your Reversed Text:"
params[:phrase].reverse
end
Cette fois-ci, l'URL de la route est toujours la même, mais la méthode HTTP est différente (soit un GET, soit un POST). Le fait de poster vers soit-même s'appelle un "postback". En plus du GET et du POST, Sinatra gère les deux autres méthodes HTTP, à savoir PUT et DELETE. Nous n'en avons pas besoin pour cette application, mais nous aurons l'occasion d'y revenir dans un autre projet.
Pour que le postback fonctionne, il faut aussi penser à modifier l'attribut action du formulaire au niveau de la vue "home.erb" pour qu'il pointe vers la même URL :
<form action="/" method ="post" accept-charset="utf-8">
<input type="text" id="phrase" name="phrase" value="Write something...">
<input type="submit" value="...and reverse it!">
</form>
Et comme pour l'instant le résultat de notre application se présente seulement sous la forme d'une ligne de texte, on va enjoliver ça en créant une nouvelle vue "reverse.erb" qui va nous permettre d'afficher ce résultat de façon un peu plus élégante :
<h3>Here is your reversed text......</h3>
<p><strong><%= @reversed_text %></strong></p>
Il suffit alors de référencer cette vue dans le fichier "main.rb", dans
lequel nous initialisons la variable session @reversed_text
que nous avons
utilisée dans notre nouvelle vue :
require 'rubygems'
require 'sinatra'
get '/' do
@title = "Enter Your text here"
erb :home
end
post '/' do
@title = "Here's Your Reversed Text:"
@reversed_text = params[:phrase].reverse
erb :reverse
end
Si on relance tout et que l'on fait tout bien comme il faut, on arrive sur l'écran suivant :
Les paramètres nommés
On pourrait en rester là puisque l'application fait ce qui était prévu. Mais on peut faire mieux, comme par exemple permettre à l'utilisateur d'indiquer la phrase qu'il veut inverser directement au niveau de l'URL. OK, mais comment faire pour retrouver le texte qui a été proposé ? Pas compliqué, il faut juste ajouter une route avec un paramètre nommé :
get '/:phrase' do
Cela va ajouter automatiquement un paramètre "phrase" à la collection
"params". On retrouvera donc tout ce que l'utilisateur aura indiqué dans l'URL
en utilisant params[:phrase]
. Par exemple, pour l'URL
http://localhost:4567/frank on aura params[:phrase]
égal à "frank" et pour
l'URL http://localhost:4567/sinatra on aura params[:phrase]
égal à "sinatra"
.
On va donc compléter notre fichier "main.rb" pour lui ajouter du code destiné à gérer cette route :
require 'rubygems'
require 'sinatra'
get '/' do
@title = "Enter Your text here"
erb :home
end
post '/' do
@title = "Here's Your Reversed Text:"
@reversed_text = params[:phrase].reverse
erb :reverse
end
get '/:phrase' do
@title = "Here's Your Reversed Text:"
@reversed_text = params[:phrase].reverse
erb :reverse
end
Voyons voir si ça marche. Tout ce qu'il y a à faire (après avoir relancé le serveur pour la dernière fois), c'est d'appeler l'URL http://localhost:4567/ suivi d'une phrase de votre choix et de vérifier que ce texte s'affiche bien à l'envers, comme dans la copie d'écran ci-dessous :
Voilà c'est fini
Et ainsi se termine le tutoriel consacré à la première application Sinatra proposée par Darren Jones (@daz4126) dont ce billet constitue une traduction très très libre.