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 5 Poursuivre la mise en page

Dans le but de faire un bref tour de Ruby au chapitre 4, nous avons ajouté quelques feuilles de styles en cascade (CSS) à la mise en page de notre site (section 4.1.2). Dans ce chapitre, nous allons ajouter quelques styles de notre cru, tout en ajoutant à notre layout des liens vers certaines pages (telles que la page Accueil ou la page « À Propos ») que nous avons créées précédemment. Chemin faisant, nous apprendrons des choses sur les partiels (partials), le routage Rails et les tests d'intégration. Nous terminerons en passant une étape importante : permettre à nos utilisateurs de s'inscrire sur notre site.

5.1 Ajout de structure

Ce Tutoriel Rails est un livre sur le développement web, pas sur le design web, mais ce serait un peu déprimant de travailler sur un site qui ressemblerait à une pouillerie, donc dans cette section nous allons structurer un peu notre mise en page et lui donner un minimum de style avec les CSS. Nous donnerons aussi du style à notre code, pour ainsi dire, en utilisant les partiels (partials) pour mieux organiser le layout lorsqu'il deviendra trop fouilli.

Quand on construit des applications web, il est souvent utile d'avoir le plus tôt possible un aperçu général de l'interface de l'utilisateur. Tout au long de la suite de ce libre, j'ajouterai donc des maquettes (appelées aussi wireframes dans le contexte du web), qui sont des croquis de ce à quoi pourra ressembler l'application.1 Dans ce chapitre, nous développerons principalement les pages statiques introduites à la section 3.1, en incluant un logo du site, une entête de navigation et un pied de page. Une maquette pour la plus importante de ces pages, la page d'accueil, est présentée dans l'illustration 5.1 (vous pouvez voir le résultat final dans l'illustration 5.8. Vous noterez qu'elle différe en quelques détails — par exemple, le pied de page contient quatre liens au lieu de trois — mais ça ne pose pas de problème, puisqu'une maquette n'a pas vocation à être exacte).

home_page_mockup
Illustration 5.1: Une maquette (anglaise) de la page Accueil de l'application exemple. (taille normale)

Comme d'habitude, si vous utilisez Git pour le contrôle de version, ce serait une bonne chose de créer une nouvelle branche :

$ git checkout -b filling-in-layout

Vous pouvez avoir encore le fichier example_user.rb du chapitre 4 dans le dossier de votre projet, si c'est le cas, vous devriez peut-être le supprimer.

5.1.1 Navigation

La dernière fois que nous avons vu le layout du site application.html.erb dans l'extrait 4.3, nous venions juste d'ajouter les feuilles de styles Blueprint en utilisant l'helper Rail stylesheet_link_tag. Il est temps d'ajouter un certains nombre de styles, un tout spécialement pour le navigateur Internet Explorer et un pour notre fichier CSS personnalisé qui sera bientôt ajouté. Nous ajouterons aussi quelques divisions (div), quelques identifiants id et quelques classes class, et le départ du matériel de navigation de notre site. Le fichier complet est présenté dans l'extrait 5.1 ; les explications des différentes parties suit l'extrait. Si vous préférez ne pas remettre à plus tard votre plaisir, vous pouvez voir le résultat dans l'illustration 5.2 (note : je vous l'accorde, ça n'est pas encore très gratifiant).

Extrait 5.1. Le layout du site avec la structure ajouté.
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <%= csrf_meta_tag %>
    <!--[if lt IE 9]>
    <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->    
    <%= stylesheet_link_tag 'blueprint/screen', :media => 'screen' %>
    <%= stylesheet_link_tag 'blueprint/print',  :media => 'print' %>
    <!--[if lt IE 8]><%= stylesheet_link_tag 'blueprint/ie' %><![endif]-->
    <%= stylesheet_link_tag 'custom', :media => 'screen' %>
  </head>
  <body>
    <div class="container">
      <header>
        <%= image_tag("logo.png", :alt => "Application Exemple", :class => "round") %>
        <nav class="round">
          <ul>
            <li><%= link_to "Accueil", '#' %></li>
            <li><%= link_to "Aide", '#' %></li>
            <li><%= link_to "Inscription", '#' %></li>
          </ul>
        </nav>
      </header>
      <section class="round">
        <%= yield %>
      </section>
    </div>
  </body>
</html>

Jetons un coup d'œil aux nouveaux éléments en commençant par le haut. Comme noté brièvement à la section 3.1, Rails 3 utilise HTML5 par défaut (comme le doctype <!DOCTYPE html> l'indique) ; puisque le standard HTML5 est récent, certains navigateurs (spécialement Internet Explorer) ne le supportent pas encore complètement, donc nous incluons un peu de code JavaScript (connu sous le nom anglais de “Shiv HTML5”) pour contourner ce problème :

<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->

La syntaxe quelque peu bizarre :

<!--[if lt IE 9]>

n'inclut les lignes entre les balises <!-- et --> que si la version d'Internet Explorer (IE) est inférieure à la version 9 (if lt IE 9). La syntaxe bizarre [if lt IE 9] n'est pas une partie de Rails ; C'est en réalité un commentaire conditionnel supporté par les navigateurs IE pour ce genre de situation. C'est une bonne chose, aussi, parce que cela signifie que nous pouvons inclure la feuille de style supplémentaire seulement pour les versions d'IE inférieures à 9, laissant les autres navigateurs tels que Firefox, Chrome ou Safari non affectés.

