Tutoriel Ruby on Rails

Apprendre Rails par l'exemple

Michael Hartl

Contenu

  1. Chapitre 1 De zéro au déploiement
    1. 1.1 Introduction
      1. 1.1.1 Commentaires pour les lecteurs différents
      2. 1.1.2 “Dimensionner” Rails
      3. 1.1.3 Conventions utilisées dans ce livre
    2. 1.2 Debout et au boulot
      1. 1.2.1 Environnements de développement
        1. IDEs
        2. Éditeurs de texte et lignes de commande
        3. Navigateurs
        4. Note à propos des outils
      2. 1.2.2 Ruby, RubyGems, Rails, et Git
        1. Installation de Rails (Windows)
        2. Installer Git
        3. Installer Ruby
        4. Installer RubyGems
        5. Installer Rails
      3. 1.2.3 La première application
      4. 1.2.4 Bundler
      5. 1.2.5 Le serveur rails (rails server)
      6. 1.2.6 Modèles-Vue-Contrôleur (MVC)
    3. 1.3 Contrôle de versions avec Git
      1. 1.3.1 Installation et réglages
        1. Initialisation des réglages système
        2. Initialisation des réglages du dépôt (repository)
      2. 1.3.2 Ajout et mandat de dépôt
      3. 1.3.3 Qu'est-ce que Git peut faire de bien pour vous ?
      4. 1.3.4 GitHub
      5. 1.3.5 Branch, edit, commit, merge
        1. Branch
        2. Edit
        3. Commit
        4. Merge
        5. Push
    4. 1.4 Déploiement
      1. 1.4.1 Réglages Heroku
      2. 1.4.2 Déploiement Heroku, première étape
      3. 1.4.3 Déploiement Heroku, seconde étape
      4. 1.4.4 Commandes Heroku
    5. 1.5 Conclusion
  2. Chapitre 2 Une application démo
    1. 2.1 Planifier l'application
      1. 2.1.1 Modéliser les utilisateurs
      2. 2.1.2 Modéliser les micro-messages
    2. 2.2 La ressource Utilisateurs (Users)
      1. 2.2.1 Un tour de l'utilisateur
      2. 2.2.2 MVC en action
      3. 2.2.3 Faiblesses de la ressource Utilisateurs (Users)
    3. 2.3 La ressource Micro-messages (Microposts)
      1. 2.3.1 Un petit tour du micro-message
      2. 2.3.2 Appliquer le micro aux micro-messages
      3. 2.3.3 Un utilisateur has_many micro-messages
      4. 2.3.4 Hiérarchie des héritages
      5. 2.3.5 Déployer l'application Démo
    4. 2.4 Conclusion
  3. Chapitre 3 Pages statiques courantes
    1. 3.1 Pages statiques
      1. 3.1.1 Pages HTML statiques
      2. 3.1.2 Les pages statiques avec Rails
    2. 3.2 Premiers tests
      1. 3.2.1 Outils de test
        1. Auto-test
      2. 3.2.2 TDD : Rouge, Vert, Refactor
        1. Spork
        2. Rouge
        3. Vert
        4. Refactor
    3. 3.3 Pages (un peu) dynamiques
      1. 3.3.1 Test d'un changement de titre
      2. 3.3.2 Réussir les tests de titre
      3. 3.3.3 Variables d'instance et Ruby embarqué
      4. 3.3.4 Supprimer les répétitions avec les layouts
    4. 3.4 Conclusion
    5. 3.5 Exercices
  4. Chapitre 4 Rails au goût Ruby
    1. 4.1 Motivation
      1. 4.1.1 Un helper pour le titre
      2. 4.1.2 Feuilles de styles (CSS — Cascading Style Sheets)
    2. 4.2 Chaines de caractères et méthodes
      1. 4.2.1 Commentaires
      2. 4.2.2 Chaines de caractères
        1. Impression
        2. Chaines de caractères « apostrophées »
      3. 4.2.3 Objets et passage de message
      4. 4.2.4 Définition de méthode
      5. 4.2.5 Retour à l'« helper » de titre
    3. 4.3 Autres structures de données
      1. 4.3.1 Tableaux et rangs
      2. 4.3.2 Blocs
      3. 4.3.3 Tables de hachage et symboles
      4. 4.3.4 CSS revisitées
    4. 4.4 Classes Ruby
      1. 4.4.1 Constructeurs
      2. 4.4.2 Héritages de classes
      3. 4.4.3 Modifier les classes d'origine
      4. 4.4.4 Classe de contrôleur
      5. 4.4.5 La classe utilisateur
    5. 4.5 Exercices
  5. Chapitre 5 Poursuivre la mise en page
    1. 5.1 Ajout de structure
      1. 5.1.1 Navigation du site
      2. 5.1.2 Personnalisation CSS
      3. 5.1.3 Partiels
    2. 5.2 Liens pour la mise en page
      1. 5.2.1 Test d'intégration
      2. 5.2.2 Routes Rails
      3. 5.2.3 Nommer les routes
    3. 5.3 Inscription de l'utilisateur : une première étape
      1. 5.3.1 Contrôleur Utilisateur
      2. 5.3.2 URL d'inscription
    4. 5.4 Conclusion
    5. 5.5 Exercices
  6. Chapitre 6 Modéliser et afficher les utilisateurs, partie I
    1. 6.1 Modèle utilisateur
      1. 6.1.1 Migrations de la base de données
      2. 6.1.2 Le fichier modèle
        1. Annotation des modèles
        2. Attributs accessibles
      3. 6.1.3 Créer des objets Utilisateur
      4. 6.1.4 Recherche dans les objets Utilisateurs
      5. 6.1.5 Actualisation des objets Utilisateurs
    2. 6.2 Validations utilisateur
      1. 6.2.1 Valider l'existence
      2. 6.2.2 Valider la longueur
      3. 6.2.3 Valider le format
      4. 6.2.4 Valider l'unicité
        1. L'avertissement d'unicité
    3. 6.3 Afficher les utilisateurs
      1. 6.3.1 Débuggage et environnements Rails
      2. 6.3.2 Modèle, Vue et Contrôleur Utilisateur
      3. 6.3.3 Ressource Utilisateurs
        1. Paramètres pour le déboggage
    4. 6.4 Conclusion
    5. 6.5 Exercices
  7. Chapitre 7 Modéliser et afficher les utilisateurs, partie II
    1. 7.1 Mots de passe non sécurisés
      1. 7.1.1 Valider le mot de passe
      2. 7.1.2 Migrer un mot de passe
      3. 7.1.3 Fonction de rappel dans l'Active Record
    2. 7.2 Sécuriser les mots de passe
      1. 7.2.1 Test de mot de passe sécurisé
      2. 7.2.2 Un peu de théorie sur la sécurisation des mots de passe
      3. 7.2.3 Implémenter la méthode has_password?
      4. 7.2.4 Méthode d'authentification
    3. 7.3 Meilleures vues d'utilisateurs
      1. 7.3.1 Tester la page de l'utilisateur (avec factories)
      2. 7.3.2 Un nom et un Gravatar
        1. Un « helper » de Gravatar
      3. 7.3.3 Une barre utilisateur latérale
    4. 7.4 Conclusion
      1. 7.4.1 Dépôt Git
      2. 7.4.2 Déploiement Heroku
    5. 7.5 Exercices
  8. Chapitre 8 Inscription
    1. 8.1 Formulaire d'inscription
      1. 8.1.1 Utiliser form_for
      2. 8.1.2 Le formulaire HTML
    2. 8.2 Échec de l'inscription
      1. 8.2.1 Test de l'échec
      2. 8.2.2 Un formulaire fonctionnel
      3. 8.2.3 Inscription : messages d'erreur
      4. 8.2.4 Filtrer les paramètres d'identification
    3. 8.3 Succès de l'inscription
      1. 8.3.1 Tester le succès de l'inscription
      2. 8.3.2 Le formulaire d'inscription finalisé
      3. 8.3.3 Le message « flash »
      4. 8.3.4 La première inscription
    4. 8.4 Test d'intégration RSpec
      1. 8.4.1 Tests d'intégration avec les styles
      2. 8.4.2 Un échec d'inscription ne devrait pas créer un nouvel utilisateur
      3. 8.4.3 Le succès d'une inscription devrait créer un nouvel utilisateur
    5. 8.5 Conclusion
    6. 8.6 Exercices
  9. Chapitre 9 Connexion, déconnexion
    1. 9.1 Les sessions
      1. 9.1.1 Le contrôleur de session
      2. 9.1.2 Formulaire d'identification
    2. 9.2 Échec de l'identification
      1. 9.2.1 Examen de la soumission du formulaire
      2. 9.2.2 Échec de l'identification (test et code)
    3. 9.3 Succès de l'identification
      1. 9.3.1 L'action create finalisée
      2. 9.3.2 Se souvenir de moi
      3. 9.3.3 Utilisateur courant
    4. 9.4 Déconnexion
      1. 9.4.1 Détruire la session
      2. 9.4.2 Connexion à l'inscription
      3. 9.4.3 Changement des liens de la mise en page
      4. 9.4.4 Test d'intégration de l'identification/déconnexion
    5. 9.5 Conclusion
    6. 9.6 Exercices
  10. Chapitre 10 Actualiser, afficher et supprimer des utilisateurs
    1. 10.1 Actualiser l'utilisateur
      1. 10.1.1 Formulaire de modification
      2. 10.1.2 Permettre les modifications
    2. 10.2 Protéger les pages
      1. 10.2.1 Utilisateurs identifiés requis
      2. 10.2.2 Nécessité du bon utilisateur
      3. 10.2.3 Redirection conviviale
    3. 10.3 Afficher les utilisateurs
      1. 10.3.1 Liste des utilisateurs
      2. 10.3.2 Exemples d'utilisateurs
      3. 10.3.3 Pagination
        1. Test de la pagination
      4. 10.3.4 Restructuration des partiels
    4. 10.4 Supprimer des utilisateurs
      1. 10.4.1 Utilisateurs administrateurs
        1. Révision de attr_accessible
      2. 10.4.2 L'action destroy (« supprimer »)
    5. 10.5 Conclusion
    6. 10.6 Exercices
  11. Chapitre 11 Micro-messages d'utilisateurs
    1. 11.1 Le modèle Micropost (« Micro-message »)
      1. 11.1.1 Le modèle initial
        1. Attributs accessibles
      2. 11.1.2 Associations Utilisateur/micro-messages
      3. 11.1.3 Affinements du micro-message
        1. Portée par défaut
        2. Dépendances de la suppression
      4. 11.1.4 Validations du micro-message
    2. 11.2 Afficher les micro-messages
      1. 11.2.1 Etoffement de la page de l'utilisateur
      2. 11.2.2 Exemples de micro-messages
    3. 11.3 Manipuler les micro-messages
      1. 11.3.1 Contrôle de l'accès
      2. 11.3.2 Créer des micro-messages
      3. 11.3.3 Une proto-alimentation
      4. 11.3.4 Supprimer des micro-messages
      5. 11.3.5 Test de la nouvelle page d'accueil
    4. 11.4 Conclusion
    5. 11.5 Exercices
  12. Chapitre 12 Suivi des utilisateurs
    1. 12.1 Le modèle Relation (Relationship model)
      1. 12.1.1 Un problème du modèle de données (et sa solution)
      2. 12.1.2 Associations Utilisateur/Relations
      3. 12.1.3 Validations
      4. 12.1.4 Auteurs suivis
      5. 12.1.5 Les Lecteurs
    2. 12.2 Une interface web pour les auteurs et les lecteurs
      1. 12.2.1 Exemple de donnée de suivi
      2. 12.2.2 Statistiques et formulaire de suivi
      3. 12.2.3 Pages d'auteurs suivis et de lecteurs
      4. 12.2.4 Un bouton de suivi standard
      5. 12.2.5 Un bouton fonctionnant avec Ajax
    3. 12.3 L'état de l'alimentation
      1. 12.3.1 Motivation et stratégie
      2. 12.3.2 Une première implémentation de peuplement
      3. 12.3.3 Champs d'application, sous-sélections et lambda
      4. 12.3.4 Nouvel état de l'alimentation
    4. 12.4 Conclusion
      1. 12.4.1 Extensions de l'application exemple
        1. Réponses
        2. Notification
        3. Notifications aux lecteurs
        4. Rappel du mot de passe
        5. Confirmation d'inscription
        6. Alimentation RSS
        7. REST API
        8. Recherche
      2. 12.4.2 Guide vers d'autres ressources
    5. 12.5 Exercices

