Commencer le développement web en Ruby
{ Guest Post }
Grâce au framework Ruby on Rails, Ruby est maintenant très populaire pour le développement d’application web. Ce domaine est un pleine croissance et Ruby on Rails attire beaucoup de débutants de tout horizons.
Je pense que commencer l’apprentissage d’un framework web nécessite de bien comprendre le rôle de chacun de ses composants. Des composants, Ruby on Rails en comporte beaucoup et chacun d’entre eux répond à une problématique bien précise. ORM, routeur, templates, controlleurs, tâches de fond et bien d’autres ; il y a de quoi se perdre.
Mon conseil à ceux qui débutent dans le domaine est de bien comprendre la raison d’être de chaque chose. Une manière amusante de cerner les problématiques qui ont donnés naissance aux frameworks tel que Rails, c’est de se passer complètement de ces briques. C’est à dire partir du strict minimum et ajouter, petit à petit, les composants dont on ressent le besoin.
Rack
Rack est la brique de base de la plupart des frameworks web Ruby actuels. Rack est avant tout une API définissant la manière pour un programme Ruby de prendre en charge et de répondre à une requête HTTP. Voici un exemple simple :
Dans cet exemple, on voit que l’on declare une variable app
à laquelle on assigne un object Proc
.
Cet objet app
est ensuite passé à la méthode Rack::Builder#run
indiquant ainsi au serveur compatible
ce qu’il faut exectuter lorsqu’une requête arrivera.
Regardons de plus près comment se comporte le Proc
ci-dessus. On voit qu’il prend un argument que l’on
appelle l’environement Rack. Le Proc
retourne une réponse Rack qui est un tableau contenant les
informations nécessaire à la construction de la réponse HTTP : le code de retour, les entêtes et le corps
de la reponse.
Rack ne nécessite pas d’utiliser un Proc
. La seule contrainte est d’être un objet répondant à la méthode
#call
prenant en argument un environement Rack et retournant une réponse Rack telle que nous venons de le voir.
Exécuter l’application
Pour executer ce config.ru
on peut utiliser l’outil rackup
fourni avec la gem rack
:
Cette commande va écouter sur le port 9292 et pour chaque requête appeller la méthode #call
de l’objet app
.
Lorsque l’on apportera es modification à notre code, il faudra bien penser à redémarrer le serveur : CTRL-C
pour l’arrêter et le relancer avec la commande que l’on vient de voir.
Qu’y a-t-il dans l’env
Pour voir ce qui se trouve dans l’environement je vais utiliser JSON.pretty_generate
qui va m’afficher la variable
env
au format JSON.
Après avoir modifié le config.ru
, il faut redémarrer le serveur.
Pour faire une requête, j’utilise httpie qui permet simplement d’envoyer des requêtes HTTP depuis le terminal et qui est équivalent à curl
avec de jolies couleurs en plus. Vous pouvez utiliser le client de votre choix bien entendu. Chez moi cela donne :
À l’issue de cette requête, on a bien le body <h1>Hello world</h1>
qui s’affiche.
On remarque également que dans le terminal où notre serveur est lancé, on voit s’afficher
sur la sortie standard :
C’est uniquement à partir de cette variable env
que notre application devra formuler une réponse Rack !
De Rack au frameworks Web
Dans cette partie, essayons de trouver des solutions a de petits problèmes. Bien sûr on n’utilisera que Rack.
Comme support nous nous mettrons dans le cas d’un réseau social très simplifié.
Le routage
Chaque membre de notre réseau social va avoir une page qui lui est propre.
Pour y accéder nous utilisons le chemin suivant : /members/<id>
où <id>
sera l’identifiant du membre.
Avec Rack nous pouvons écrire le code suivant pour parvenir à isoler l’identifiant du membre :
Cette méthode va nous permettre d’obtenir l’identifiant du membre en fonction de l’environement Rack. On peut introduire ce code dans notre application :
Ici on a extrait un paramètre de l’URL. Si on visite /members/Nicolas
on verra le texte Hello Nicolas s’afficher.
Par contre, si on visite /signup
, on verra Hello s’afficher seul puisque member_id
sera égal à nil
.
En pratique, notre réseau social va avoir besoin d’identifier des dixaines voir des centaines d’URLs différentes. Avec ce volume, il est nécessaire de s’organiser autrement et d’associer les URLs gérée par notre application avec le code responsable de répondre à la requête. Dans le cas ou une URL n’est pas gérée, on souhaite répondre par un code d’erreur.
Voici un extrait de code permettant de répondre à cette problématique :
On voit que j’ai implicitement définit une interface : #match?(env)
et call(env)
pour les actions que
l’application peut réaliser. Chaque action est responsable de formuler une réponse Rack lors d’un appel à la
méthode call
et de savoir, via match?
, si oui ou non elle doit s’executer.
L’ensemble des frameworks web font un traitement semblable en utilisant une solution que l’on appelle le routage. Différentes approches concernant le routage existent, voir Roda, Sinatra et Rails.
Voici par exemple un extrait de code qui utilise Sinatra pour faire exactement ce que nous avons fait :
Remarque :On voit dans cet exemple que Sinatra permet d’extraire les paramètres de l’URL automatiquement.
Les templates
Lorsque l’on développe une fonctionnalité d’un site web, il est fréquent de le faire en deux phases. Une phase de design où l’on va écrire HTML et CSS afin de visualiser le résultat voulu. Une autre phase où l’on écrira le code métier qui va injecter les bonnes valeurs dans le HTML en fonction de l’action effectuée.
Par exemple dans notre code : "<h1>Hello #{member_id}</h1>"
on injecte la variable member_id
dans
du HTML. De manière générale, le HTML est beaucoup plus volumineux que dans notre exemple.
Dans la vie de tout les jours, il est fréquent qu’une équipe soit en charge de la phase plus visuelle (HTML / CSS) et une autre en charge du code métier. Il est donc fréquent de séparer ces deux composantes de notre code.
Pour effectuer cette séparation, on a recours à des moteurs de templates. Ces briques logicielles vont nous permettre de séparer notre présentation du code métier. Voici un exemple, toujours en utilisant Rack :
Ici, on va utiliser ERB
pour charger le fichier template.html.erb
. Au sein de ce fichier,
la variable member_id
sera injectée dans le HTML grâce à la notation <%= ... %>
.
Les moteurs de templates sont nombreux : erb, haml, slim, builder, liquid etc. Leur usage va plus loin que la simple séparation du code de présentation et du code métier (voir les partials, stuctures de controle…).
Tester son application
Avant de continuer je vais déplacer le code de l’application du fichier config.ru
vers app.rb
.
Au passage, j’en profite pour extraire une classe Router
ainsi qu’une constante App
qui contiendra
notre application.
Les tests sont indispensables lors du développement d’une application. Tester une application Rack est
assez aisé grâce aux outils inclus dans la gem rack-test
. Voici un exemple d’un fichier de test écrit
avec minitest :
Pour lancer les tests, la commande : bundle exec ruby -Ilib:test *_test.rb --pride
suffit.
Le frameworks web tels que Rails instaurent des conventions et des outils par défaut pour le test de ses applications. Les outils de tests fonctionnent également hors des frameworks comme le montre notre exemple.
Pour finir
En continuant sur cette lancée, on peut rencontrer d’autres problématiques comme les sessions, la persistance, l’organisation du coe métier, le caching et bien d’autres. Il est très facile, avec un peu de recul, de créer son propre framework ou d’utiliser un micro-framework puis d’y ajouter ses propres conventions ainsi que les outils de son choix.
Même si ce billet s’adresse principalement aux débutants, j’espère qu’il touchera également quelques enseignants ou mentors. Peu importe votre profil, n’hésitez pas à partager vos premiers pas avec le développement web en Ruby ainsi que la manière dont vous l’aborderiez aujourd’hui.
Qui a écrit cet article ?
Nicolas Zermati
Software writer, building the backend of Sleekapp.io at Tigerlily