Après les lignes pour inclure la feuille de style Blueprint (introduite dans l'extrait 4.4), il y a une autre ligne spécifique à IE – ligne spécifique qui cette fois inclut une feuille de style si la version de IE est inférieur à 8 (if lt IE 8):

<!--[if lt IE 8]><%= stylesheet_link_tag 'blueprint/ie' %><![endif]-->

IE possède un grand nombre de comportements bien à lui (spécialement avant la version 8) et Blueprint est fourni avec un fichier dédié ie.css qui fixe un certain nombre de ces problèmes.

Après la feuille de style IE vient un lien pour une feuille de style qui n'existe pas encore, custom.css, où nous coderons les styles de notre cru :

<%= stylesheet_link_tag 'custom', :media => 'screen' %>

CSS est très conciliant, et même si le fichier n'existe pas encore notre page fonctionnera quand même bien (nous créerons le fichier custom.css à la section 5.1.2.)

La prochaine section place un conteneur div autour des éléments de navigation et de contenu de notre site, qui consiste en une balise div de classe (class) container. Ce conteneur div est demandé par Blueprint (voyez le tutoriel Blueprint pour plus d'information). Viennent ensuite les éléments header et section ; le header (entête) contient le logo de l'application (que vous pourrez télécharger plus tard) et les éléments de navigation du site (nav). Enfin, on trouve un élément section contenant le contenu principal du site :

<div class="container">
  <header>
    <%= image_tag("logo.png", :alt => "Application exemple", :class => "round") %>
    <nav class="round">
      <ul>
        <li><%= link_to "Accueil", '#' %></li>
        <li><%= link_to "Aide", '#' %></li>
        <li><%= link_to "Inscription", '#' %></li>
      </ul>
    </nav>
  </header>
  <section class="round">
    <%= yield %>
  </section>
</div>

La balise div, en HTML, est une division générique ; elle sert simplement à part diviser le document en parties distinctes. Dans le vieux style HTML, les tables div sont utilisés pour presque toutes les divisions, mais HTML5 ajoute les éléments header, nav et section pour les divisions communes à beaucoup d'applications. On peut assigner des classes (class) et identifiant (id) à tous les éléments HTML, dont les divs et les nouveaux éléments HTML5 2 ; ce sont simplement des labels, et sont nécessaires pour styliser en CSS (section 5.1.2). La différence principale entre les class et id réside dans le fait que les classes peuvent être utilisés plusieurs fois alors que les identifiants, par nature, sont uniques dans la page.

Dans le header se trouve un helper Rails appelé image_tag :

<%= image_tag("logo.png", :alt => "Application Exemple", :class => "round") %>

Notez que, comme avec stylesheet_link_tag (section 4.3.4), nous passons une table d'options, dans ce cas définissant les attribuets alt et class de la balise image en utilisant les symboles :alt et :class. Pour rendre cela plus clair, regardons ce que cette balise produit en HTML :3

<img alt="Application Exemple" class="round" src="/images/logo.png" />

L'attribut alt définit ce qui sera affiché si l'image est introuvable,4 et la classe sera utilisée pour styliser le logo à la section 5.1.2.(les helpers Rails prennent souvent des tables d'options comme celle-ci, nous offrant la flexibilité d'ajouter des options HTML arbitraire sans jamais quitter Rails). Vous pouvez voir le résultat dans l'illustration 5.2 ; nous ajouterons l'image du logo à la fin de cette section.

Le deuxième élément à l'intérieur de l'header du layout, la balise nav, est une liste de liens pour la navigation, construite en utilisant une balise de liste non ordonnée ul et des balises d'items li :

<nav class="round">
  <ul>
    <li><%= link_to "Accueil", '#' %></li>
    <li><%= link_to "Aide", '#' %></li>
    <li><%= link_to "Inscription", '#' %></li>
  </ul>
</nav>

Cette liste utilise l'helper Rails link_to pour créer des liens (que vous avons créés directement avec la balise ancre a à la section 3.3.2) ; le premier argument est le texte du lien et le second est l'URL du lien. Nous définirons les URLs avec des noms de route à la section 5.2.3, mais pour le moment nous utilisons l'URL souche ’#’ utilisé couramment en design web. Une fois que Rails a exécuté ce layout et évalué le code Ruby embarqué, la liste ressemble à :

<nav class="round">
  <ul>
    <li><a href="#">Accueil</a></li>
    <li><a href="#">Aide</a></li>
    <li><a href="#">Inscription</a></li>
  </ul>
</nav>

Notre layout est maintenant achevé, et nous pouvons regarder le résultat en visitant, par exemple, la page d'accueil. En anticipant l'ajout d'utilisateurs à notre site au chapitre 8, ajoutons un lien d'inscription à la vue home.html.erb (extrait 5.2).

Extrait 5.2. Une page d'accueil avec un lien d'inscription.
app/views/pages/home.html.erb
<h1>Application Exemple</h1>

<p>
  C'est la page d'accuile pour l'application exemple du
  <a href="http://railstutorial.org/">Tutoriel Ruby on Rails</a>.
</p>

<%= link_to "S'inscrire&nbsp;!", '#', :class => "signup_button round" %>

Comme avec le précédent usage de link_to, cela crée simplement un lien souche de la forme :

<a href="#" class="signup_button round">S'inscrire !</a>

Notez encore le thème désormais récurrent de la table d'options, utilisée dans ce cas pour ajouter quelques classes CSS à une balise ancre. Vous pouvez noter que la balise a ici possède deux classes, séparées par une espace :

<a href="#" class="signup_button round">

C'est pratique pour le cas courant d'un élément avec deux styles différents.

Nous sommes maintenant prêts à tirer les fruits de notre dur labeur (illustration 5.2).5 Plutôt décevant, dites-vous ? Peut-être. Heureusement, cependant, nous avons fait du bon travail en renant nos élément HTML sensibles aux id et class, ce qui nous met en bonne position pour mettre notre site en forme grâce aux CSS.

layout_no_logo_or_custom_css
Illustration 5.2: La page Accueil (/pages/home) sans logo ou CSS personnalisé (version anglaise). (taille normale)

Avant que nous stylisions avec les CSS, remplaçons le texte alternatif du logo (balise alt) avec une image ; vous pouvez télécharger le logo de l'application exemple à l'adresse :

http://railstutorial.org/images/sample_app/logo.png

Déposez le logo dans le dossier public/images pour que Rails puisse le trouver. Le résultat apparait dans l'illustration 5.3.

layout_logo_no_css
Illustration 5.3: La page Accueil (/pages/home) avec un logo mais toujours pas de style personnalité. (taille normale)

5.1.2 CSS personnalisés

À la section 5.1.1, vous pouvez avoir noté que les éléments CSS sont sémantiques, c'est-à-dire qu'ils ont une signification en anglais qui transcende la structure de la page. Par exemple, au lieu d'avoir à écrire que le menu de navigation doit être aligné en haut à droite, nous utilisons l'élément « nav ». Cela nous donne une considérable flexibilité pour construire un layout avec CSS.

Commençons par remplir le fichier custom.css avec l'extrait 5.3 (il n'y a que quelques règles dans l'extrait 5.3. Pour avoir une idée de ce que ces règles CSS font, il est souvent utile de les commenter en utilisant les commentaires CSS, c'est-à-dire en les plaçant entre /* … */, et voir ce que ça change).

Extrait 5.3. CSS pour le conteneur, le body et les liens.
public/stylesheets/custom.css
.container {
  width: 710px;
}

body {
  background: #cff;
}

header {
  padding-top: 20px;
}

header img {
  padding: 1em;
  background: #fff;
}

section {
  margin-top: 1em;
  font-size: 120%;
  padding: 20px;
  background: #fff;
}

section h1 {
  font-size: 200%;
}

/* Liens */

a {
  color: #09c;
  text-decoration: none;
}

a:hover {
  color: #069;
  text-decoration: underline;
}

a:visited {
  color: #069;
}

Vous pouvez voir le résultat de ce code CSS dans l'illustration 5.4. Il y a beaucoup de CSS ici, mais il a une forme consistante. Chaque règle se réfère à une class, un id ou une balise HTML, ou une combinaison des trois, suivi par une liste des commandes de style. Par exemple :

body {
  background: #cff;
}

… change la couleur de fond de la balise body en « baby blue », tandis que :

header img {
  padding: 1em;
  background: #fff;
}

met une couche de padding de 1 em (grossièrement la largeur de la lettre M) atour de l'image (img) à l'intérieur de la balise header. Cette règle définit aussi la couleur de fond #fff, qui correspond au code du blanc.6

Similairement :

.container {
  width: 710px;
}

… stylise un élément de class container, dans ce cas en lui donnant une largeur de 710 pixels (correspondant à 18 colonnes Blueprint).7 Le point . dans .container indique que la règle stylise une class appelée “container” (comme nous le verrons à la section 8.2.3, le signe dièse # identifie une règle pour styliser un id CSS de la même manière qu'un point indique une class CSS).

layout_with_colors
Illustration 5.4: La page Accueil (/pages/home) avec des couleurs personnalisées. (taille normale)

Changer les couleurs est sympa, mais les liens de navigation sont toujours « pendus » au côté gauche de la page. Bougeons-les à un meilleur endroit et donnons-leur une apparence plus belle avec les règles de navigation de l'extrait 5.4. Le résultat apparait dans l'illustration 5.5 (dans certains des exemples de code du livre, incluant l'extrait 5.4, j'utilise trois points alignés verticalement pour indiquer du code omis. Quand vous reproduisez ce code à la main, prenez soin de ne pas taper ces points ; de la même manière, si vous copiez-collez ce code, assurez-vous de ne pas prendre ces points).

Extrait 5.4. Navigation CSS.
public/stylesheets/custom.css
.
.
.
/* Navigation */

nav {
  float: right;
}

nav {
  background-color: white;
  padding: 0 0.7em;
  white-space: nowrap;
}

nav ul {
  margin: 0;
  padding: 0;
}

nav ul li {
  list-style-type: none;
  display: inline-block;
  padding: 0.2em 0;
}

nav ul li a {
  padding: 0 5px;
  font-weight: bold;
}

nav ul li a:visited {
  color: #09c;
}

nav ul li a:hover {
  text-decoration: underline;
}

Ici, nav ul stylise une balise ul à l'intérieuur d'une balise nav, nav ul li stylise une balise li à l'intérieur d'une balise ul à l'intérieur d'une balise nav, et ainsi de suite.

layout_no_rounded_corners
Illustration 5.5: La page Accueil (/pages/home) avec la navigation stylisée (version anglaise). (taille normale)

En pénultième étape, nous allons rendre le lien à notre page d'inscription un peu moins évident (bien que nous n'en ayons rien à faire pour notre application exemple, sur un site réel il est naturellement assez important de rendre le lien d'inscription très proéminent — et pourquoi donc ?… NdT). L'extrait 5.5 montre le code CSS pour rendre gros, vert et cliquable le lien d'inscription (pour qu'un clic n'importe où dans la boite puisse suivre ce lien).

Extrait 5.5. CSS pour rendre le bouton d'inscription gros, vert et cliquable.
public/stylesheets/custom.css
.
.
.

/* Bouton inscription */

a.signup_button {
  margin-left: auto;
  margin-right: auto;
  display: block;
  text-align: center;
  width: 190px;
  color: #fff;
  background: #006400;
  font-size: 150%;
  font-weight: bold;
  padding: 20px;
}

Il y a beaucoup de règles ici ; comme d'habitude, « commentez » une ligne et rechargez la page si vous voulez voir à quoi elles sert. Le résultat final est un lien d'inscription difficile à rater (illustration 5.6).

signup_button
Illustration 5.6: La page Accueil (/pages/home) avec un bouton d'inscription proéminent. (taille normale)

En touche finale, nous allons utiliser les classes round que nous avons placées sur plusieurs des éléments de notre site. Bien que la dureté des coins des boites soit… terribles, il est un peu plus amical de les adoucir pour ne pas couper en tranches nos utilisateurs. Nous pouvons accomplir cela en utilisant le code CSS de l'extrait 5.6, pour obtenir le résultat de l'illustration 5.7.

Extrait 5.6. Règles CSS pour des coins arrondis.
public/stylesheets/custom.css
.
.
.
/* Coins arrondis */

.round {
  -moz-border-radius:    10px;
  -webkit-border-radius: 10px;
  border-radius:         10px;
}

Il est important de noter que cet astuce des bords arrondis fonctionne sur Firefox, Safari, Opera, et de nombreux autres navigateurs, mais que ça ne fonctionne pas sur Internet Explorer (encore lui… NdT). Il existe une façon d'obtenir des arrondis sur tous les navigateurs, mais aucune qui ne soit aussi facile que celle-ci, donc nous prenons le risque de laisser nos utilisateurs IE avec quelques petites coupures aux doigts.

layout_rounded_corners
Illustration 5.7: La page Accueil (/pages/home) avec coins arrondis. (taille normale)

5.1.3 Partiels

Bien que le layout de l'extrait 5.1 remplisse son role, il devient un peu encombré de code ; il y a plusieurs lignes d'inclusion de fichiers CSS et même plus de lignes d'header pour ce qui n'est logiquement que deux idées. Nous pouvons ranger ces sections en utilisant la facilité de Rails qui s'appelle les partiels (partials). Jetons d'abord un œil à l'aspect du layout après que les partiels auront été définis (extrait 5.7).

Extrait 5.7. Le layout du site avec des partiels pour les feuilles de styles et l'entête.
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <%= csrf_meta_tag %>
    <%= render 'layouts/stylesheets' %>
  </head>
  <body>
    <div class="container">
      <%= render 'layouts/header' %>
      <section class="round">
        <%= yield %>
      </section>
    </div>
  </body>
</html>

Dans l'extrait 5.7, nous avons remplacé les lignes de feuilles de styles par un simple appel à un helper Rails appellé render :

<%= render 'layouts/stylesheets' %>

L'effet de cette ligne est de trouver un fichier appelé app/views/layouts/_stylesheets.html.erb, d'évaluer son contenu et d'en insérer le contenu dans la vue.8 (Rappelez-vous que <%= ... %> est la syntaxe du « Ruby embarqué » nécessaire pour évaluer une expression Ruby et insérer le résultat à l'intérieur du template.) Remarquez le tiret bas au début du nom du fichier _stylesheets.html.erb ; ce tiret bas est la convention universelle pour nommer les partiels, et parmi d'autres choses rend possible d'identifier d'un coup d'œil tous les partiels dans un dossier.

Bien entendu, pour obtenir que le partiel fonctionne, nous devons remplir son contenu; dans le cas du partiel des feuilles de styles, c'est juste les quatre ligne incluse de l'extrait 5.1 ; le résultat apparait dans l'extrait 5.8. (Techniquement, le HTML shiv inclut Javascript, pas CSS. En d'autres mots, son but est de permettre à Internet Explorer de comprendre CSS avec HTML5, donc logiquement il doit rester dans le partiel des feuilles de style.)