Avant-propos

Ma précédente compagnie (CD Baby) fut une des premières à basculer intégralement vers Ruby on Rails, et à rebasculer aussi intégralement vers PHP (googlez-moi si vous voulez prendre la mesure du drame). On m'a tellement recommandé ce livre Michael Hartl que je n'ai pu faire autrement que de le lire. C'est ainsi que le Tutoriel Ruby on Rails m'a fait revenir à nouveau à Rails.

Bien qu'ayant parcouru de nombreux livres sur Rails, c'est ce tutoriel-là qui m'a véritablement « mis en possession » de Rails. Tout est fait ici « à la manière de Rails » — une manière qui ne m'avait jamais semblé naturelle avant que je ne lise ce livre. C'est aussi le seul ouvrage sur Rails qui met en place, d'un bout à l'autre, un Développement Dirigé par les Tests (Test-Driven Development), une approche que je savais hautement recommandée par les experts mais dont je n'avais jamais compris aussi bien la pertinence que dans ce livre. Enfin, en incluant Git, GitHub et Heroku dans les exemples de la démonstration, l'auteur vous donne vraiment le goût de ce qu'est le développement d'un projet dans la vie réelle. Et le exemples de code ne sont pas en reste.

La narration linéaire adoptée par ce tutoriel est vraiment un bon format. Personnellement, j'ai étudié Le Tutoriel Rails en trois longues journées, en faisant tous les exemples et les exercices proposés à la fin de chaque chapitre. C'est en lisant ce livre du début à la fin, sans sauter la moindre partie, qu'on en tire tout le bénéfice.

Régalez-vous !

Derek Sivers (sivers.org)
Précédemment : Fondateur de CD Baby
Actuellement : Fondateur de Thoughts Ltd.

Remerciements

Ce Tutoriel Ruby on Rails doit beaucoup à mon livre précédent sur Rails, RailsSpace, et donc à mon co-auteur Aurelius Prochazka. J'aimerais remercier Aure à la fois pour le travail qu'il a accompli sur ce précédent livre et pour son soutien pour le présent ouvrage. J'aimerais aussi remercier Debra Williams Cauley, mon éditeur pour les deux ouvrages ; aussi longtemps qu'elle jouera avec moi au baseball, je continuerai d'écrire des livres pour elle.

J'aimerais remercier une longue liste de Rubyistes qui m'ont parlé et inspiré au cours des années : David Heinemeier Hansson, Yehuda Katz, Carl Lerche, Jeremy Kemper, Xavier Noria, Ryan Bates, Geoffrey Grosenbach, Peter Cooper, Matt Aimonetti, Gregg Pollack, Wayne E. Seguin, Amy Hoy, Dave Chelimsky, Pat Maddox, Tom Preston-Werner, Chris Wanstrath, Chad Fowler, Josh Susser, Obie Fernandez, Ian McFarland, Steven Bristol, Giles Bowkett, Evan Dorn, Long Nguyen, James Lindenbaum, Adam Wiggins, Tikhon Bernstam, Ron Evans, Wyatt Greene, Miles Forrest, les gens bien de Pivotal Labs, le gang Heroku, les mecs de thoughtbot et l'équipe de GitHub. Enfin, tellement, tellement, tellement de lecteurs — beaucoup trop pour les citer tous — qui ont contribué par leur rapport de bogues et leurs suggestions durant l'écriture de ce livre, et je tiens à saluer leur aide sans laquelle ce livre ne serait pas ce qu'il est.

À propos de l'auteur

Michael Hartl est programmeur, éducateur et entrepreneur. Il est le co-auteur de RailsSpace, un tutoriel Rails publié en 2007, et a été co-fondateur et développeur en chef de Insoshi, une plateforme de réseau social populaire en Ruby on Rails. Précédement, il a enseigné la théorie et la physique informatique au California Institute of Technology (Caltech), où il a reçu le Lifetime Achievement Award for Excellence en enseignement. Michael est diplômé du Harvard College, a un Ph.D. en physique (un doctorat. NdT) de Caltech, et il est ancien élève du programme des entrepreneurs Y Combinator.

Copyright et license

Le Tutoriel Ruby on Rails : apprendre Rails par l'exemple. Copyright © 2010 par Michael Hartl. Tout le code source du Tutoriel Ruby on Rails est disponible sous la license MIT License et la licence Beerware License.

   Copyright (c) 2010 Michael Hartl

   Permission est accordée, à titre gratuit, à toute personne obtenant
   une copie de ce logiciel et la documentation associée, pour faire des 
   modification dans le logiciel sans restriction et sans limitation des
   droits d’utiliser, copier, modifier, fusionner, publier, distribuer,
   concéder sous licence, et / ou de vendre les copies du Logiciel, et à
   autoriser les personnes auxquelles le Logiciel est meublé de le faire, 
   sous réserve des conditions suivantes:

   L’avis de copyright ci-dessus et cette autorisation doit être inclus
   dans toutes les copies ou parties substantielles du Logiciel.

   LE LOGICIEL EST FOURNI «TEL QUEL», SANS GARANTIE D’AUCUNE SORTE, 
   EXPLICITE OU IMPLICITE, Y COMPRIS, MAIS SANS S’Y LIMITER, LES 
   GARANTIES DE QUALITÉ MARCHANDE, ADAPTATION À UN USAGE PARTICULIER ET
   D’ABSENCE DE CONTREFAÇON. EN AUCUN CAS LES AUTEURS OU TITULAIRES DU
   ETRE TENU RESPONSABLE DE TOUT DOMMAGE, RÉCLAMATION OU AUTRES
   RESPONSABILITÉ, SOIT DANS UNE ACTION DE CONTRAT, UN TORT OU AUTRE,
   PROVENANT DE, DE OU EN RELATION AVEC LE LOGICIEL OU L’UTILISATION OU
   DE TRANSACTIONS AUTRES LE LOGICIEL.
	
/*
 * ------------------------------------------------------------
 * "LA LICENCE BEERWARE" (Révision 42) :
 * Michael Hartl a écrit ce code. Aussi longtemps que vous
 * conservez cette note, vous pouvez faire ce que vous voulez
 * de ce travail. Si nous nous rencontrons un jour, et que vous
 * pensez que ce travail en vaut la peine, vous pourrez me
 * payer une bière en retour.
 * ------------------------------------------------------------
 */

Chapitre 3 Pages statiques courantes

Dans ce chapitre, nous commencerons à développer l'Application Exemple qui nous servira de base tout au long de ce tutoriel. Bien que cette Application Exemple puissent éventuellement avoir des utilisateurs, des micro-messages et un patch d'authentification complet, nous commencerons par un sujet en apparence plus limité : la création de pages statiques. Malgré son apparente simplicité, la création de pages statiques est une exercice hautement formateur, riche en implications — un parfait commencement pour notre application naissante !

Bien que Rails soit destiné à créer des sites web dynamiques avec base de données, il excèle aussi à faire ce genre de pages statiques que nous pouvons créer simplement par des fichiers de code HTML brut. En fait, utiliser Rails même pour des pages statiques produit un avantage certain : nous pouvons ajouter seulement une petite quantité de code dynamique. Nous verrons comment dans ce chapitre. Chemin faisant, nous aurons un avant-goût du testing automatique, qui nous aidera à nous sentir plus confiant en notre code. Plus loin encore, avoir une bonne suite de tests nous permettra de restructurer notre code en toute confiance, en changeant sa forme sans altérer sa fonction.

Comme au chapitre 2, avant de commencer nous avons besoin de créer un nouveau projet Rails, cette fois appelé sample_app (Application Exemple) :

$ cd ~/rails_projects
$ rails new sample_app -T
$ cd sample_app

Ici, l'option -T ajoutée à la commande rails demande à Rails de ne pas générer le dossier test associé au frameword par défaut Test::Unit. Cela ne signifie pas que nous n'exécuterons pas de tests ; au contraire, dès la section 3.2 nous utiliserons un framework de test alternatif appelé RSpec pour réaliser une suite approfondie de tests.

Comme à la section 2.1, notre prochaine étape consiste à utiliser un éditeur de texte pour actualiser le fichier Gemfile avec les gems nécessaires à notre application (souvenez-vous que vous pouvez avoir besoin de la version 1.2.5 du gem sqlite3-ruby, en fonction de votre système d'exploitation). D'un autre côté, pour l'Application Exemple, nous aurons aussi besoin de deux nouveaux gems : le gem RSpec et le gem de la librairie RSpec spécifique à Rails. Le code pour les inclure est montré dans l'extrait 3.1 (note : si vous préférez installer tous les gems nécessaire à l'Application Exemple, vous devriez utiliser le code de l'extrait 10.42 dès à présent).

Extrait 3.1. Un Gemfile pour l'Application Exemple.
source 'http://rubygems.org'

gem 'rails', '3.0.7'
gem 'sqlite3-ruby', '1.3.2', :require => 'sqlite3'

group :development do
  gem 'rspec-rails', '2.5.0'
end

group :test do
  gem 'rspec', '2.5.0'
  gem 'webrat', '0.7.1'
end

Ceci inclut rspec-rails en mode développement, de telle sorte que nous avons accès aux génératers spécifiques à RSpec, et il inclut également rspec en mode de test dans le but de jouer les tests (nous incluons aussi un gem pour Webrat, un utilitaire de test qui a l'habitude d'être installé automatiquement en tant que dépendance mais a besoin ici d'être inclus explicitement). Pour installer et inclure les gems RSpec, nous utilisons bundle install comme d'habitude :

$ [sudo] bundle install

Pour demander à Rails d'utiliser RSpec plutôt que Test::Unit (qui est utilisé par défaut), nous avons besoin d'installer les fichiers nécessaires à RSpec. Cela peut être accompli avec rails generate :

$ rails generate rspec:install

Ceci fait, il ne nous reste plus qu'à initialiser le repository Git :1

$ git init
$ git add .
$ git commit -m "Depot Initial"

Tout comme la première application, je suggère d'actualier le fichier README (situé dans le dossier racine de l'application) pour être plus utile et descriptif, comme montré dans l'extrait 3.2.

Extrait 3.2. Un fichier README amélioré pour l'Application Exemple.
# Tutoriel Ruby on Rails : Application Exemple

C'est l'Application Exemple pour le
[*Tutoriel Ruby on Rails : Apprendre Rails par l'exemple*](http://railstutorial.org/)
par [Michael Hartl](http://michaelhartl.com/).

Changez ce fichier pour utiliser l'extension markdown et déposez les changements :

$ git mv README README.markdown
$ git commit -a -m "Amelioration du README"
create_repository
Illustration 3.1: Créer le repository de l'Application Exemple sur GitHub. (taille normale)

Puisque nous utiliserons cette Application Exemple jusqu'à la fin de ce livre, c'est une bonne idée de faire un repository sur GitHub (illustration 3.1) et de la « pusher » :

$ git remote add origin git@github.com:<username>/sample_app.git
$ git push origin master

(Notez que, comme résultat de cette étape, le repository sur http://github.com/railstutorial/sample_app contient le code source de toute l'Application Exemple. Vous êtes encouragé à le consulter en guise de référence, avec deux mises en garde : (1) vous en apprendrez beaucoup plus si vous écrivez vous-même le code source, plutôt que de vous appuyer sur une version achevée ; (2) Il peut exister des différences mineures entre le repository GitHub et le code de ce livre. Cela est dû d'un côté à l'incorporation des exercices du livre et d'un autre côté au repository utilisé dans les screencasts du tutoriel Rails, qui inclut quelques tests supplémentaires).

Bien entendu, nous pouvons optionnellement déployer l'application vers Heroku même à ce stade peu avancé :

$ heroku create
$ git push heroku master

(Si cela ne fonctionne pas pour vous, consultez la note au-dessus de l'extrait 1.8 pour des solutions possibles.) À mesure que vous avancez dans le livre, je vous recommande de « pusher » et de déployer l'application régulièrement avec :

$ git push
$ git push heroku

Cela posé, nous sommes prêts à développer l'Application Exemple.

3.1 Pages statiques

Rails propose deux principales manières de créer des pages web statiques. Primo, Rails peut utiliser des pages vraiment statiques contenant du code HTML brut. Secondo, Rails nous permet de définir des vues (views) contenant du HTML brut, que Rails peut rendre de telle sorte que les serveurs web puisse les envoyer au navigateur.

Afin de prendre nos repères, il est utile de se remémorer la struture du dossier Rails de la section 1.2.3 (illustration 1.2). Dans cette section, nous travaillerons principalement sur les dossiers app/controllers (contenant les contrôleurs, controllers) et app/views (contenant les vues, views) (à la section 3.2, nous ajouterons même un dossier de notre cru).

3.1.1 Vraies pages statiques

Commençons avec les vraies pages statiques. Rappelez-vous, de la section 1.2.5, que toute application Rails se présente comme une application fonctionnelle minimale générée par le script rails, avec une page d'accueil à l'adresse http://localhost:3000/ (illustration 1.3).

public_index_rails_3
Illustration 3.2: Le fichier public/index.html(taille normale)

Pour savoir d'où cette page provient, jetez-un coup d'œil au fichier public/index.html (illustration 3.2). Comme le fichier contient ses propres informations de mise en page, c'est un peu le bazard, mais ça fonctionne : par défaut, Rails renvoie tout fichier du dossier public directement à votre navigateur.2 Dans le cas du fichier spécial index.html, vous n'avez même pas à indiquer le nom du fichier dans l'URL, puisque le nom index.html est le nom de fichier par défaut. Vous pouvez l'inclure si vous voulez, cependant ; l'adresse :

http://localhost:3000/

… et l'adresse :

http://localhost:3000/index.html

… sont équivalentes.

Comme vosu pouvez vous y attendre, si nous le voulons nous pouvons fabriquer nos propres fichiers HTML statiques, et les déposer dans le même dossier public que le fichier index.html. Par exemple, créons un fichier avec un message de salutation amicale (extrait 3.3) :3

$ mate public/hello.html
Extrait 3.3. Un fichier HTML classique, avec une salutation amicale.
public/hello.html
<!DOCTYPE html>
<html>
  <head>
    <title>Salutations</title>
  </head>
  <body>
    <p>Salut le monde&nbsp;!</p>
  </body>
</html>

Nous pouvons voir dans l'extrait 3.3 la structure caractéristique d'un document HTML : un type de document (document type), ou doctype, qui est une déclaration aux navigateurs de la version du code HTML que nous employons (dans ce cas, HTML5) ;4 une section head, dans le cas présent contenant le texte « Greeting » à l'intérieur d'une balise title ; et une section body (corps), dans le cas présennt avec le texte « Salut le monde& ! » à l'intérieur d'une balise paragraphe p (l'indentation du texte est optionnelle — HTML n'est pas sensible aux espaces « blanches », et ignore aussi bien les tabulations que les espaces — mais cela rend la structure du document plus lisible) Comme attendu, en visitant l'adresse http://localhost:3000/hello.html, Rails rend aussitôt cette page (illustration 3.3). Notez que le titre affiché au sommet de la fenêtre du navigateur dans l'illustration 3.3 correspond au contenu de la balise title, c'est-à-dire « Salutations ».

hello_world
Illustration 3.3: Notre vrai fichier HTML statique (http://localhost:3000/hello.html). (taille normale)

Puisque ce fichier n'a valeur que de démonstration, nous n'avons pas vraiment envie qu'il fasse partie de notre applicaton, donc il est peut-être mieux de le détruire une fois l'euphorie de cette création passée :

$ rm public/hello.html

Nous laisserons de côté le fichier index.html pour le moment, mais nous pourrions bien sûr l'effacer aussi, éventuellement : nous ne désirons pas que la racine de notre application soit la page par défaut de Rails montrée dans l'illustration 1.3. Nous verrons dans la section 5.2 comment changer l'adresse http://localhost:3000/ pour qu'elle pointe vers autre chose que le fichier public/index.html.

3.1.2 Les pages statiques avec Rails

La capacité de retourner des fichiers HTML statiques est bien, mais cela n'est pas particulièrement utile pour créer des applications web dynamiques. Dans cette section, nous ferons un premier pas vers la création de pages dynamiques en créant un jeu d'actions Rails, qui sont une façon plus puissante de définir des URLs que les fichiers statiques.5 Les actions Rails sont regroupées à l'intérieur de contrôleurs (controllers) (le « C » dans le MVC de la section 1.2.6), qui contient des jeux d'actions reliées par un but commun. Nous avons eu un aperçu des contrôleurs au chapitre 2, et approfondirons pour en avoir une compréhension plus profonde une fois que nous explorerons mieux l'architecture REST (à partir du chapitre 6) ; par essence, un contrôleur est le conteneur d'un groupe de pages web (peut-être dynamiques).

Pour commencer, rappelez-vous, section 1.3.5, que lorsque nous utilisons Git, c'est une bonne pratique d'exécuter notre travail sur une autre branche que la branche maitresse. Si vous utilisez Git pour le contrôle de versions, vous devriez jouer ces commandes :

$ git checkout -b static-pages

Rails est fourni avec un script pour créer des contrôleurs, appelé generate ; tout ce dont il a besoin pour faire son tour de magie, c'est le nom du contrôleur. Puisque nous créons ce contrôleur pour traiter les pages statiques (courantes), nous l'appellerons simplement le contrôleurs Pages, et planifierons de faire des actions pour la page d'accueil, la page de contact et la page « À Propos ». Le script generate prend une liste optionnelle d'actions, donc nous incluerons certaines de ces actions initiales directement en ligne de commande :

Extrait 3.4. Générer un contrôleur de Pages.
$ rails generate controller Pages home contact
      create  app/controllers/pages_controller.rb
       route  get "pages/contact"
       route  get "pages/home"
      invoke  erb
      create    app/views/pages
      create    app/views/pages/home.html.erb
      create    app/views/pages/contact.html.erb
      invoke  rspec
      create    spec/controllers/pages_controller_spec.rb
      create    spec/views/pages
      create    spec/views/pages/home.html.erb_spec.rb
      create    spec/views/pages/contact.html.erb_spec.rb
      invoke  helper
      create    app/helpers/pages_helper.rb
      invoke    rspec

(notez que, puisque nous avons installé RSpec avec le code rails generate rspec:install, la génération de contrôleur crée automatiquement des fichiers de test RSpec dans le dossier spec/).

Ici, j'ai intentionnellement « oublié » la page À Propos pour que nous puissions voir comment l'ajouter « à la main » à la section 3.2.

La génération de pages de contrôleurs de l'extrait 3.4 actualise automatiquement le fichier de routage, nommé config/routes.rb, que Rails utilise pour trouver la correspondance entre URLs et pages web. C'est notre première rencontre avec le dossier config, donc il peut être utile d'y jeter un coup d'œil (illustration 3.4). Le dossier config est l'endroit où Rails rassemble les fichiers nécessaires à la configuration de l'application — d'où bien sûr son nom.

config_directory_rails_3
Illustration 3.4: Contenu du dossier config de l'Application Exemple(taille normale)

Puis nous avons généré les actions home et contact, le routage de fichier possède déjà des règles pour chacun d'eux, comme on peut le voir dans l'extrait 3.5.

Extrait 3.5. Le routage pour l'action home et l'action contact dans le contrôleur Pages.
config/routes.rb
SampleApp::Application.routes.draw do
  get "pages/home"
  get "pages/contact"
  .
  .
  .
end

Ici la règle :

get "pages/home"

… se charge de la requête pour l'URL /pages/home pour la page d'accueil (action home) dans le contrôleur Pages. Plus encore, en utilisant get nous nous arrangeons pour que la route réponde à la requête GET, qui est un des verbes HTTP fondamentaux supporté par le Hypertext Transfer Protocol (Box 3.1). Dans notre cas, cela signifie que lorsque nous générons une action home à l'intérieur du contrôleur Pages nous atteignons automatiquement une page à l'adresse /pages/home. Pour voir le résultat, « tuez » le serveur avec Ctrl-C, jouez la commande rails server, et naviguez alors à l'adresse /pages/home (illustration 3.5).

raw_home_view
Illustration 3.5: La vue de l'accueil brute (/pages/home) générée par Rails. (taille normale)

Pour comprendre d'où ces pages proviennent, commençons par jeter un œil au contrôleur Pages dans un éditeur de texte ; vous devriez voir quelque chose comme le code de l'extrait 3.6 (vous pouvez noter que, contrairement aux contrôleurs Users et Microposts de l'application Démo du chapitre 2, le contrôleur Pages ne respecte pas les conventions REST).

Extrait 3.6. Le contrôleur Pages contruit par l'extrait 3.4.
app/controllers/pages_controller.rb
class PagesController < ApplicationController

  def home
  end

  def contact
  end
end

Nous voyons ici que le fichier pages_controller.rb définit une classe appelée PagesController. Les classes sont simplement une façon pratique d'organiser les fonctions (appelées aussi méthodes) comme les actions home et contact, qui sont définies en utilisant le mot-clé def. Le signe « < » indique que PagesController hérite de la classe Rails ApplicationController ; comme nous le verrons dans un instant, cela signifie que nos pages possèdent une grande quantité de fonctionnalités spécifiques à Rails. Nous en apprendrons plus sur les classes et l'héritage dans la section 4.4).

Dans le cas du contrôleur Pages, ces deux méthodes sont initialement vides :

def home
end

def contact
end

En pur Ruby, ces méthodes ne feraient tout simplement… rien. En Rails, la situation est différente ; PagesController est une classe Ruby, mais parce qu'elle hérite de la classe ApplicationController le comportement de ses méthodes est spécifique à Rails : en visitant l'URL /pages/home, Rails consulte le contrôleur Pages et exécute le code de l'action home, et rend alors la vue (le caractère « V » de MVC de la section 1.2.6) correspondant à l'action. Dans le cas présent, l'action home est vide, donc tous les appels à /pages/home rendent simplement la vue. Donc, à quoi ressemble une vue, et où la trouvons-nous ?

Si vous jetez un nouveau coup d'œil à la sortie de l'extrait 3.4, vous devriez être en mesure de deviner la correspondance entre les actions et les vues : une action comme home possède une vue correspondante appelée home.html.erb. Nous apprendrons à la section 3.3 ce que la partie .erb signifie ; la partie .html ne devrait pas vous surprendre puisqu'elle ressemble à HTML (extrait 3.7).

Extrait 3.7. La vue générée pour la page d'accueil (Home).
app/views/pages/home.html.erb
<h1>Pages#home</h1>
<p>Find me in app/views/pages/home.html.erb</p>

Il en va de même de la vue de l'action contact (extrait 3.8).

Extrait 3.8. La vue générée pour la page Contact.
app/views/pages/contact.html.erb
<h1>Pages#contact</h1>
<p>Find me in app/views/pages/contact.html.erb</p>

Ces deux vues sont juste des espaces réservés : elles possèdent une entête (à l'intérieur de la balise h1) et un paragraphe (la balise p) contenant le chemin d'accès au fichier correspondant. Nous allons ajouter quelque (très léger) contenu dynamique à partir de la section 3.3, mais telles quelles, ces vues marquent un point important : les vues Rails peuvent ne contenir que du code HTML statique. Du point de vue du navigateur, le code HTML des fichiers de la section 3.1.1 et la méthode controller/action des pages délivrées sont indistinctes : tout ce que voit le navigateur est de l'HTML.

Dans la suite de ce chapitre, nous ajouterons d'abord l'action about que nous avons « oubliée » à la section 3.1.2, un petit peu de contenu dynamique et nous effecturons les premiers pas vers la stylisation des pages grâce aux CSS. Avant de poursuivre, si vous utilisez Git, c'est une bonne idée d'ajouter dès maintenant les fichiers du contrôleur Pages au repository :

$ git add .
$ git commit -m "Ajout d'un controleur Pages"

3.2 Nos premiers tests

Si vous demandez à cinq développeurs Rails comment tester une partie quelconque de code, vous obtiendrez quinze réponses différentes — mais ils seront tous d'accord sur le fait que vous devrez écrire ces tests. C'est dans cet esprit que nous approcherons le testing de notre Application Exemple, en écrivant de solides tests sans se préoccuper trop pour le moment de leur perfection. Ne prenez pas les tests du Tutoriel Rails pour parole d'évangile ; ils s'appuient sur le style que j'ai développé au cours de mes propres travaux ainsi qu'à la lecture du code d'autres développeurs. À mesure que vous étofferez votre expérience en tant que développeur Rails, il n'y a aucun doute que vous développerez vos propres préférences et votre propre style en matière de test.

En complément de l'écriture des tests au cours du développement de l'Application Exemple, nous ferons aussi le choix de plus en plus fréquent d'écrire ces tests avant d'écrire le code de l'application — une approche connue sous le nom « Développement Dirigée par le Test » (test-driven development, ou TDD).6 Notre exemple spécifique sera d'ajouter une page « À Propos » à notre exemple de site. Heureusement, ajouter cette page supplémentaire n'est pas difficile — vous devriez même être en mesure de deviner la procédure en vous basant sur les exemples de la section précédente — ce qui signifie que nous pouvons nous concentrer sur le testing, qui contient pas mal d'idées nouvelles.

Tout d'abord, tester l'existence d'une page peut paraitre superflu, mais l'expérience montre que ça ne l'est pas. Tellement de choses peuvent mal tourner quand on développe un logiciel qu'avoir une bonne batterie de tests est inestimable pour assurer sa qualité. Plus encore, il est courant pour les programmes informatiques — et spécialement les applications — d'être étendus, et chaque fois que vous faites un changement vous risquez d'introduire des erreurs. L'écriture de tests ne garantit pas que ces erreurs ne surviendront pas, mais ils les rendent beaucoup plus faciles à localiser et à corriger. Plus encore, en écrivant des tests pour des bogues qui surviennet vraiment, nous pouvons réduire leur probabilité.

(Comme noté à la section 1.1.1, si vous trouvez que le testing est trop écrasant, poursuivez en les sautant au cours d'une première lecture. Une fois que vous aurez une bonne maitrise de Rails and Ruby, vous pourrez y revenir et apprendre le testing dans un second temps)

3.2.1 Outils de test

Pour écrire des tests pour notre Application Exemple, notre outil principal sera le framework appelé RSpec, qui est un langage de domaine spécifique pour décrire le comportement du code, doublé d'un programme (appelé rspec) pour vérifier le comportement attendu. Conçu pour tester n'importe quel programme Ruby, RSpec a connu un regain important dans la communauté Rails. Obie Fernandez, auteur de The Rails 3 Way, a qualifié RSpec de « La Manière Rails Way » et je suis d'accord.7

Si vous avez suivi les étapes de l'introduction, RSpec est déjà installé via le Bundler Gemfile (extrait 3.1) et la commande bundle install.

Autotest

Autotest est un outil qui joue en permanence votre suite de tests en arrière-plan, en s'appuyant sur les changements de fichiers spécifiques que vous faites. Par exemple, si vous modifiez un fichier contrôleur, Autotest jouera les tests pour ce contrôleur spécifique. Le résultat est un feedback instantané sur l'état de vos tests. Nous en apprendrons davantage sur Autotest quand nous le verrons en actions (section 3.2.2).

L'installation de Autotest est optionnelle, et sa configuration peut être un peu délicate, mais si vous parvenez à le faire fonctionner sur votre système, je suis certain que vous le trouverez tout comme moi très utile. Pour installer Autotest, installez les gems autotest et autotest-rails-pure8 comme suit :

$ [sudo] gem install autotest -v 4.4.6
$ [sudo] gem install autotest-rails-pure -v 4.1.2

La prochaine étape dépend de votre plateforme. Je passerai par les étapes pour le système OS X, puisque c'est celui que j'utilise, et donnerai ensuite les références vers les posts de blog qui parlent d'Autotest sur Linus et Windows. Sur OS X, vous devrez installer Growl (si vous ne l'avez pas déjà) et installer alors les gems autotest-fsevent et autotest-growl :9

$ [sudo] gem install autotest-fsevent -v 0.2.4
$ [sudo] gem install autotest-growl -v 0.2.9

Si FSEvent ne s'installe pas proprement, vérifiez bien que Xcode est installé sur votre système.

Pour utiliser les gems Growl et FSEvent, faites un fichier de configuration Autotest dans votre dossier Rails racine et remplissez-le avec le contenu de l'extrait 3.9 (ou de l'extrait 3.10 si l'extrait 3.9 renvoie une erreur sur votre système) :

$ mate .autotest
Extrait 3.9. Le fichier de configuration .autotest pour Autotest sur OS X.
require 'autotest/growl'
require 'autotest/fsevent'
Extrait 3.10. Un fichier .autotest alternatif nécessaire sur certains systèmes.
require 'autotest-growl'
require 'autotest-fsevent'

(Note : cela créera une configuration Autotest pour l'Application Exemple seulement ; si vous voulez partager cette configuration Autotest avec les autres projets Rails ou Ruby, vous devriez crér un fichier .autotest plutôt dans votre dossier accueil (home) :

$ mate ~/.autotest

~ (tilde) est le symbole Unix pour le « dossier home »).

Si vous tournez sous Linux avec le bureau Gnome, vous devrez essayer les étapes décrites à l'adresse Automate Everything, qui installe sur Linux un système semblable aux notifications Growl sur OS X. Les utilisateurs Windows devront essayer d'installer Growl pour Windows et suivre ensuite les instructions de la page GitHub pour autotest-growl. Les utilisateurs Linux tout comme les utilisateurs Windows peuvent avoir à jeter un œil à la page autotest-notification ; le lecteur du Tutoriel Rails Fred Schoeneman a écrit un compte-rendu intéressant sur son blog : À Propos des notifications Autotest.10

3.2.2 TDD : Rouge, Vert, Restructurer

Dans un « Développement Dirigé par les Tests » (TDD), nous écrivons d'abord un test échec : dans notre cas, une pièce de code qui exprime l'idée qu'il devrait y avoir une page « À Propos » (about). Nous lançons alors le test, dans notre cas en ajoutant l'action about et la vue correspondante. La raison typique pour laquelle nous ne faisons pas l'inverse — l'implémentation d'abord, puis le test — permet de nous assurer que nous testons vraiment la fonctionnalité que nous ajoutons. Avant d'utiliser le processus TDD, j'étais souvent surpris de découvrir que mes tests, en réalité, testaient la mauvaise chose, quand ils ne testaient pas, tout simplement, rien du tout. En s'assurant que, dans un premier temps, le test échoue et ensuite seulement réussit, nous pouvons être plus confiant sur le fait que nous accomplissons le bon test.

Il est important de comprendre que TDD n'est pas toujours le bon outil pour accomplir le travail. En particulier, quand vous n'êtes pas sûr du tout de comment résoudre un problème de programmation donné, il est souvent utile de laisser tomber les tests un moment pour écrire seulement le code de l'application, juste pour sentir à quoi la solution pourrait ressembler (dans le langage de l''Extreme Programming (XP), cette étape d'exploration est appelée une spike — une « pointe », un gros clou). Une fois que vous avez trouvé la forme générale de la solution, vous pouvez alors utiliser TDD pour implémenter une version plus efficiente.

Une façon de procéder au développement dirigé par le test est un cycle connu sous le terme « Rouge, Vert, Restructurer ». La première étape, le Rouge, se réfère à l'écriture d'un test conduisant à l'échec, que de nombreux outils de test indiquent avec la couleur rouge. L'étape suivante, Vert, se réfère à un test conduisant à la réussite, indiqué par la couleur (attendue) verte. Une fois que nous avons un test (ou une batterie de tests) qui réussit, nous sommes libres de restructurer notre code, d'en changer la forme (en éliminant les répétitions par exemple) sans en changer la fonction.

Nous n'avons pas encore de couleur, donc commençons par le Rouge. RSpec (et le testing en général) peut être intimidant de prime abord, donc nous allons utiliser les tests générés par les rails generate controller Pages (les Pages de génération de contrôleur de Rails) de l'extrait 3.4 pour commencer. Puisque je ne suis pas partisan de séparer les tests pour les Vues de ceux pour les Helpers (les « Assistants ». NdT), séparation que j'ai toujours trouvée soit fragile soit redondante, notre première étape va consister à les effacer. Si vous utilisez Git, vous pouvez le faire comme suit :

$ git rm -r spec/views
$ git rm -r spec/helpers

Dans le cas contraire, supprimez-les directement :

$ rm -rf spec/views
$ rm -rf spec/helpers

Nous allons traiter les tests pour les Vues et les Helpers directement dans les tests du contrôleur commençant à la section 3.3.

Pour commencer avec RSpec, jetez un œil aux spécifications du contrôleurs Pages11 que nous venons de générer (extrait 3.11).

Extrait 3.11. Les spécifications du contrôleur Pages généré.
spec/controllers/pages_controller_spec.rb
require 'spec_helper'

describe PagesController do

  describe "GET 'home'" do
    it "should be successful" do
      get 'home'
      response.should be_success
    end
  end

  describe "GET 'contact'" do
    it "should be successful" do
      get 'contact'
      response.should be_success
    end
  end
end

(« should be successful » signifie « devrait réussir ». NdT)

Ce code est en pur Ruby, mais même si vous avez étudié Ruby précédemment il ne vous semblera pas très familier. C'est parce que RSpec utilise la malléabilité de Ruby pour définir son propre langage de domaine (domain-specific language (DSL)) construit juste pour le testing. Le point important ici est que vous n'avez pas besoin de comprendre la syntaxe RSpec pour pouvoir utiliser RSpec. Ça peut sembler magique dans un premier temps, mais RSpec est conçu pour se lire plus ou moins comme de l'anglais, et si vous suivez les exemples du script généré et les autres exemples de ce tutoriel, vous le maitriserez rapidement.

L''extrait 3.11 contient deux blocs descriptifs (blocs describe, décrire), chacun avec un exemple (par exemple un bloc commençant avec it "…" do — il "…" fait). Concentrons-nous sur le premier pour sentir ce qui se passe :

  describe "GET 'home'" do
    it "devrait réussir" do
      get 'home'
      response.should be_success
    end
  end

La première ligne indique que nous décrivons (describe) une opération GET pour l'action home. C'est juste une description, et ça peut être ce que vous voulez ; RSpec s'en fiche, mais vous ou d'autres lecteurs humains probablement pas. Dans ce cas, "GET ’home’" indique que le test correspond à une requête HTTP GET, comme discutée dans le Box 3.1. Alors la spécification dit que lorsque vous visitez la page d'accueil, cela devrait réussir. Comme avec la première ligne, ce qui vient à l'intérieur des guillemets n'intéresse pas RSpec, ça n'est pertinent qu'aux lecteurs humains. La troisième ligne, get ’home’, est la première ligne qui véritablement accomplit quelque chose. À l'intérieur de RSpec, cette ligne soumet vraiment une requête GET ; en d'autres termes, elle agit comme un navigateur et pointe vers la page, dans ce cas /pages/home (il sait automatiquement qu'il doit toucher le contrôleur Pages puisque c'est un test de ce contrôleur Pages ; il sait qu'il doit atteindre la page d'accueil parce que nous lui disons explicitement). Enfin, la quatrième ligne dit que la réponse (response) de notre application devrait indiquer un succès (elle devrait par exemple retourner un code d'état de 200 ; voir Box 3.2).

Il est temps maintenant de faire jouer nos tests. Il existe plusierurs façons différentes et équivalentes de le faire.12 Une façon de jouer tous les tests consiste à utiliser le script rspec en ligne de commande comme suit :13

$ rspec spec/
....

Finished in 0.07252 seconds

2 examples, 0 failures

Sur certains système (spécialement ceux utilisant l'invite de commande Windows), vous pourriez avoir un message d'erreur à ce stade indiquant des difficultés à trouver le programme rspec :

C:\Sites\sample_app>rspec spec/
'rspec' is not recognized as an internal or external command,
operable program or batch file.

Dans ce cas, vous aurez besoin d'exécuter rspec par le Bundler en utilisant la commande bundle exec :

C:\Sites\sample_app>bundle exec rspec spec/
....

Finished in 0.07252 seconds
2 examples, 0 failures

Malheureusement, de nombreuses autres choses peuvent mal tourner à ce niveau. Si un test échoue, assurez-vous d'avoir bien migré la base de données avec rake db:migrate comme cela est décrit à la section 1.2.5. Si RSpec ne fonctionne pas du tout, essayez de le désinstaller et de le ré-installer :

$ gem uninstall rspec rspec-rails
$ bundle install

Si ça ne fonctionne toujours pas et que vous utilisez RVM, essayez de supprimer le gemset du tutoriel Rails et de ré-installer les gems :

$ rvm gemset delete rails3tutorial
$ rvm --create use 1.9.2@rails3tutorial
$ rvm --default 1.9.2@rails3tutorial
$ gem install rails -v 3.0.7
$ bundle install

(Si ça continue de ne pas fonctionner… je suis à court d'idées.)

En jouant rspec spec/, rspec est un programme fourni par RSpec, tandis que spec/ est le dossier où vous voulez que rspec s'exécute. Vous pouvez également jouer seulement les specs dans un sous-dossier particulier. Par exemple, la commande suivante joue uniquement ls specs des contrôleurs :

$ rspec spec/controllers/
....
Finished in 0.07502 seconds

2 examples, 0 failures

Vous pouvez aussi jouer seulement un fichier :

$ rspec spec/controllers/pages_controller_spec.rb
....
Finished in 0.07253 seconds

2 examples, 0 failures

Le résultat de ces trois commandes est le même puisque le spec du contrôleur Pages est actuellement notre seul fichier de test. Au cours de la suite de ce livre, je ne montrerai pas toujours la sortie du testing, mais vous devrez jouer rspec spec/ (ou une de ses variantes) régulièrement en poursuivant — ou, même mieux, utiliser Autotest pour jouer automatiquement la batterie de tests. En parlant de ça…

Si vous avez intallés Autotest, vous pouvez le faire travailler sur vos tests RSpec en utilisant la command autotest :

$ autotest

Si cela ne fonctionne pas, essayez :

$ bundle exec autotest

Si vous utilisez un Mac avec les notifications Growl activées, vous devriez pouvoir répliquer mes réglages montrés dans l'illustration 3.6. Avec Autotest jouant en arrière-tâche et les notifications Growl vous disant l'état de vos tests, vous pourriez bien devenir accro du développement TDD.

autotest_green
Illustration 3.6: Autotest (via autotest) en action avec les notifications Growl. (taille normale)

Spork

Vous avez peut-être noté que les ressources-système liées à la gestion d'une suite de tests peuvent être considérables. Cela s'explique par le fait que chaque fois que RSpec joue les tests, il doit recharger tout l'environnement Rails. Le serveur de test Spork14 a pour but de régler ce problème. Spork charge une seule fois l'environnement, et maintient alors un pool de processus pour jouer les prochains tests. Combiné à Autotest, Spork est particulièrement utile.

Configurer et faire fonctionner Spork peut être difficile, et c'est un sujet de niveau plutôt avancé. Si vous vous sentez dépassé, n'hésitez pas à passer cette section.

La première étape consiste à ajouter la dépendance gem spork au Gemfile (extrait 3.12).

Extrait 3.12. Un Gemfile pour l'Application Exemple.
source 'http://rubygems.org'

gem 'rails', '3.0.7'
gem 'sqlite3-ruby', '1.3.2', :require => 'sqlite3'

group :development do
  gem 'rspec-rails', '2.5.0'
end

group :test do
  gem 'rspec', '2.5.0'
  .
  .
  .
  gem 'spork', '0.9.0.rc5'
end

Ensuite, installez-le :

$ bundle install

Amorcer la configuration Spork :

$ spork --bootstrap

Maintenant nous devons éditer le fichier de configuration RSpec, spec/spec_helper.rb, pour que l'environnement soit chargé dans le bloc prefork, pour n'avoir à le charger qu'une seule fois (extrait 3.13).

Extrait 3.13. Ajout du chargement de l'environnement dans le bloc Spork.prefork.
spec/spec_helper.rb
Note : utilisez seulement ce code si vous utilisez Spork. Si vous essayez d'utiliser l''extrait 3.13 sans Spork, la suite de test de votre application ne fonctionnera pas.
require 'spork'

Spork.prefork do
  # En charger plus dans ce bloc accélèrera les tests. Cependant, 
  # si vous changez de configuration ou le code des
  # librairies chargées ici, vous devrez redémarrer spork.
  ENV["RAILS_ENV"] ||= 'test'
  require File.expand_path("../../config/environment", __FILE__)
  require 'rspec/rails'

  # Requires supporting files with custom matchers and macros, etc,
  # in ./support/ and its subdirectories.
  Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

  Rspec.configure do |config|
    # == Mock Framework
    #
    # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
    #
    # config.mock_with :mocha
    # config.mock_with :flexmock
    # config.mock_with :rr
    config.mock_with :rspec

    config.fixture_path = "#{::Rails.root}/spec/fixtures"

    # If you're not using ActiveRecord, or you'd prefer not to run each of your
    # examples within a transaction, comment the following line or assign false
    # instead of true.
    config.use_transactional_fixtures = true
  end
end

Spork.each_run do
end

Avant de lancer Spork, nous pouvons obtenir une référence du temps que prend notre batterie de tests comme suit :

$ time rspec spec/
..


Finished in 0.09606 seconds
2 examples, 0 failures

real    0m7.445s
user    0m5.248s
sys     0m1.475s

Ici la suite de tests prend plus de 7 secondes pour être exécutée même si les tests effectifs jouent en moins d'une dizaine de secondes. Pour accélérer cela, nous pouvons ouvrir une fenêtre terminal dédiée aux tests, nous rendre dans le dossier Rails racine et démarrer un serveur Spork :

$ spork
Using RSpec
Loading Spork.prefork block...
Spork is ready and listening on 8989!

Dans une autre fenêtre Terminal, nous pouvons maintenant lancer notre batterie de tests avec l'option --drb 15 et vérifier que le temps de chargement de l'environnement est considérablement réduit :

$ time rspec --drb spec/
..


Finished in 0.10519 seconds
2 examples, 0 failures

real    0m0.803s
user    0m0.354s
sys     0m0.171s

Comme attendu, les temps ont été considérablement réduits.

Pour lancer RSpec et Spork avec Autotest, nous avons besoin de configurer RSpec pour utiliser l'option --drb par défaut, ce que nous pouvons faire en l'ajoutant au fichier de configuration .rspec dans le dossier racine de Rails (extrait 3.14).

Extrait 3.14. Ajout de l'option --drb au fichier .rspec.
--colour
--drb

Avec ce fichier .rspec actualisé, la suite de tests devrait fonctionner aussi vite que précédemment, même sans l'option explicite --drb :

$ time rspec spec/
..


Finished in 0.10926 seconds
2 examples, 0 failures

real    0m0.803s
user    0m0.355s
sys     0m0.171s

Bien entendu, jouer time ici n'a de sens que pour l'illustration ; normalement, vous avez juste à jouer :

$ rspec spec/

… ou :

$ autotest

… sans la commande time.

Un petit conseil quand vous utilisez Spork : si vos tests échouent quand vous pensez qu'ils devraient réussir, le problème peut venir du chargement du prefork de Spork, qui peut parfois empêcher le chargement de certains fichiers utiles. Dans le doute, quittez alors le serveur Spok (avec Control-C) et relancez-le :

$ spork
Using RSpec
Loading Spork.prefork block...
Spork is ready and listening on 8989!
^C
$ spork

Rouge

Maintenant voyons la partie Rouge de notre cycle Rouge-Vert en écrivant un test qui va échouer sur la page « À Propos » (page about). En suivant les modèles de l'extrait 3.11, vous pouvez probablement deviner le test adéquat (extrait 3.15).

Extrait 3.15. Le spec du contrôleur Pages avec un test d'échec pour la page « À Propos ».
spec/controllers/pages_controller_spec.rb
require 'spec_helper'

describe PagesController do
  render_views

  describe "GET 'home'" do
    it "devrait réussir" do
      get 'home'
      response.should be_success
    end
  end

  describe "GET 'contact'" do
    it "devrait réussir" do
      get 'contact'
      response.should be_success
    end
  end

  describe "GET 'about'" do
    it "devrait réussir" do
      get 'about'
      response.should be_success
    end
  end
end

Notez que nous avons ajouté une ligne pour dire à RSpec de rendre les vues à l'intérieur des tests du contrôleur. En d'autres termes, par défaut, RSpec teste juste les actions à l'intérieur d'un test d'un contrôleur de test ; si vous voulez en plus qu'il rende les vues, nous devons lui demander explictement via la deuxième ligne :

describe PagesController do
  render_views
  .
  .
  .

Cela assure que si les tests réussissent, la page est vraiment là.

Le nouveau test tente d'atteindre (get) l'action about, et indique que la réponse en résultant doit être un succès. À dessein, cela échoue (avec un message d'erreur rouge), comme le montre l'illustration 3.7 (rspec spec/) et l'illustration 3.8 (autotest) (si vous testez les vues dans les contrôleurs comme le préconise ce tutoriel, cela vaut la peine de noter que changer le fichier de la vue n'invitera pas Autotest à jouer le test de contrôleur correspondant. Il existe probablement une façon de configurer Autotest pour faire cela automatiquement, mais habituellement je bascule juste vers le contrôleur et presse la touche « espace-retour » pour que le fichier soit marqué comme modifié. Sauver le contrôleur conduit Autotest à jouer les tests comme prévu).

rspec_spec_red
Illustration 3.7: Specification de l'échec pour la page About (« À Propos ») en utilisant rspec spec/(taille normale)
autotest_red
Illustration 3.8: Spécification de l'échec de la page About en utilisant Autotest. (taille normale)

C'est Rouge. Maintenant obtenons le Vert.

Vert

Rappelez-vous, de la section 3.1.2, que nous pouvons générer une page statique avec Rails en créant une action et la vue correspondante avec le nom de la page. Dans notre cas, la page « À Propos » aura d'abord besoin d'une action appelée about dans le contrôleur Pages. Ayant écrit un test d'échec, nous pouvons maintenant être sûr qu'en le faisant réussir, nous avons bien créé une page about qui fonctionne.

En suivant les modèles fournis par home et contact dans l'extrait 3.6, commençons d'abord par ajouter l'action about dans le contrôleur Pages (extrait 3.16).

Extrait 3.16. Le contrôleur Pages avec l'ajout de l'action about.
app/controllers/pages_controller.rb
class PagesController < ApplicationController

  def home
  end

  def contact
  end

  def about
  end
end

Ensuite, nous allons ajouté l'action about au fichier de routage (extrait 3.17).

Extrait 3.17. Ajout de la route about.
config/routes.rb
SampleApp::Application.routes.draw do
  get "pages/home"
  get "pages/contact"
  get "pages/about"
  .
  .
  .
end

Pour finir, nous allons ajouter la Vue about. Éventuellement, nous pourrions la remplir avec quelque chose de plus informatif, mais pour le moment nous copierons seulement le contenu des vues générées (extrait 3.7 et extrait 3.8) pour la vue about (extrait 3.18).

Extrait 3.18. Une page « À Propos » souche.
app/views/pages/about.html.erb
<h1>Pages#about</h1>
<p>Find me in app/views/pages/about.html.erb</p>

Jouer les specs et regarder les actualisations depuis Autotest (illustration 3.9) devrait retourner un message vert :

$ rspec spec/
autotest_back_to_green
Illustration 3.9: Autotest de retour au Vert : tous les tests réussissent. (taille normale)

Bien sûr, ça n'est jamais une mauvaise idée de jeter un œil à la page dans un navigtaeur pour s'assurer que les tests ne sont pas totalement absurdes (illustration 3.10).

raw_about_page
Illustration 3.10: La nouvelle page (brute de décoffrage) « À Propos » (/pages/about). (taille normale)

Restructurer

Maintenant que nous sommes au Vert, nous sommes libre de restructurer notre code en changeant sa forme sans changer sa fonction. Souvent, le codage se fait au feeling, ce qui signifie qu'il devient rapidement laid, bouffi et plein de répétitions. L'ordinateur s'en fiche, bien sûr, mais pas les humains, donc il est important de garder le code base le plus propre possible en le restructurant fréquemment. Avoir une bonne batterie de tests (qui réussissent) est un outil inestimable à cet égard, car il réduit considérablement la probabilité d'introduire des bogues en cours de restructuration.

Notre Application Exemple est un peu trop petite pour la restructurer maintenant, mais l'odeur de code sale s'infiltre par chaque fissure, donc nous n'aurons pas à attendre bien longtemps : nous serons contraints de restructurer dès la section 3.3.3 de ce chapitre.

3.3 Pages (un peu) dynamiques

Maintenant que nous avons créé les actions et les vues pour quelques pages statiques, nous allons les rendre très légèrement dynamiques en ajoutant du contenu qui change en fonction des pages : nous devons changer le titre de chaque page pour qu'il reflète son contenu. Que cela représente vraiment un contenu dynamique est discutable, mais en tout cas il présente les fondements nécessaires pour le contenu indiscutablement dynamique du chapitre 8.

(Si vous avez passé ce qui concernait le TDD de la section 3.2, assurez-vous de créer une page « À Propos » ici en utilisant le code de l'extrait 3.16, de l'extrait 3.17 et de l'extrait 3.18.)

3.3.1 Tester un changement de titre

Notre plan est d'éditer les pages Home (Accueil), Contact et About (À Propos) pour ajouter le type de structure HTML que nous avons vu dans l'extrait 3.3, en incluant des titres qui changent pour chacune des pages. C'est un sujet épineux de décider lequel de ces changements tester, et en général, tester le code HTML peut se révéler hasardeux entendu que le contenu tend à changer fréquemment. Nous garderons nos tests le plus simple possible en ne testant que le titre de la page.

PageURLTitre de baseVariable titre
Accueil/pages/home"Simple App du Tutoriel Ruby on Rails"" | Accueil"
Contact/pages/contact"Simple App du Tutoriel Ruby on Rails"" | Contact"
À Propos/pages/about"Simple App du tutoriel Ruby on Rails"" | À Propos"
Table 3.1: Les pages statiques (courantes) pour l'Application Exemple.

À la fin de cette section, nos trois pages statiques auront des titres de la forme « Simple App du Tutoriel Ruby on Rails | Accueil », où la dernière partie de titre variera en fonction de la page (Table 3.1). Nous construirons les tests d'après l'extrait 3.15, en ajoutant des tests sur le titre suivant le modèle de l'extrait 3.19.

Extrait 3.19. Un test de titre.
it "doit avoir le bon titre" do
  get 'home'
  response.should have_selector("title",
                    :content => "Simple App du Tutoriel Ruby on Rails | Accueil")
end

Ce code utilise la méthode have_selector à l'intérieur de RSpec ; la documentation sur have_selector est étonnament éparse, mais ce qu'elle fait, c'est de comparer le contenu d'un élément HTML (d'un « selector ») avec un contenu donné. En d'autres termes, le code :

response.should have_selector("title",
                  :content => "Simple App du Tutoriel Ruby on Rails | Accueil")

… s'assure que le contenu entre les balises <title></title> est bien "Simple App du Tutoriel Ruby on Rails | Accueil".16 Il est utile de mentionner que le contenu n'a pas besoin d'être exact ; toute sous-chaine fonctionne aussi, donc le code :

response.should have_selector("title", :content => " | Accueil")

… fonctionnera tout aussi bien.17

Notez que dans l'extrait 3.19 j'ai coupé le texte à l'intérieur du have_selector en deux lignes ; cela nous apprend quelque chose d'important à propos de la syntaxe Ruby : Ruby ne se soucie pas des retours à la ligne.18 La raison pour laquelle j'ai choisi de couper le code en deux est que je préfère garder des lignes de code inférieures à 80 signes pour une question de lisibilité.19 À l'heure actuelle, je trouve le formatage de ce code plutôt laid ; la section 3.5 comprend un exercice de restructuration qui lui refera une beauté.20

Ajouter les nouveaux tests pour chacune de nos trois pages statiques en suivant le modèle de l'extrait 3.19 nous fournit notre nouveau spec du contrôleur Pages (extrait 3.20).

Extrait 3.20. Le spec du contrôleur Pages avec le test des titres.
spec/controllers/pages_controller_spec.rb
require 'spec_helper'

describe PagesController do
  render_views

  describe "GET 'home'" do
    it "devrait réussir" do
      get 'home'
      response.should be_success
    end

    it "devrait avoir le bon titre" do
      get 'home'
      response.should have_selector("title",
                        :content => "Simple App du Tutoriel Ruby on Rails | Accueil")
    end
  end

  describe "GET 'contact'" do
    it "devrait réussir" do
      get 'contact'
      response.should be_success
    end

    it "devrait avoir le bon titre" do
      get 'contact'
      response.should have_selector("title",
                        :content =>
                          "Simple App du Tutoriel Ruby on Rails | Contact")
    end
  end

  describe "GET 'about'" do
    it "devrait réussir" do
      get 'about'
      response.should be_success
    end

    it "devrait avoir le bon titre" do
      get 'about'
      response.should have_selector("title",
                        :content =>
                          "Simple App du Tutoriel Ruby on Rails | À Propos")
    end
  end
end

Notez que la ligne render_views introduite dans l'extrait 3.15 est nécessaire pour que le test des titres fonctionne.

Avec ces tests en place, nous pouvons jouer :

$ rspec spec/

… ou utiliser Autotest pour vérifier que notre code est maintenant Rouge (échec des tests).

3.3.2 Réussir les tests de titre

Nous allons maintenant faire en sorte que nos tests de titre réussissent, et en même temps ajouter une structure HTML valide. Commençons avec la page d'accueil (extrait 3.21), en utilisant le même squelette HTML basique que la page « hello » de l'extrait 3.3.

Note : avec Rails 3, le générateur de contrôleur crée un fichier de mise en forme (layout), dont nous expliquerons brièvement la fonction, mais que nous devons pour le moment supprimer avant de poursuivre :

$ rm app/views/layouts/application.html.erb
Extrait 3.21. La vue pour la page d'accueil avec la structure HTML complète.
app/views/pages/home.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Simple App du Tutoriel Ruby on Rails | Accueil</title>
  </head>
  <body>
    <h1>Simple App</h1>
    <p>
      Ceci est la page d'accueil de l'Application Exemple du
      <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>.
    </p>
  </body>
</html>

L''extrait 3.21 utilise le titre testé dans l'extrait 3.20 :

<title>Simple App du Tutoriel Ruby on Rails | Accueil</title>

Les tests pour la page d'accueil devraient maintenant réussir. Nous obenons toujours du Rouge à cause des tests des pages Contact et « À Propos », et nous allons pouvoir obtenir du Vert avc le code de l'extrait 3.22 et de l'extrait 3.23.

Extrait 3.22. La vue pour la page de Contact avec la structure HTML complète.
app/views/pages/contact.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Simple App du Tutoriel Ruby on Rails | Contact</title>
  </head>
  <body>
    <h1>Contact</h1>
    <p>
      Contact Ruby on Rails Tutorial à propos de l'Application Exemple à
      <a href="http://railstutorial.org/feedback">feedback page</a>.
    </p>
  </body>
</html>
Extrait 3.23. La vue pour la page « À Propos » avec la structure HTML complète.
app/views/pages/about.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Simple App du Tutoriel Ruby on Rails | À Propos</title>
  </head>
  <body>
    <h1>À Propos de nous</h1>
    <p>
      <a href="http://railstutorial.org/">Le Tutoriel Ruby on Rails</a>
      est un projet de livre et de screencasts pour apprendre le développement web
      avec <a href="http://rubyonrails.org/">Ruby on Rails</a>. Ceci
      est l'Application Exemple du tutoriel.
    </p>
  </body>
</html>

Ces pages exemples introduisent la balise ancre tag a, qui crée des liens vers les URLs données (appelés « href » ou « hypertext reference » dans le contexte d'une balise ancre) :

<a href="http://railstutorial.org/">Tutoriel Ruby on Rails</a>

Vous pouvez voir les résultats dans l'illustration 3.11.

new_home_page
Illustration 3.11: Une page accueil minimale pour l'Application Exemple (/pages/home). (taille normale)

3.3.3 Variables d'instance et Ruby embarqué

Nous avons déjà fait beaucoup de choses dans cette section, générer trois pages valides en utilisant les contrôleurs et actions Rails, mais ce sont de pures pages statiques HTML et elles ne font donc pas la démonstration de la puissance de Rails. Plus encore, elles souffrent de terribles duplications :

  • Les titres de page sont presque (mais pas tout à fait) exactement semblables ;
  • « Simple App du Tutoriel Ruby on Rails » est commun aux trois titres ;
  • L'entière structure du code HTML est répétée dans chacune des pages.

Ce code répété est une violation du principe « Don’t Repeat Yourself » (DRY — « Ne vous répétez pas ») ; dans cette section et la suivante, nous allons « DRYer » notre code en supprimant les répétitions.

Paradoxalement, nous allons passer la première étape d'élimination des duplications en en ajoutant plus encore : nous allons faire que les titres des pages, qui sont en général les mêmes, correspondre exactement. Cela rendra plus simple la suppression en un coup de toutes les répétions.

La technique implique de créer des instances de variables à l'intérieur de nos actions. Puisque les titres des pages Accueil, Contact et À Propos ont un contenu variable, nous allons renseigner la variable @titre (prononcez « hate titre ») avec le titre approprié à chaque action (extrait 3.24).

Extrait 3.24. Le contrôleur Pages avec le titre suivant la page.
app/controllers/pages_controller.rb
class PagesController < ApplicationController

  def home
    @titre = "Accueil"
  end

  def contact
    @titre = "Contact"
  end

  def about
    @titre = "À Propos"
  end
end

Une déclaration comme :

@titre = "Accueil"

… est un assignement, qui crée dans ce cas une nouvelle variable @titre de valeur "Accueil". Le signe arobase, @, dans le nom @titre indique que c'est une variable d'instance. Les variables d'instance ont un sens plus général en Ruby (voir la section 4.2.3), mais en Rails leur rôle est principalement de lier les actions et les vues : toute variable d'instance définie dans l'action home est automatiquement accessible dans la vue home.html.erb, et aisni de suite pour toutes les paires action/vue.21

Nous pouvons voir comment cela fonctionne en remplaçant le titre littéral « Accueil » par le contenu de la variable @titre dans la vue home.html.erb (extrait 3.25).

Extrait 3.25. La Vue pour la page d'accueil avec un titre Ruby embarqué.
app/views/pages/home.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Simple App du Tutoriel Ruby on Rails | <%= @titre %></title>
  </head>
  <body>
    <h1>Sample App</h1>
    <p>
      C'est la page d'accueil de l'Application Exemple du
      <a href="http://railstutorial.org/">Tutoriel Ruby on Rails</a>.
    </p>
  </body>
</html>

L''extrait 3.25 est notre premier exemple de Ruby embarqué, également appelé ERb (vous savez maintenant pourquoi les fichiers des vues HTML possèdent l'extension .html.erb). ERb est le principal mécanisme Rails pour inclure du contenu dynamique dans les pages web.22 Le code :

<%= @titre %>

… indique par l'utilisation de <%= ... %> que Rails devra insérer le contenu de la variable @titre, quel qu'il puisse être. Quand nous visitons la page /pages/home, Rails exécute le corps de l'action home, qui procède à la déclaration @titre = "Accueil", donc dans le cas présent :

<%= @titre %>

… sera remplacé par « Accueil ». Rails rend alors la vue, en utilisant ERb pour insérer la valeur de @titre dans le gabarit, que le serveur web envoie alors à votre navigateur comme code HTML. Le résultat est exactement le même qu'auparavant, à la différence près que la partie variable du titre est générée dynamiquement par ERb.

Nous pouvons vérifier que tout fonctionne en jouant les tests de la section 3.3.1 et en constatant qu'ils réussissent toujours. Nous pouvons alors faire les remplacements correspondants dans les pages Contact et À Propos (extrait 3.26 et extrait 3.27).

Extrait 3.26. La vue pour la page Contact avec un titre en Ruby embarqué.
app/views/pages/contact.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Simple App du Tutoriel Ruby on Rails | <%= @titre %></title>
  </head>
  <body>
    <h1>Contact</h1>
    <p>
      Contact du Tutoriel Ruby on Rails à propos de l'Application Exemple à la
      <a href="http://railstutorial.org/feedback">page de feedback</a>.
    </p>
  </body>
</html>
Extrait 3.27. La vue de la page À Propos avec un titre en Ruby embarqué.
app/views/pages/about.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Simple App du Tutoriel Ruby on Rails | <%= @titre %></title>
  </head>
  <body>
    <h1>À Propos de nous</h1>
    <p>
      <a href="http://railstutorial.org/">Le Tutoriel Ruby on Rails</a>
    est un projet pour faire un livre des screnncasts pour apprendre le
    développement web avec 
    <a href="http://rubyonrails.org/">Ruby on Rails</a>. C'est
      une Application Exemple pour le tutoriel.
    </p>
  </body>
</html>

Comme précédemment, les tests devraient réussir.

3.3.4 Éliminer les duplications avec les layouts

Maintenant que nous avons remplacé la partie variable des titres de la page avec une variable d'instance et ERb, chacune de nos pages ressemble à quelque chose comme :

<!DOCTYPE html>
<html>
  <head>
    <title>Simple App du Tutoriel Ruby on Rails | <%= @titre %></title>
  </head>
  <body>
      Contenu
  </body>
</html>

En d'autres mots, toutes nos pages sont identiques en structure, incluant même le titre (à cause du code Ruby embarqué), avec pour seule exception le contenu de chaque page.

Ne serait-il pas intéressant de structurer les éléments communs dans une sorte de gabarit global (un layout), et que le contenu du body soit inséré sur cette page de base ? Vraiment, ce serait bien, et Rails nous y convie gentiment en utilisant un fichier spécial appelé application.html.erb, qui doit résider dans le dossier layouts. Pour capturer le squelette de la structure, créez le fichier application.html.erb et copiez-y le contenu de l'extrait 3.28.

Extrait 3.28. Le layout du site de l'Application Exemple.
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Simple App du Tutoriel Ruby on Rails | <%= @titre %></title>
    <%= csrf_meta_tag %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

Remarquez ici la ligne spéciale :

<%= yield %>

Ce code est responsable de l'insertion du contenu de chaque page dans le layout. Comme pour <%= @titre %>, la balise <% ... %> indique du Ruby embarqué, et le signe égal dans <%= ... %> assure que les résultats de l'évaluation de l'expression soient insérés à ce point exact du template (ne vous inquiétez pas pour la signification du mot « yield » ( )rendement) dans ce contexte ;23 ce qui importe c'est d'être certain qu'avec ce layout, la visite de la page /pages/home convertira le contenu du fichier home.html.erb en HTML et l'insèrera alors à la place du <%= yield %>.

Maintenant que nous avons un layout pour le site, nous avons aussi saisi l'opportunité d'ajouter une fonctionnalité de sécurité pour chaque page. L'extrait 3.28 ajoute ce code :

<%= csrf_meta_tag %>

… qui utilise la méthode Rails csrf_meta_tag qui empêche le cross-site request forgery (CSRF), un type d'attaque web malicieuse. Ne vous pré-occupez pas trop du détail (je ne le fais pas) ; sachez simplement que Rails travaille dur pour la sécurité de votre application.

Bien sûr, les vues des extrait 3.25, extrait 3.26 et extrait 3.27 contiennent toujours toute la structure de code HTML que nous venons tout juste d'introduire dans le layout, donc il nous faut la retirer, en ne laissant que le contenu intérieur. Les vues clarifiées en résultant apparaissent dans les extrait 3.29, extrait 3.30 et extrait 3.31.

Extrait 3.29. La vue Accueil avec la structure HTML supprimée.
app/views/pages/home.html.erb
<h1>Sample App</h1>
<p>
  C'est la page d'accueil de l'Application Exemple du
  <a href="http://railstutorial.org/">Tutoriel Ruby on Rails</a>.
</p>
Extrait 3.30. La vue Contact avec la structure HTML supprimée.
app/views/pages/contact.html.erb
<h1>Contact</h1>
<p>
  Contact du Tutoriel Ruby on Rails à propos de l'Application Exemple à la
  <a href="http://railstutorial.org/feedback">page de feedback</a>.
</p>
Extrait 3.31. La vue À Propos avec la structure HTML supprimée.
app/views/pages/about.html.erb
<h1>About Us</h1>
<p>
  <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
  est un projet de livre et de screencasts pour apprendre le développement web
  avec <a href="http://rubyonrails.org/">Ruby on Rails</a>. C'est l'Application Exemple de ce tutoriel.
</p>

Avec ces vues redéfinies, les pages Accueil, Contact et À Propos avec exactement les mêmes que précédemment — c'est-à-dire que nous les avons restructurées avec succès — mais elles contiennent beaucoup moins de duplications de code. Et, comme requis, les tests réussissent toujours.

3.4 Conclusion

Vu de l'extérieur, ce chapitre n'a pas accompli grand chose : nous l'avons commencé avec des pages statiques, et nous le finissons avec… les pages le plus souvent statiques. Mais les apparences sont trompeuses : en développant en termes de Contrôleurs Rails, d'Actions Rails et de Vues Rails, nous sommes maintenant en mesure d'ajouter n'importe quel contenu dynamique à notre site. Voir comment cela joue exactement est la tâche que se propose la suite de ce tutoriel.

Avant de poursuivre, prenez le temps de « commettre » vos changements et les fusionner avec la branche maitresse. Dans la section 3.1.2, nous avons créé une branche Git pour le développement des pages statiques. Si vous n'avez pas fait de dépôts à mesure que nous avancions, commencez par en faire un en indiquant que nous avons atteint un point d'arrêt :

$ git add .
$ git commit -m "Fini avec les pages statiques"

Fusionnez alors les changements dans la branche maitresse en utilisant la même technique que dans la section 1.3.5 :

$ git checkout master
$ git merge static-pages

Une fois que vous atteignez un point d'arrêt comme celui-ci, c'est habituellement une bonne idée de « pusher » votre code sur le repository à distance (qui, si vous avez suivi les étapes de la section 1.3.4 doit se trouver sur GitHub) :

$ rspec spec/
$ git push

Si vous le désirez, à ce point vous pouvez même déployer l'application actualisée sur Heroku :

$ rspec spec/
$ git push heroku

Notez que dans les deux cas j'ai joué rspec spec/, juste pour m'assurer que tous les tests réussissaient toujours. Faire jouer les tests avant de « pusher » du code ou de déployer l'application est une habitude à cultiver.

3.5 Exercises

  1. Créez une page d'aide pour l'Application Exemple. Écrivez d'abord un test pour vérifier l'existence d'une page à l'URL /pages/help. Écrivez ensuite un second test pour le titre « Simple App du Tutoriel Ruby on Rails | Aide ». Faites le nécessaire pour que ces tests réussissent, et remplissez alors la page Aide avec le contenu de l'extrait 3.32.
  2. Vous avez peut-être noté quelques répétitions dans les specs du contrôleur Pages (extrait 3.20). En particulier, la base du titre, « Simple App du Tutoriel Ruby on Rails », est la même pour tous les tests de titre. En utilisant la facilité RSpec before(:each), qui exécute un code avant chaque cas testé, remplissez l'extrait 3.33 pour définir une instance de variable @base_title qui éliminera cette redondance (ce code utilise deux nouveaux éléments : un symbole, :each, et une opération de concaténation de chaine, +. Nous en apprendrons plus sur ces deux éléments au chapitre 4, et nous reverrons before(:each) à la section 6.2.1). Notez que, avec la base du titre capturée dans une variable d'instance, nous sommes maintenant en mesure d'aligner :content avec le premier caractère à l'intérieur de chaque parenthèse ouverte. C'est ma convention préférée pour formater du code découpé en plusieurs lignes.
Extrait 3.32. Code pour la page d'aide proposée.
app/views/pages/help.html.erb
<h1>Aide</h1>
<p>
  Obtenez de l'aide sur le Tutoriel Ruby on Rails sur la
  <a href="http://railstutorial.org/help">page d'aide du Tutoriel Rails</a>.
  Pour obtenir de l'aide sur cette Application Exemple, voyez le 
  <a href="http://railstutorial.org/book">livre du Tutoriel Rails</a>.
</p>
Extrait 3.33. Le spec du contrôleur Pages avec une base de titre.
spec/controllers/pages_controller_spec.rb
require 'spec_helper'

describe PagesController do
  render_views

  before(:each) do
    #
    # Define @base_title here.
    #
  end

  describe "GET 'home'" do
    it "devrait réussir" do
      get 'home'
      response.should be_success
    end

    it "devrait avoir le bon titre" do
      get 'home'
      response.should have_selector("title",
                                    :content => @base_title + " | Home")
    end
  end

  describe "GET 'contact'" do
    it "devrait réussir" do
      get 'contact'
      response.should be_success
    end

    it "devrait avoir le bon titre" do
      get 'contact'
      response.should have_selector("title",
                                    :content => @base_title + " | Contact")
    end
  end

  describe "GET 'about'" do
    it "devrait réussir" do
      get 'about'
      response.should be_success
    end

    it "devrait avoir le bon titre" do
      get 'about'
      response.should have_selector("title",
                                    :content => @base_title + " | About")
    end
  end
end
  1. Comme précédemment, vous pouvez trouver le fichier « augmenté » de l'extrait 1.5 plus pratique en fonction de votre système. 
  2. En fait, Rails s'assure que les requêtes pour de tels fichiers [(never hit the main Rails stack)(n'atteignent jamais la pile principale de Rails)] ; ils sont livrés directement du système de fichier (voir The Rails 3 Way pour de plus amples détails). 
  3. Comme d'habitude, remplacez mate avec la commande de votre propre éditeur de texte. 
  4. HTML change avec le temps ; en faisant une déclaration de doctype explicite nous nous arrangeons pour que les navigateurs rendent toujours nos pages proprement dans le futur. Le doctype <!DOCTYPE html> extrêmement simple est caractéristique du dernier standard HTML, HTML5. 
  5. Notre méthode pour créer des pages statiques est probablement la plus simple, mais ce n'est pas la seule. La méthode optimale dépend vraiment de vos besoins ; si vous comptez sur un grand nombre de pages statiques, utiliser un contrôleur Pages peut être très lourd, mais dans notre Application Exemple nous n'avons besoin que de quelques pages statiques. Lisez ce post de blog sur des pages simples avec has_many :through pour une vue d'ensemble des techniques de création de pages statiques avec Rails. Attention : la discussion est plutôt de niveau avancé, donc vous avez peut-être intérêt à attendre un peu avant d'essayer de la comprendre. 
  6. Dans le contexte de RSpec, TDD est aussi connu sous le nom de Behavior Driven Development, or BDD (Développement Conduit par le Comportement) (franchement, je ne suis pas convaincu qu'il y ait une énorme différence). 
  7. Le frameword de testing Shoulda est une bonne alternative (et en fait peut être utilisé avec RSpec). C'est l'autre « manière Rails », pour ainsi dire. 
  8. Cela a été utilisé pour autotest-rails, mais ce gem dépend de la suite complète ZenTest, qui occasionne des problèmes sur certains systèmes. Le gem autotest-rails-pure évite cette dépendance. 
  9. Le gem Autotest Growl fait que les résultats du test sont automatiquement affichés à l'écran tandis que le FSEvent fait qu'Autotest utilise les évènements du système de fichier d'OS X pour déclencher la suite de tests, plutôt que continuellement sonder le système de fichier. Notez également qu'avec ces deux gems vous pouvez avoir besoin d'utiliser une version actualisée si vous travaillez sous le système OS X Snow Leopard. 
  10. http://fredschoeneman.posterous.com/pimp-your-autotest-notification 
  11. Dans le contexte de RSpec, les tests sont souvent appelés des specs, mais pour la simplicité je m'en tiendrai la plupart du temps au terme « test »— sauf quand je me réfèrerai à un fichier tel que pages_controller_spec, dans ce cas j'utiliserai « spec du contrôleur Pages ». 
  12. La plupart des IDEs ont également une interface de testing, mais comme cela a été noté à la section 1.2.1 j'ai une expérience limitée de ces outils. 
  13. Vous pouvez aussi jouer rake spec, qui est par essence équivalent (fâcheusement, si vous voulez jouer rake spec ici vous devrez d'abord jouer rake db:migrate, quand bien même les tests de ce chapitre ne requièrent par de base de données). 
  14. Un spork est la combinaison « spoon-fork » (cuillière-fourchette). Mon sentiment est que le nom du projet est un jeu de mot sur l'utilisation de Spork des fourchettesPOSIX
  15. DRb signifie « Distributed Ruby ». 
  16. Nous apprendrons à la section 4.3.3 que la syntaxe :content => "…" est un tableau hash utilisant un symbole comme clé. 
  17. Je considère que c'est un recul par rapport à la version RSpec 1.3, qui utilisait have_tag dans ce context, qui pourrait être utilisé pour requérir une correspondance plus exacte. Malheureusement, cette écriture have_tag n'est plus utilisable avec RSpec 2. 
  18. Une nouvelle ligne est ce qui arrive à la fin de la ligne, commençant avec, hé bien, une nouvelle ligne. Dans le code, c'est représenté par le signe \n
  19. En vérité, le comptage des colonnes peut vous rendre fou, ce qui explique que de nombreux éditeurs de texte possèdend une aide visuelle pour vous aider. Considérons TextMate, par exemple ; si vous revenez à l'illustration 1.1, vous verrez une petite ligne verticale sur la droite pour aider à garder le code sous les 80 signes (il est en fait à 78 colonnes, ce qui vous laisse une marge d'erreur). Si vous utilisez TextMate, vous pouvez trouver cette fonctionnalité dans le menu View > Wrap Column > 78
  20. Rails 2.3/RSpec 1.3, utilisait le code have_tag plus court au lieu de have_selector, l'argument :content n'était pas nécessaire non plus. Nouveau ne signifie pas toujours meilleur… 
  21. En fait, la variable d'instance est vraiment lisible dans n'importe quelle vue, un fait dont nous nous servirons à la section 8.2.2
  22. Il existe un second système de template populaire appelé Haml, que j'aime personnellement, mais il n'est pas encore assez standard pour l'utiliser dans l'introduction d'un tutoriel. Si cela se révèle suffisamment intéressant, je pourrais produire une série de screencasts pour le Tutoriel Rails utilisant Haml pour les vues. Cela permettrait aussi une introduction à Sass, le technologie sœur de Haml, si quelque chose peut être plus impressionnant que Haml. 
  23. Si vous avez étudié Ruby auparavant, vous pouvez vous douter que Rails soumet le contenu à un block, et votre supposition sera exacte. Mais, aussi loin qu'on développe des applications web avec Rails, ça n'est pas très important, et je n'ai honnêtement jamais accordé plus de réflexion au sens du mot <%= yield %>