Extrait 5.8. Un partiel pour les feuilles de styles incluses.
app/views/layouts/_stylesheets.html.erb
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<%= stylesheet_link_tag 'blueprint/screen', :media => 'screen' %>
<%= stylesheet_link_tag 'blueprint/print',  :media => 'print' %>
<!--[if lt IE 8]><%= stylesheet_link_tag 'blueprint/ie' %><![endif]-->
<%= stylesheet_link_tag 'custom', :media => 'screen' %>

Similairement, nous pouvons déplacer le matériel de l'entête dans un partiel montré dans l'extrait 5.9 et l'insérer dans le layout avec un autre appel à render :

<%= render 'layouts/header' %>
Extrait 5.9. Un partiel avec l'entête du site.
app/views/layouts/_header.html.erb
<header>
  <%= image_tag("logo.png", :alt => "Application exemple", :class => "round") %>
  <nav class="round">
    <ul>
      <li><%= link_to "Accueil", '#' %></li>
      <li><%= link_to "Aide", '#' %></li>
      <li><%= link_to "Connexion", '#' %></li>
    </ul>
  </nav>
</header>

Maintenant que nous savons faire des partiels, ajoutons au site un pied de page qui soit harmonisé avec l'entête. Dès à présent vous devez pouvoir devnier que nous l'appelerons _footer.html.erb et le déposer dans le dossier des layouts (extrait 5.10).

Extrait 5.10. Un partiel pour le pied de page du site.
app/views/layouts/_footer.html.erb
<footer>
  <nav class="round">
    <ul>
      <li><%= link_to "À Propos", '#' %></li>
      <li><%= link_to "Contact", '#' %></li>
      <li><a href="http://news.railstutorial.org/">News</a></li>
      <li><a href="http://www.railstutorial.org/">Tutoriel Rails</a></li>
    </ul>
  </nav>
</footer>

Comme pour l'entête, dans le pied de page nous utilisons link_to pour les liens internes vers les pages À Propos et Contact et initier les URLs avec ’#’ pour le moment (comme pour header, la balise footer est nouvelle en HTML5.)

Nous pouvons rendre le partial du pied de page dans le layout en suivant le même modèle que les partiels des feuilles de styles et de l'entête (extrait 5.11).

Extrait 5.11. La layout du site avec un partiel pour le pied de page.
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <%= csrf_meta_tag %>
    <%= render 'layouts/stylesheets' %>
  </head>
  <body>
    <div class="container">
      <%= render 'layouts/header' %>
      <section class="round">
        <%= yield %>
      </section>
      <%= render 'layouts/footer' %>
    </div>
  </body>
</html>

Bien sûr, le pied de page sera laid tant que nous ne lui aurons pas appliqué un peu de style (extrait 5.12). Le résultat est présenté dans l'illustration 5.8.

Extrait 5.12. Ajout de CSS pour le pied de page.
public/stylesheets/custom.css
.
.
.
footer {
  text-align: center;
  margin-top: 10px;
  width: 710px;
  margin-left: auto;
  margin-right: auto;
}

footer nav {
  float: none;
}

Remarquez ici la règle :

footer nav {
  float: none;
}

… qui écrase la règle précédente :

nav {
  float: right;
}

… de telle sorte que le pied de page est centré en bas de page plutôt que d'être poussé vers la droite comme les liens de navigation dans l'entête. Cette convention d'avoir des successions de règles, avec des règles ultérieures écrasant des règles antérieures, est ce qui explique le « en cascase » dans le « Cascade Style Sheets » (Feuilles de Styles en Cascade).

site_with_footer
Illustration 5.8: La page Accueil (/pages/home) avec un pied de page ajouté. (taille normale)

5.2 Liens pour la mise en page

Maintenant que nous avons fini notre layout du site avec une stylisation décente, il est temps de commencer à remplir les liens que nous avons amorcés avec ’#’. Bien sûr, nous pourrions coder les liens en dur comme :

<a href="/pages/about">À Propos</a>

… mais ce n'est pas la façon de faire Rails. Primo, ce serait bien si l'URL pour la page « À Propos » était /about plutôt que /pages/about ; plus encore, Rails utilise conventionnellement des routes nommées qui permet d'utiliser du code comme :

<%= link_to "À Propos", about_path %>

De cette manière le code a un sens plus clair, et il est également plus flexible puisque nous n'avons qu'à changer la définition de about_path pour que les URLs changent partout où about_path est utilisé.

La liste complète de nos liens planifiés (« routés ») est présentée dans la Table 5.1, avec leur carte vers les URLs et les routes. Nous les implémenterons toutes sauf la dernière d'ici la fin de ce chapitre (la dernière sera implémentée au chapitre 9.)

PageURLRoute nommée
Accueil/root_path
À Propos/aboutabout_path
Contact/contactcontact_path
Aide/helphelp_path
Inscription/signupsignup_path
Connexion/signinsignin_path
Table 5.1: Carte des Routes et URLs des liens du site.

5.2.1 Test d'intégration

Avant d'écrire les routes de notre application, nous allons poursuivre en appliquant notre « Développement Dirigé par les Tests » et donc écrire des tests pour ces routes. Il existe plusieurs façons de tester le routage, et nous allons profiter de cette opportunité pour introduire la notion de test d'intégration, qui nous permet de simuler un navigateur accédant à notre application et, de cette manière, de pouvoir la tester de bout en bout. Comme nous le verrons en abordant la section 8.4, tester le routage n'est qu'un début.

Nous commençons par générer un test d'intégration pour les liens du layout de l'application exemple :

$ rails generate integration_test layout_links
      invoke  rspec
      create    spec/requests/layout_links_spec.rb

Notez que le générateur ajoute automatiquement _spec.rb au nom de notre fichier test, ce qui donne donc spec/requests/layout_links_spec.rb (en RSpec, les tests d'intégration sont aussi appelés des request specs — des requêtes spec, ou tout simplement des « specs ») ; les origines de cette terminologie me reste obscures).

Notre test d'intégration utilisera la même fonction get que celle utilisée à la section 3.2 dans le spec du contrôleur Pages, avec du code comme :

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

Dans cette section, nous voulons tester des URLs comme « / » et « /about », mais nous ne pouvons pas obtenir (get) ces URLs à l'intérieur du test simple de contrôleur — les tests de contrôleur connaissent seulement les URLs définis pour ce contrôleur précis. En contraste, les test d'intégration ne sont pas limités par de telles restrictions, puisqu'ils sont désignés comme tests intégrés pour l'application complète et ainsi peuvent obtenir (get) la page qu'ils veulent.

En suivant le modèle du spec du contrôleur Pages, nous pouvons écrire un spec d'intégration pour chacune des pages de la table 5.1 que nous avons déjà créées, c'est-à-dire les pages « Accueil », « À Propos », « Contact » et « Aide ». Pour être sûr que la bonne page (c'est-à-dire la bonne vue) soit rendue dans chaque cas, nous allons tester la validité du titre en utilisant have_selector. Les définitions du test apparaissent dans l'extrait 5.13.

Extrait 5.13. Test d'intégration pour les routes.
spec/requests/layout_links_spec.rb
require 'spec_helper'

describe "LayoutLinks" do

  it "devrait trouver une page Accueil à '/'" do
    get '/'
    response.should have_selector('title', :content => "Accueil")
  end

  it "devrait trouver une page Contact at '/contact'" do
    get '/contact'
    response.should have_selector('title', :content => "Contact")
  end

  it "should have an À Propos page at '/about'" do
    get '/about'
    response.should have_selector('title', :content => "À Propos")
  end

  it "devrait trouver une page Iade à '/help'" do
    get '/help'
    response.should have_selector('title', :content => "Aide")
  end
end

Ces tests devraient pour le moment échouer — être au Rouge — (puisque les routes ne sont pas encore bien définies) ; nous les passerons au Vert à la section 5.2.2.

En passant, si vous n'avez pas de page d'aide, il serait temps d'en ajouter une (si vous avez résolu les exercices de la section 3.5 du chapitre 3, vous en avez déjà une). D'abord, ajoutez l'action help au contrôleur Pages (extrait 5.14). Ensuite, créez la vue correspondante (extrait 5.15).

Extrait 5.14. Ajout de l'action help (aide) au contrôleur Pages.
app/controllers/pages_controller.rb
class PagesController < ApplicationController
  .
  .
  .
  def help
    @title = "Aide"
  end
end
Extrait 5.15. Ajout d'une vue pour la page d'aide.
app/views/pages/help.html.erb
<h1>Aide</h1>
<p>
  Obtenez de l'aide sur le Tutoriel Ruby on Rails Tutorial sur la
  <a href="http://railstutorial.org/help">page d'aide du Tutoriel Rails</a>.
  Pour obtenir de l'aide sur cette application exemple, consultez le
  <a href="http://railstutorial.org/book">livre du Tutoriel Rails</a>.
</p>

Il y a un détail final pour gérer les changements précédents : si vous lancez Autotest, vous pourrez noter qu'il ne joue pas le test d'intégration. C'est à dessein, puisque les tests d'intégration peuvent être lents et donc entraver le cycle rouge-vert-restructuration, mais je trouve quand même préférable de dire à Autotest de lancer les tests du dossier spec/requests (extrait 5.16 ou extrait 5.17).

Extrait 5.16. Ajouts à .autotest nécessaire pour jouer les tests d'intégration avec Autotest sur OS X.
Autotest.add_hook :initialize do |autotest|
  autotest.add_mapping(/^spec\/requests\/.*_spec\.rb$/) do
    autotest.files_matching(/^spec\/requests\/.*_spec\.rb$/)
  end  
end
Extrait 5.17. Ajouts à .autotest nécessaire pour jouer les tests d'intégration avec Autotest sur Ubuntu Linux.
Autotest.add_hook :initialize do |autotest|
  autotest.add_mapping(%r%^spec/(requests)/.*rb$%) do|filename, _|
    filename
  end
end 

Ne vous souciez pas de où vient ce code ; je ne connais pas non plus l'API Autotest. Pour ce cas précis, j'ai lancé Google sur des termes de recherche tels que “rspec autotest integration” et j'ai trouvé ce code, et lorsque je l'ai introduit dans mon fichier .autotest, ça a fonctionné.

5.2.2 Routes Rails

Maintenant que nous avons des tests pour les URLs désirés, il est temps de les faire fonctionner. Comme noté à la section 3.1.2, le fichier que Rails utilise pour le routage des URL est le fichier config/routes.rb. Si vous jetez un coup d'œil au fichier par défaut des routes, vous verrez que c'est plutôt le bazar, mais c'est un bazar utile — plein d'exemples de routages à décommenter. Je vous suggère de le lire, et je vous suggère aussi de jeter un œil à l'article des Guides Rails “Rails Routing from the outside in” pour un traitement plus en profondeur des routes. Pour le moment, cependant, nous en resterons là avec les exemples de l'extrait 5.18.9

Extrait 5.18. Routes pour les pates statiques.
config/routes.rb
SampleApp::Application.routes.draw do
  match '/contact', :to => 'pages#contact'
  match '/about',   :to => 'pages#about'
  match '/help',    :to => 'pages#help'
  .
  .
  .
end

L'extrait 5.18 contient des routes personnalisées pour les pages contact, about (à propos), et help (aide) ; nous prendrons soin de la page d'accueil elle-même dans extrait 5.20 (puisque nous utiliserons des routes personnalisées dans l'extrait 5.18 exclusivement à partir de maintenant, nous avons saisi l'opportunité de supprimer les routes du contrôleur Pages (get "pages/home", etc.) vues dans l'extrait 3.17).

Si vous lisez attentivement le code de l'extrait 5.18, vous pouvez probablement imaginer ce qu'il fait ; par exemple, nous pouvons voir que :

match '/about', :to => 'pages#about'

… trouve le ’/about’ et le route vers l'action about du contrôleur Pages. Avant, c'était plus explicite : nous utilisions get ’pages/about’ pour atteindre le même endroit, mais /about est plus succint. Ce qui n'est pas évident, c'est que match ’/about’ crée aussi automatiquement des routes nommées (named routes) à utiliser dans les contrôleurs et dans les vues :

about_path => '/about'
about_url  => 'http://localhost:3000/about'

Notez que about_url correspond à l'URL entière http://localhost:3000/about (avec localhost:3000 remplacé par le nom de domaine, tel que example.com, pour un site déployé online). Comme abordé à la section 5.2, pour obtenir juste /about, vous utilisez about_path (le Tutoriel Rails utilise la forme path (chemin) pour la consistance, mais la différence importe souvent peu en pratique).

Ces routes maintenant définies, les tests pour les pages « À Propos », « Contact » et « Aide » devraient maintenant réussir (comme d'habitude, utilisez Autotest ou rspec spec/ pour vérifier). Il ne reste plus que le test pour la page d'accueil.

Pour établir la route vers la page d'accueil, nous pourrions utiliser un code tel que :

match '/', :to => 'pages#home'

Ça n'est pas nécessaire, pourtant ; Rails possède des instructions spéciales pour l'URL racine / (“slash”) localisé plus bas dans le fichier (extrait 5.19).

Extrait 5.19. La proposition à décommenter pour définir la route racine.
config/routes.rb
SampleApp::Application.routes.draw do
  .
  .
  .
  # You can have the root of your site routed with "root"
  # just remember to delete public/index.html.
  # root :to => "welcome#index"
  .
  .
  .
end

En utilisant l'extrait 5.19 comme modèle, nous arrivons à l'extrait 5.20 pour router l'URL « / » vers la page d'accueil.

Extrait 5.20. Ajout d'une care pour la route racine.
config/routes.rb
SampleApp::Application.routes.draw do
  match '/contact', :to => 'pages#contact'
  match '/about',   :to => 'pages#about'
  match '/help',    :to => 'pages#help'

  root :to => 'pages#home'
  .
  .
  .
end

Ce code dirige l'URL racine « / » vers /pages/home, et fournit aussi l'URL aux helpers comme suit :

root_path => '/'
root_url  => 'http://localhost:3000/'

Nous devrions aussi tenir compte du commentaire de l'extrait 5.19 et effacer public/index.html pour empêcher Rails de retourner la page par défaut (illustration 1.3) quand nous visitons « / ». Vous pouvez bien entendu simplement détruire le fichier, mais si vous utilisez Git pour le contrôle de version il existe une façon de dire à Git qu'une destruction a été faite en même temps qu'il le détruit, en utilisant git rm:

$ git rm public/index.html
$ git commit -am "Removed default Rails page"

Vous vous souvenez peut-être, à la section 1.3.5, que nous avons utilisé la commande Git git commit -a -m "Message", avec des drapeaux pour “tous les changements” (-a) et un message (-m). Comme montré ci-dessus, Git nous laisse aussi enrouler ces deux drapeaux dans un seul en utilisant git commit -am "Message".

Avec ça, toutes les routes pour les pages statiques fonctionnent, et les tests devraient réussir. Maintenant nous avons juste à renseigner les liens du layout.

5.2.3 Routes nommées

Posons les routes nommées créées à la section 5.2.2 pour qu'elles fonctionnent dans notre layout. Cela va consister à renseigner le second argument des fonctions link_to avec le nom propre à la route. Par exemple, nous convertirons :

<%= link_to "À Propos", '#' %>

… en :

<%= link_to "À Propos", about_path %>

… et ainsi de suite.

Nous allons commencer dans le partiel de l'entête, _header.html.erb (extrait 5.21), qui contient des liens vers les pages d'accueil et d'aide. Pendant que nous y serons, nous suivrons une convention web courante et lierons le logo également à la page d'accueil.

Extrait 5.21. Partiel de l'entête avec des liens.
app/views/layouts/_header.html.erb
<header>
  <% logo = image_tag("logo.png", :alt => "Application exemple", :class => "round") %>
  <%= link_to logo, root_path %>
  <nav class="round">
    <ul>
      <li><%= link_to "Accueil", root_path %></li>
      <li><%= link_to "Aide", help_path %></li>
      <li><%= link_to "S'identifier", '#' %></li>
    </ul>
  </nav>
</header>

Nous n'aurons pas de route nommée pour le lien “Inscription” jusqu'au chapitre 9, donc nous le laisserons à ’#’ pour le moment. Notez que ce code définit la variable locale logo pour la balise de l'image logo, et ensuite la lie à la ligne suivante :

  <% logo = image_tag("logo.png", :alt => "Application exemple", :class => "round") %>
  <%= link_to logo, root_path %>

C'est un peu plus propre que de tout rassembler dans une seule ligne. C'est spécialement important de noter que le ERb pour l'assignement de variable ne contient pas de signe égal (=) ; c'est seulement <% ... %>, parce que nous ne voulons pas que cette ligne soit insérée dans le template (utiliser une variable locale de cette manière est une façon de faire mais ça n'est pas la seule. Une façon encore plus propre consiste à définir un helper logo ; voyez la section 5.5.)

L'autre endroit où on peut trouver des liens, c'est le partiel du pied de page, _footer.html.erb, qui contient des liens pour les pages « À Propos » et « Contact » (extrait 5.22).

Extrait 5.22. Partiel du pied de page avec des liens.
app/views/layouts/_footer.html.erb
<footer>
  <nav class="round">
    <ul>
      <li><%= link_to "À Propos", about_path %></li>
      <li><%= link_to "Contact", contact_path %></li>
      <li><a href="http://news.railstutorial.org/">News</a></li>
      <li><a href="http://www.railstutorial.org/">Rails Tutorial</a></li>
    </ul>
  </nav>
</footer>

Avec ça, notre layout a des liens vers toutes les pages statiques créées dans le chapitre 3, et donc, par exemple, « /about » se rend à la page « À Propos » (illustration 5.9).

En passant, il est important de noter que, bien que nous n'ayons pas testé, en fait, la présence des liens dans le layout, nos tests échoueront si les routes ne sont pas définies. Vous pouvez le vérifier en commentant les routes de l'extrait 5.18 et en jouant votre suite de tests. Pour une méthode de testing qui s'assure en fait que le lien conduise au bon endroit, voyez la section 5.5.

about_page
Illustration 5.9: La page À Propos à /about(taille normale)

5.3 Inscription de l'utilisateur : une première étape

Pierre angulaire de notre travail sur le layout et le routage, nous allons faire dans cette section une route pour la page d'inscription, ce qui signifiera de créer un second contrôleur. C'est un premier pas important vers la possibilité pour les utilisateurs de s'enregistrer sur notre site ; nous aborderons l'étape suivante, la modélisation des utilisateurs, au chapitre 6, et nous achèverons le travail au chapitre 8.

5.3.1 Contrôleur des utilisateurs (Users )

Cela fait un moment que nous avons créé notre premier contrôleur, le contrôleur Pages, à la section 3.1.2. Il est temps d'en créer un second, le contrôleur Users (Utilisateurs ). Comme précédemment, nous utiliserons la commande generate pour faire le contrôleur le plus simple pour remplir nos besoins actuels, c'est-à-dire un contrôleur avec la souche d'une page d'inscription pour les nouveaux utilisateurs. En suivant les conventions de l'architecture REST préconisée par Rails, nous appellerons l'action new du contrôleur et la passerons comme argument à generate controller pour la créer automatiquement (extrait 5.23).

Extrait 5.23. Générer un contrôleur Users (avec une action new).
$ rails generate controller Users new
      create  app/controllers/users_controller.rb
       route  get "users/new"
      invoke  erb
      create    app/views/users
      create    app/views/users/new.html.erb
      invoke  rspec
      create    spec/controllers/users_controller_spec.rb
      create    spec/views/users
      create    spec/views/users/new.html.erb_spec.rb
      invoke  helper
      create    app/helpers/users_helper.rb
      invoke    rspec
      create    spec/helpers/users_helper_spec.rb

Comme avec le contrôleur Pages, cela génère vue et specs helper dont nous n'avons pas besoin, donc détruisons-les :

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

Le générateur de contrôleur construit deux choses : un contrôleur Users et un test par défaut, qui vérifie que l'action new réponde bien à la requête GET (Box 3.1) ; le code est présenté dans l'extrait 5.24. Il devrait vous sembler familier ; il suit exactement la même forme que le spec du contrôleur Pages vue précédemment à la section 3.3.1 (extrait 3.20).

Extrait 5.24. Tester la page d'inscription.
spec/controllers/users_controller_spec.rb
require 'spec_helper'

describe UsersController do

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

Par construction, le contrôleur Users possède déjà sa propre action new et le template new.html.erb pour faire réussir ce test (extrait 5.25) (pour voir la page, à l'adresse /users/new, vous devez peut-être relancer le serveur).

Extrait 5.25. Action pour la page d'inscription du nouvel utilisateur.
app/controllers/users_controller.rb
class UsersController < ApplicationController

  def new
  end

end

Pour respecter l'esprit du « Développement Dirigé par les Tests », ajoutons un second test de notre cru qui échouera en testant le titre (title) qui devrait contenir la chaine "Inscription" (extrait 5.26). Assurez-vous d'ajouter render_views comme nous l'avons fait dans le spec du contrôleur Pages (extrait 3.20) ; sinon, le test échouera même après avoir ajouté le titre adéquat.

Extrait 5.26. Un test sur le titre de la page d'inscription.
spec/controllers/users_controller_spec.rb
require 'spec_helper'

describe UsersController do
  render_views

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

    it "devrait avoir le titre adéquat" do
      get 'new'
      response.should have_selector("title", :content => "Inscription")
    end
  end
end

Ce test utilise la méthode have_selector que nous avons vue précédemment (section 3.3.1) ; notez que, comme à la section 3.3.1, have_selector a besoin de la ligne render_views puisqu'il teste la vue associée à l'action et doit donc la rendre pour pouvoir la tester.

Bien sûr, c'est à dessein que ce test, pour le moment, doit échouer (Rouge). Pour avoir un titre personnalisé, nous avons besoin de créer une variable d'instance @titre comme à la section 3.3.3. Nous pouvons ainsi obtenir le Vert avec le code de l'extrait 5.27.

Extrait 5.27. Définir le titre personnalisé pour la page de nouvel utilisateur.
app/controllers/users_controller.rb
class UsersController < ApplicationController

  def new
    @titre = "Inscription"
  end
end

5.3.2 URL de l'inscription

Avec le code de la section 5.3.1, nous avons déjà une page fonctionnelle pour les nouveaux utilisateurs à l'adresse /users/new, mais vous vous souvenez que d'après la Table 5.1 nous voulons que l'URL soit plutôt /signup. Comme à la section 5.2, nous allons d'abord écrire un test (extrait 5.28).

Extrait 5.28. Simple test d'intégration pour le lien d'inscription utilisateur.
spec/requests/layout_links_spec.rb
require 'spec_helper'

describe "LayoutLinks" do
  .
  .
  .
  it "devrait avoir une page d'inscription à '/signup'" do
    get '/signup'
    response.should have_selector('title', :content => "Inscription")
  end
end

Notez que c'est le même fichier que celui utilisé pour les autres liens du layout, même si la page d'inscription est dans un contrôleur différent. Avoir la capacité d'atteindre des pages dans divers contrôleurs est l'un des nombreux avantages de l'utilisation des tests d'intégration.

La dernière étape consiste à faire une route nommée pour les inscriptions. Nous suivrons les exemples de l'extrait 5.18 et ajouterons une règle match ’/signup’ pour l'URL de l'inscription (extrait 5.29).

Extrait 5.29. Une route pour la page d'inscription.
config/routes.rb
SampleApp::Application.routes.draw do
  get "users/new"

  match '/signup',  :to => 'users#new'

  match '/contact', :to => 'pages#contact'
  match '/about',   :to => 'pages#about'
  match '/help',    :to => 'pages#help'

  root :to => 'pages#home'
  .
  .
  .
end

Notez que nous avons gardé la règle get "users/new", qui a été généré pendant la génération automatique du contrôleur à l'extrait 5.23. Pour le moment, cette règle est nécessaire pour router /users/new correctement, mais il ne suit pas proprement les conventions REST (Table 2.2), et nous l'éliminerons à la section 6.3.3.

À ce stade, le test d'inscription de l'extrait 5.28 devrait réussir. Tout ce que qui nous reste à faire est d'ajouter le propre lien au bouton sur la page d'accueil. Comme avec les autres routes, match ’/signup’ nous donne la route nommée gives signup_path, que nous allons utiliser dans l'extrait 5.30.

Extrait 5.30. Lier le bouton à la page d'inscription.
app/views/pages/home.html.erb
<h1>Application exemple</h1>

<p>
  C'est la page d'accueil pour l'application exemple du
  <a href="http://railstutorial.org/">Tutoriel Ruby on Rails</a>.
</p>

<%= link_to "S'inscrire !", signup_path, :class => "signup_button round" %>

Cela fait, nous en avons fini avec les liens et les routes nommées, au moins jusqu'à ce que nous ajoutions la route pour s'identifier (se connecter) (chapitre 9). La page d'inscription d'un nouvel utilisateur résultant de ce travail (à l'URL /signup) est présentée dans l'illustration 5.10.

blank_signup_page
Illustration 5.10: La nouvelle page d'inscription à l'adresse /signup (version anglaise). (taille normale)

5.4 Conclusion

Dans ce chapitre, nous avons arrangé la mise en forme de notre application et avons peaufiné le routage. La suite du livre se consacrera à donner du contenu à cette Application Exemple : d'abord, en ajoutant des utilisateurs qui peuvent s'inscrire, s'identifier et se déconnecter ; ensuite, en ajoutant des micro-messages d'utilisateur ; et, enfin, en ajoutant des relations de suivi entre ces utilisateurs.

Si vous vous servez de Git pour le contrôle de vos versions, n'oubliez pas de « commettre » votre dépôt (commit) et de fusionner votre branche actuelle avec la branche maitresse (et, juste par paranoïa, jouez vos tests avant) :

$ rspec spec/
$ git add .
$ git commit -m "Fin du layout et des routages"
$ git checkout master
$ git merge filling-in-layout

Vous pouvez aussi vouloir « pusher » votre travail sur GitHub, ou déployer votre application actualisée sur Heroku :

$ git push
$ git push heroku

5.5 Exercises

  1. Remplacez la variable locale logo de l'extrait 5.21 par une méthode helper du même nom, pour que le nouveau partiel ressemble à l'extrait 5.31. Utilisez le code de l'extrait 5.32 pour vous aider à démarrer.
  2. Vous pouvez avoir noté que nos tests pour les liens du layout testent le routage mais ne vérifient pas en fait que ces routages conduisent effectivement sur les bonnes pages. Une façon d'implémenter ces tests est d'utiliser visit et click_link (cliquer le lien) à l'intérieur du test d'intégration RSpec. Remplissez le code de l'extrait 5.33 pour vérifier que tous les liens du layout sont proprement définis.
Extrait 5.31. Partiel d'entête avec un helper logo de l'extrait 5.32.
app/views/layouts/_header.html.erb
<header>
  <%= link_to logo, root_path %>
  <nav class="round">
    <ul>
      <li><%= link_to "Accueil", root_path %></li>
      <li><%= link_to "Aide", help_path %></li>
      <li><%= link_to "Connexion", '#' %></li>
    </ul>
  </nav>
</header>
Extrait 5.32. Un modèle pour l'helper logo.
app/helpers/application_helper.rb
module ApplicationAideer

  def logo
    # Fill in.
  end

  # Retourne un titre basé sur la page.
  def titre
    .
    .
    .
  end
end
Extrait 5.33. Un test pour les liens sur le layout.
spec/requests/layout_links_spec.rb
require 'spec_helper'

describe "LayoutLinks" do
  .
  .
  .
  it "devrait avoir le bon lien sur le layout" do
    visit root_path
    click_link "À Propos"
    response.should have_selector('title', :content => "À Propos")
    click_link "Aide"
    response.should # fill in
    click_link "Contact"
    response.should # fill in
    click_link "Accueil"
    response.should # fill in
    click_link "S'inscrire !"
    response.should # fill in
  end
end
  1. Les maquettes du Tutoriel Ruby on Rails sont réalisées avec une excellente application appelée Mockingbird
  2. Ils sont tout à fait sans lien avec les classes Ruby. 
  3. Vous pouvez noter que la balise img plutôt que de ressembler à <img>...</img>, se code <img ... />. Les balises qui suivent cette forme sont appelées balises auto-fermentantes (self-closing). 
  4. Le texte alt est aussi ce qui sera affiché par les lecteurs ayant une déficience visuelle. Bien que les gens soient souvent négligeants sur l'inclusion de l'attribut alt pour les images, c'est en fait exigé par les standards HTML. Heureusement, Rails inclut un attribut alt par défaut ; si vous ne spécifiez pas la valeur de l'attribut dans l'appel pour image_tag, Rails utilise simplement le nom du fichier (sans l'extension). Dans ce cas, cependant, Application exemple est plus descriptif que logo, donc j'ai décidé de définir explicitement la valeur de alt
  5. Notez que les utilisateurs Safari et Chrome verront un indicateur d'image brisée à la place du texte alternatif “Application Exemple”. 
  6. Les couleurs HTML peuvent être codées avec trois nombres en base 16 (hexadécimaux), chacun respectivement pour une des couleurs primaires rouge, vert et bleu. #fff mixe les trois couleurs, rendant du pur blanc. Consultez ce Site des couleurs HTML pour plus d'information. 
  7. Blueprint CSS utilise une grille de colonnes sur 40 pixels, 30 pixels pour la colonne elle-même et 10 pixels pour le padding. La colonne la plus à droite n'a pas besoin de padding, donc 18 colonnes représentent 710 pixels : 18 * 40 – 10 = 710. 
  8. De nombreux développeurs Rails utilisent un dossier partagé (shared) pour des partiels partagés par différentes vues. Je préfère utiliser le dossier shared pour des partiels utilitaires qui sont utiles sur de multiples vues, tandis que je mets les partiels qui sont littéralement sur chaque page (comme une part du layout du site) dans le dossier layouts (nous créerons le dossier shared en abordant le chapitre 8). Ça me semble être une division logique, mais les mettre tous dans le dossier shared fonctionne certainement bien, également. 
  9. Dans la ligne SampleApp::Application.routes.draw do vous pouvez reconnaitre que la méthode draw comprend un bloc, une construction syntaxique que nous avons vue précédemment à la section 4.3.2