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 4 Rails au goût Ruby

En s'appuyant sur les exemples du chapitre 3, ce chapitre explore quelques éléments Ruby importants pour Rails. Ruby est un vaste langage, mais heureusement le sous-ensemble nécessaire pour être productif en tant que développeur Rails est relativement limité. Plus encore, ce sous-ensemble est différent de l'approche habituelle de l'apprentissage de Ruby, ce pourquoi, si votre but est de construire des applications web dynamiques, je recommande d'apprendre Rails d'abord, en picorant quelques éléments Ruby chemin faisant. Pour devenir un expert Rails, vous avez besoin de comprendre Ruby plus profondément, et ce livre vous donne de solides fondations sur lesquelles former cette expertise. Comme indiqué à la section 1.1.1, après avoir achevé ce Tutoriel Rails je vous suggère de lire des livres de pur Ruby, tels que Beginning Ruby, The Well-Grounded Rubyist ou The Ruby Way.

Ce chapitre couvre beaucoup de points, et il est possible que vous ne les saisissiez pas du premier coup. Rassurez-vous, j'y reviendrai fréquemment au cours des prochains chapitres.

4.1 Motivation

Comme nous l'avons vu dans le dernier chapitre, il est possible de développer le squelette d'une application Rails, et même de commencer à la tester, sans aucune connaissance du langage Ruby. Nous avons fait cela en nous appuyant sur le code de contrôleur et de test généré automatiquement et en suivant les exemples trouvés. Cette situation ne peut tout de même pas durer éternellement, cependant, et nous ouvrirons ce chapitre avec un certain nombre d'ajouts au site qui vont nous permettre de nous confronter à nos limites en langage Ruby.

4.1.1 Un « helper » pour le titre

Quand nous avons vu la dernière fois notre application, nous avions juste actualisé les pages statiques classiques pour utiliser le layout Rails dans le but d'éliminer les redondances de code présentes dans nos vues (extrait 4.1).

Extrait 4.1. 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>

Ce layout fonctionne bien, mais il contient une partie qui pourrait être encore améliorée. Rappelez-vous que la ligne de titre :

Simple App du Tutoriel Ruby on Rails | <%= @titre %>

… s'appuie sur la définition de @titre dans les actions, telle que :

class PagesController < ApplicationController

  def home
    @titre = "Home"
  end
  .
  .
  .

Mais que se passe-t-il si nous ne définissons pas la variable @titre ? C'est une bonne convention graphique d'avoir toujours une base de titre sur chaque page, avec une variable optionnelle si on a besoin d'être plus précis. Nous avons presque accompli cela avec notre présent layout, à une nuance près : comme vous pouvez le voir si vous supprimez la déclaration de @titre dans l'une des actions, en l'absence de la variable @titre le titre s'affiche comme suit :

Simple App du Tutoriel Ruby on Rails | 

En d'autres termes, il y a une base de titre satisfaisante, mais il y a aussi un caractère de barre verticale « | » à la fin de cette base de titre.

Une façon courante de traiter ce cas est de définir un helper (un « assistant », un « auxiliaire ». NdT), qui est une fonction conçue pour être utilisée avec les vues. Définissons un helper titre qui retourne une base de titre, « Simple App du Tutoriel Ruby on Rails », si aucune variable @titre n'est définie, et ajoute une barre verticale dans le cas où cette variable serait définie (extrait 4.2).1

Extrait 4.2. Définir un helper de titre.
app/helpers/application_helper.rb
module ApplicationHelper

  # Retourner un titre basé sur la page.
  def titre
    base_titre = "Simple App du Tutoriel Ruby on Rails"
    if @titre.nil?
      base_titre
    else
      "#{base_titre} | #{@titre}"
    end
  end
end

Ce code peut sembler très simple aux yeux d'un développeur Rails expérimenté, mais il est en réalité plein de nouvelles idées Ruby : modules, commentaires, déclaration de variable locale, booléens, contrôle de flux, interpolation de chaine, et retour de valeur. Nous allons passer en revue chacune de ces idées au cours de ce chapitre.

Maintenant que nous avons un helper, nous pouvons l'utiliser pour simplifier notre layout en remplaçant :

<title>Simple App du Tutoriel Ruby on Rails | <%= @titre %></title>

… par :

<title><%= titre %></title>

… comme on peut le voir dans l'extrait 4.3. Notez en particulier le basculement de la variable d'instance @titre vers la méthode de l'helper titre (sans le signe arobase, @). En utilisant Autotest ou rspec spec/, vous devriez pouvoir vérifier que les tests du chapitre 3 réussissent toujours.

Extrait 4.3. Le layout du site de l'application exemple.
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= titre %></title>
    <%= csrf_meta_tag %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

4.1.2 Feuilles de styles en cascade (CCS — Cascading Style Sheets)

Il y a une seconde addition à notre site qui peut sembler simple mais introduit de nouveaux concepts Ruby : l'inclusion de feuilles de styles dans le layout de notre site. Bien que ce soit un livre sur le développement web, pas sur le design web, nous utiliserons les feuilles de styles en cascade (CSS) pour donner un minimum de style à notre application exemple, et nous utiliserons pour cela le framework Blueprint CSS.

Pour commencer, téléchargez le dernier Blueprint CSS (pour la simplicité, je présuppose que vous téléchargez Blueprint dans un dossier Downloads, mais utilisez le dossier que vous voulez). En utilisant soit le mode en ligne de commande soit un outil graphique, copiez le dossier de Blueprint CSS blueprint dans le dossier public/stylesheets de votre application exemple, un dossier spécial où Rails conserve les feuilles de styles. Sur mon Mac, la commande ressemble à celle-ci, mais dans le détail elle peut varier pour vous :

$ cp -r ~/Downloads/joshuaclayton-blueprint-css-<version number>/blueprint \
> public/stylesheets/

Ici cp est la commande Unix pour copier, et le drapeau -r copie récursivement (nécessaire pour copier tous les sous-dossiers) (comme mentionné brièvement à la section 3.2.1.1, le tilde ~ signifie «dossier home» en Unix). Note : vous ne devriez pas coller le signe « > » dans votre terminal. Si vous collez la première ligne avec ce signe et pressez la touche retour-chariot, vous verrez « > » indiquant une continuation de ligne. Vous devrez alors coller dans la seconde ligne et presser la touche Retour-chariot une nouvelle fois pour exécuter la commande. Notez aussi que vous aurez à renseigner le numéro de version à la main, puisqu'il change à chaque actualisation de Blueprint. Enfin, assurez-vous de ne pas taper :

$ cp -r ~/Downloads/joshuaclayton-blueprint-css-<version number>/blueprint/ \
> public/stylesheets/

… qui a une balance (« / ») à la fin …/blueprint/. Cela déposerait le contenu du dossier Blueprint dans le dossier public/stylesheets au lieu de déplacer le dossier blueprint lui-même.

Une fois que les feuilles de styles se trouvent dans le bon dossier, Rails fournit un helper pour les inclure dans nos pages en utilisant du Ruby embarqué (extrait 4.4).

Extrait 4.4. Ajout de feuilles de styles au layout de l'application exemple.
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= titre %></title>
    <%= csrf_meta_tag %>
    <%= stylesheet_link_tag 'blueprint/screen', :media => 'screen' %>
    <%= stylesheet_link_tag 'blueprint/print',  :media => 'print' %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

Concentrons-nous sur les nouvelles lignes :

<%= stylesheet_link_tag 'blueprint/screen', :media => 'screen' %>
<%= stylesheet_link_tag 'blueprint/print',  :media => 'print' %>

Elles utilisent l'helper Rails intégré stylesheet_link_tag, détaillé dans l'API Rails.2 La première ligne stylesheet_link_tag inclut la feuille de style blueprint/screen.css pour l'écran (par exemple le moniteur de votre ordinateur), et la seconde inclut blueprint/print.css pour l'impression (l'helper ajoute automatiquement l'extension .css au nom de fichier s'il est absent, donc je l'ai omis pour la brièveté). Comme pour l'helper de titre, pour un développeur Rails expérimenté ces lignes semblent vraiment simples, mais elles contiennent au moins quatre nouvelles idées : les méthodes Rails intégrées, l'invocation de méthode avec l'omission des parenthèses, les symboles et les tableaux hash. Ce chapitre couvre aussi ces nouvelles idées (nous verrons le code HTML produit par ces feuilles de styles dans l'extrait 4.6 de la section 4.3.4).

En passant, notons que les nouvelles feuilles de styles ne changent pas l'aspect de notre site, c'est seulement un bon début (illustration 4.1). Nous travaillerons sur cette fondation au chapitre 5.3

home_with_stylesheet
Illustration 4.1: La page d'accueil avec les nouvelles feuilles de styles Blueprint. (taille normale)

4.2 Chaines de caractères et méthodes

Notre outil principal pour apprendre Ruby sera la console Rails , qui est un outil en ligne de commande pour intéragir avec les applications Rails. La console elle-même est construite au sommet de Ruby (irb), elle a donc accès à toute la puissance du langage (comme nous le verrons à la section 4.4.4, la console a aussi accès à l'environnement Rails). Démarrez la console en ligne de commande comme suit :4

$ rails console
Loading development environment (Rails 3.0.7)
>> 

Par défaut, la console commence en environnement de développement, qui est l'un des trois environnements séparés définis par Rails (les deux autres environnements sont l'environnement de test et l'environnement de production). Cette distinction ne sera pas importante dans ce chapitre ; nous en apprendrons plus sur les environemments à la section 6.3.1.

La console est un puissant outil d'apprentissage, et vous devrez toujours vous sentir libre de l'explorer — ne vous inquiétez pas, vous ne casserez (probablement) rien. En utilisant la console, tapez Ctrl-C si vous vous retrouvez coincé, ou Ctrl-D pour quitter cette console.

Tout au long de ce chapitre, vous pourrez trouver utile de consulter l'API Ruby.5 Cet API est emballée (peut-être même trop emballée) avec des informations ; par exemple, pour en apprendre plus sur les chaines de caractères Ruby vous pouvez consulter l'entrée de l'API Ruby pour la classe String.

4.2.1 Commentaires

Les commentaires Ruby commencent par le signe dièse # et s'étendent jusqu'à la fin de la ligne. Ruby (et donc Rails) ignore les commentaires, mais ils sont utiles pour les lecteurs humains (à commencer, souvent, par l'auteur du code lui-même !). Dans le code :

  # Retourne un titre propre à la page.
  def titre
  .
  .
  .

… la première ligne est un commentaire indiquant le rôle joué par la fonction qui le suit.

D'ordinaire, vous n'incluez pas de commentaires dans les sessions de console, mais dans le but de cet apprentissage, j'en incluerai dans ce qui suit, comme cela :

$ rails console
>> 17 + 42   # Addition d'entiers
=> 59

Si vous poursuivez cette section en tapant ou en copiant-collant les commandes dans votre console, vous pouvez bien sûr omettre ces commentaires ; la console les ignorera de toute façon.

4.2.2 Chaines de caractères

la chaine de caractères (Strings) est probablement la structure de données la plus importante des applications web, puisque les pages web consistent en fin de compte en des chaines de texte envoyées par le serveur aux navigateurs. Commençons à explorer ces chaines de caractères avec la console, cette fois en commençant avec rails c (si vous avez déjà quitté votre console. NdT), qui est un raccourci pour la commande rails console :

$ rails c
>> ""         # Une chaine vide
=> ""
>> "foo"      # Une chaine non vide
=> "foo"

Voici des chaines littérales, créées en utilisant les guillemets doubles « " ». La console écrit le résultat de l'évaluation de chaque ligne, qui dans le cas d'une chaine littérale est juste la chaine elle-même.

Nous pouvons aussi concaténer des chaines avec l'opérateur + :

>> "foo" + "bar"    # Concaténation de chaines
=> "foobar"

Ici, l'évaluation de "foo" plus "bar" donne la chaine "foobar".6

Une autre façon de contruire des chaines de caractère se fait via l'interpolation en utilisant la syntaxe spéciale #{} :7

>> first_name = "Michael"    # Déclaration de variable
=> "Michael"
>> "#{first_name} Hartl"     # Interpolation de chaine
=> "Michael Hartl"

Ici, nous avons assigné la valeur "Michael" à la variable first_name et l'avons interpolée à l'intérieur de la chaine "#{first_name} Hartl". Nous pouvons aussi assigner les deux chaines à une variable :

>> first_name = "Michael"
=> "Michael"
>> last_name = "Hartl"
=> "Hartl"
>> first_name + " " + last_name    # Concaténation, avec une espace entre les deux
=> "Michael Hartl"
>> "#{first_name} #{last_name}"    # L'interpolation équivalente
=> "Michael Hartl"

Notez que les deux expressions finales sont équivalentes, mais je préfère la version interpolée ; avoir à ajouter une simple espace " " semble un peu maladroit (« espace » est féminine en typographie. NdT).

Impression

Pour imprimer une chaine de caractères, la fonction Ruby utilisée le plus couramment est puts (prononcez « poute esse », pour «put string») :

>> puts "foo"     # put string
foo
=> nil

La méthode puts entraine un effet secondaire : l'expression puts "foo" affiche la chaine à l'écran et ne retourne ensuite litéralement rien du tout : nil (nul), qui est une valeur Ruby spéciale pour « rien du tout » (par la suite, je supprimerai parfois la partie « => nil » pour la simplicité).

L'utilisation de puts ajoute automatiquement un caractère de nouvelle ligne \n à la sortie ; la méthode print liée ne le fait pas :

>> print "foo"    # imprime la chaine (~˜= puts, mais sans nouvelle ligne)
foo=> nil
>> print "foo\n"  # = puts "foo"
foo
=> nil

Chaines de caractères « apostrophées »

Tous les exemples précédents utilisaient les chaines entre guillemets, mais Ruby supporte aussi les chaines entre apostrophes. Pour de nombreux usages, les deux types de chaines sont en fait identiques :

>> 'foo'          # Une chaine apostrophe simple
=> "foo"
>> 'foo' + 'bar'
=> "foobar"

Il existe cependant une différence de taille ; Ruby ne procède à aucune interpolation à l'intérieur d'une chaine apostrophe simple :

>> '#{foo} bar'     # Les chaines apostrophe empêchent l'interpolation
=> "\#{foo} bar"

Notez comment la console retourne des valeurs en utilisant des chaines entre guillemets (tel que « # » ci-dessus), ce qui demande d'utiliser le caractère spécial d'échappement (backslash, « \ »).

Si les chaines entre guillemets peuvent faire tout ce que font les chaines entre apostrophes, et peuvent interpoler, quelle est l'utilité des chaines apostrophiées ? Elles sont souvent utiles parce qu'elles sont vraiment littérales, et contiennent réellement les caractères que vous tapez. Par exemple, le caractère d'échappement « backslash » est un caractère spécial sur la plupart des systèmes, comme dans le retour à la ligne \n. Si vous voulez qu'une variable contienne un caractère d'échappement littéral, les apostrophes seront plus pratiques :

>> '\n'       # Une combinaison à échappement littéral
=> "\\n"

Comme avec le caractère « # » de notre exemple précédent, Ruby a besoin d'échapper le caractère d'échappement lui-même ; à l'intérieur d'une chaine entre guillemets, un échappement littéral doit être représenté par deux échappements. Pour un court exemple comme celui-là, il n'y a pas un gain énorme, mais s'il y a beaucoup de choses à échapper ça peut être d'une grande utilité :

>> 'Les nouvelles lignes (\n) et les tabulations (\t) utilisent toutes
   deux les échappements \.'
=> "Les nouvelles lignes (\\n) et les tabulations (\\t) utilisent toutes
deux les échappements \\."

4.2.3 Objets et passage de message

Tout, en Ruby, les chaines et même la valeur nil (nul), sont des objets. Nous verrons le sens technique de cela à la section 4.4.2, et je pense que personne n'a jamais compris ce qu'était un objet en en lisant la définition dans un livre ; vous devez vous faire une idée intuitive des objets en consultant un grand nombre d'exemples.

Il est beaucoup plus facile de décrire ce que les objets font, qui est de répondre aux messages. Un objet comme une chaine de caractères, par exemple, peut répondre au message length (longueur), qui retourne le nombre de signes/caractères de la chaine :

>> "foobar".length        # Passer le message "length" à la chaine
=> 6

Typiquement, les messages qui peuvent être passés aux objets sont appelés des méthodes, qui sont des fonctions définies pour ces objets.8 Les chaines répondent aussi à la méthode empty? (vide ?) :

>> "foobar".empty?
=> false        # = Faux
>> "".empty?
=> true         # = Vrai

Notez le point d'interrogation à la fin de la méthode empty?. C'est une convention Ruby indiquant que la valeur de retour est booléenne : true (vrai) ou false (faux). Les booléens sont particulièrement utiles pour le contrôle de flux :

>> s = "foobar"
>> if s.empty?
>>   "La chaine est vide"
>> else
>>   "La chaine n'est pas vide"
>> end
=> "La chaine n'est pas vide"

Les booléens peuvent aussi être combinés en utilisant les opérateurs && (« et »), || (« ou ») et ! (« pas ») :

>> x = "foo"
=> "foo"
>> y = ""
=> ""
>> puts "Les deux chaines sont vides" if x.empty? && y.empty?
=> nil
>> puts "L'une des chaines est vide" if x.empty? || y.empty?
"L'une des chaines est vide"
=> nil
>> puts "x n'est pas vide" if !x.empty?
"x n'est pas vide"

Puisque tout en Ruby est objet, il en découle que nil (la valeur nul) est un objet, donc elle peut répondre aussi aux méthodes. On en trouve un exemple avec la méthode to_s qui peut dans l'absolu convertir tout objet en chaine de caractères :

>> nil.to_s
=> ""

Cela apparait certainement comme une chaine vide, comme nous pouvons le vérifier en chainant les messages que nous passons à nil:

>> nil.empty?
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.empty?
(
Traduction du message d'erreur :
ErreurDAbsenceDeMethode: Vous avez un objet null inattendu !
Vous attendiez peut-être une instance de tableau (Array)
L'erreur est survenue en évaluant l'expression nil.empty?
)
>> nil.to_s.empty?      # Chainage du message
=> true

Nous voyons ici que l'objet nil ne répond pas de lui-même à la méthode empty?, mais nil.to_s le fait.

Il existe une méthode spéciale pour tester la nullité, que vous devriez être en mesure de deviner :

>> "foo".nil?
=> false
>> "".nil?
=> false
>> nil.nil?
=> true

Si vous retournez à l'extrait 4.2, vous verrez que l'helper de titre (titre) teste pour voir si la variable @titre est nil en utilisant la méthode nil?. C'est le signe qu'il y a quelque chose de spécial à propos des variables d'instance (les variables commençant par le signe « @ »), qui peut être mieux compris en les comparant aux variables ordinaires. Par exemple, supposons que nous entrions les deux variables titre et @titre à la console sans les définir avant :

>> titre        # Houps ! Nous n'avons pas défini la variable titre.
NameError: undefined local variable or method `titre' 
>> @titre       # Une variable d'instance dans la console
=> nil
>> puts "Il n'y a pas de telle variable d'instance." if @titre.nil?
Il n'y a pas de telle variable d'instance.
=> nil
>> "#{@titre}"  # Interpolation de @titre quand elle est nulle
=> ""

Vous pouvez voir dans cet exemple que Ruby se plaint si nous essayons d'évaluer une variable locale, mais ne renvoie pas cette plainte pour une variable d'instance qui n'existe pas ; au lieu de ça, les variables d'instance prennent la valeur nil si elles ne sont pas définies. Cela explique pourquoi le code :

Simple App du Tutoriel Ruby on Rails | <%= @titre %>

… devient :

Simple App du Tutoriel Ruby on Rails | 

… quand @titre est nil : Le Ruby embarqué insère la chaine correspondant à la variable donnée, et la chaine correspondant à la valeur nil est une chaine vide "".

Le dernier exemple montre aussi un usage alternatif du mot-clé if : Ruby vous permet d'écrire des blocs qui ne sont évalués que si l'expression suivant le if est vraie. Il existe un mot-clé complémentaire, unless (sauf), qui fonctionne de la même façon, mais à l'opposé :

>> string = "foobar"
>> puts "La chaine '#{string}' n'est pas vide." unless string.empty?
La chaine string 'foobar' n'est pas vide.
=> nil

Il est important de noter que l'objet nil est spécial, dans le sens où c'est le seul objet Ruby qui est faux dans un contexte booléen, à part bien sûr l'objet false lui-même :

>> if nil
>>   true
>> else
>>   false        # nil est faux
>> end
=> false

En particulier, tous les autres objets Ruby sont true, même 0 :

>> if 0
>>   true        # 0 (et toute autre valeur que nil et false) est vrai
>> else
>>   false
>> end
=> true

4.2.4 Définition des méthodes

La console nous permet de définir des méthodes de la même façon que nous le faisions avec l'action home de l'extrait 3.6 ou l'helper de titre de l'extrait 4.2 (définir des méthodes dans la console est un peu lourd, et d'ordinaire on utilise plutôt un fichier, mais c'est pratique ici pour les besoins de la démonstration). Par exemple, définissons une fonction string_message qui prend un seul argument et retourne un message en fonction de la nullité ou non de l'argument :

>> def string_message(string)
>>   if string.empty?
>>     "C'est une chaine vide !"
>>   else
>>     "La chaine n'est pas vide."
>>   end
>> end
=> nil
>> puts string_message("")
C'est une chaine vide !
>> puts string_message("foobar")
La chaine n'est pas vide.

Notez que les fonctions Ruby ont un retour implicite, ce qui signifie qu'elles retournent la valeur de la dernière expression évaluée — dans ce cas, l'un des deux messages, en fonction du fait que l'argument string de la fonction est vide ou non. Mais Ruby possède aussi une façon de définir le retour de façon explicite ; la fonction suivante est équivalente à celle ci-dessus :

>> def string_message(string)
>>   return "C'est une chaine vide !" if string.empty?
>>   return "La chaine n'est pas vide."
>> end

Le lecteur attentif pourra noter que le second return ici est en fait superfétatoire — étant la dernière expression de la fonction, la chaine "La chaine n'est pas vide." sera retournée sans égard pour le mot-clé return, mais utiliser return aux deux endroits présente une symétrie plaisante.

4.2.5 Retour à l'helper titre

Nous sommes maintenant en mesure de comprendre l'helper titre de l'extrait 4.2 :9

module ApplicationHelper

  # Retourne un titre propre à la page.               # Commentaire de documentation
  def titre                                           # Définition de la méthode
    base_titre = "Simple App du Tutoriel Ruby on Rails"  # Assignement de variable
    if @titre.nil?                                    # Test booléen pour la nullité
      base_titre                                      # Retour implicite
    else
      "#{base_titre} | #{@titre}"                     # Interpolation de chaine
    end
  end
end

Ces éléments — fonction, définition, assignement de variable, tests booléens, contrôle de flux et extrapolation de chaine — se combinent pour créer une méthode d'helper compacte à utiliser dans notre layout. L'élément final est module ApplicationHelper : le code dans les modules Ruby peut être divisé en classes Ruby. En écrivant du Ruby ordinaire, vous écrivez souvent des modules et les inclurez explicitement vous-mêmes, mais dans notre cas Rails gère l'inclusion automatiquement pour nous. Le résultat est que la méthode titre est automagiquement (sic) accessible à toutes nos vues.

4.3 Autres structures de données

Bien que les applications web produisent en dernier lieu des chaines de caractères, la fabrication de ces chaines peut requérir l'utilisation de tout autant d'autres structures de données. Dans cette section, nous allons étudier quelques structures de données Ruby importante pour la conception d'applications Rails.

4.3.1 Tableaux (Arrays) et rangs (ranges)

Un tableau (array) est juste une liste d'éléments dans un ordre particulier. Nous n'avons pas encore abordé les tableaux dans ce Tutoriel Rails, mais les comprendre offre de bonnes bases pour comprendre les tables de hachage (hashes) (section 4.3.3) et les aspects de la modélisation des données de Rails (tels que l'association has_many vue à la section 2.3.3 et couverte plus largement à la section 11.1.2).

Jusqu'ici nous avons passé beaucoup de temps à comprendre les chaines de caractères, et il existe une façon naturelle de passer des chaines aux tableaux en utilisant la méthode split (scinder) :

>>  "foo bar     baz".split     # Scinde une chaine en trois éléments
=> ["foo", "bar", "baz"]

Le résultat de cette opération est un tableau de trois chaines. Par défaut, split divise une chaine en tableau en la scindant selon les espaces blancs (whitespace), mais vous pouvez scinder tout aussi bien selon n'importe quelle autre signe ou caractère :

>> "fooxbarxbazx".split('x')
=> ["foo", "bar", "baz"]

Conformément à la convention respectée par de nombreux langages informatiques, les tableaux Ruby sont zero-offset (décalage-zéro), ce qui signifie que le premier élément dans la liste possède l'index 0, le deuxième l'index 1, et ainsi de suite :

>> a = [42, 8, 17]
=> [42, 8, 17]
>> a[0]               # Ruby utilise les crochets pour accéder aux éléments.
=> 42
>> a[1]
=> 8
>> a[2]
=> 17
>> a[-1]              # Les indices peuvent même être négatifs !
=> 17

Nous voyons ici que Ruby utilise des crochets pour accéder aux éléments du tableau. En addition à cette notation par crochets, Ruby offre des synonymes pour l'accès à certains éléments particuliers :10

>> a                  # Pour se rappeler ce qu'est 'a' 
=> [42, 8, 17]
>> a.first
=> 42
>> a.second
=> 8
>> a.last
=> 17
>> a.last == a[-1]    # Comparaison, avec ==
=> true

La dernière ligne introduit l'opérateur de comparaison d'égalité « == », que Ruby partage avec de nombreux autres langages, tout comme les opérateurs liés « != » (« différent de… »), etc. :

>> x = a.length       # Les tableaux répondent aussi à la méthode 'length'.
=> 3
>> x == 3
=> true
>> x == 1
=> false
>> x != 1
=> true
>> x >= 1
=> true
>> x < 1
=> false

En addition à length (aperçu dans la première ligne ci-dessus), les tableaux répondent à une foule d'autres méthodes :

>> a.sort
=> [8, 17, 42]
>> a.reverse
=> [17, 8, 42]
>> a.shuffle
=> [17, 42, 8]

Vous pouvez aussi faire des ajouts aux tableaux avec l'opérateur « push », << :

>> a << 7                     # Pousser 7 dans le tableau
[42, 8, 17, 7]
>> a << "foo" << "bar"        # Chainer les ajouts
[42, 8, 17, 7, "foo", "bar"]

Ce dernier exemple montre que vous pouvez chainer les ajouts ensemble, et aussi que, contrairement aux tableaux dans d'autres langages, les tableaux Ruby peuvent contenir des éléments de types différents (dans ce cas, des chaines de caractères et des entiers).

Nous avons vu précédemment que split convertissait une chaine de caractères en tableau. Nous pouvons également faire le chemin inverse avec la méthode join (joindre) :

>> a
=> [42, 8, 17, 7, "foo", "bar"]
>> a.join                       # Joindre avec rien
=> "428177foobar"
>> a.join(', ')                 # Joindre avec virgule-espace
=> "42, 8, 17, 7, foo, bar"

Très proches des tableaux arrays, on trouve les rangs (ranges), qui peuvent probablement être plus facilement compréhensibles en les convertissant en tableaux à l'aide de la méhode to_a (« to_a » pour « to array », vers un tableau) :

>> 0..9
=> 0..9
>> 0..9.to_a              # Oops, call to_a on 9
ArgumentError: bad value for range
(NdT. ErreurArgument: mauvaise valeur pour le rang)
>> (0..9).to_a            # Utiliser les parenthèses pour appeler to_a sur un rang
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Bien que 0..9 soit un rang valide, la seconde expression ci-dessus montre que nous avons besoin d'ajouter des parenthèses pour lui appliquer la méthode.

Les Rangs sont utiles pour tirer des éléments d'un tableau :

>> a = %w[foo bar baz quux]         # Utilisez %w pour faire un tableau de chaines.
=> ["foo", "bar", "baz", "quux"]
>> a[0..2]
=> ["foo", "bar", "baz"]

Les rangs fonctionnent aussi avec les caractères :

>> ('a'..'e').to_a
=> ["a", "b", "c", "d", "e"]

4.3.2 Blocs

Les tableaux (arrays) et les rangs (ranges) répondent tous deux à une foule de méthodes qui acceptent des blocs. Ces blocs sont en même temps une des fonctionnalités les plus puissantes et de celles qui entrainent le plus de confusion :

>> (1..5).each { |i| puts 2 * i }
2
4
6
8
10
=> 1..5

Ce code appelle la méthode each (chaque) sur le rang (1..5) et lui passe le bloc { |i| puts 2 * i }. La barre verticale autour du nom de la variable dans |i| est la syntaxe Ruby pour une variable de bloc, et c'est à la méthode de savoir ce qu'elle doit faire avec le bloc , dans ce cas précis, la méthode each du rang peut traiter le bloc avec une simple variable locale, que nous avons appelée i, et elle exécute simplement le bloc pour chaque valeur de ce rang (elle exécute puts 2 * i avec i = 1 — premier terme du rang —, elle exécute puts 2 * i avec i = 2, etc. jusqu'à i = 5 — dernier élément du rang —).

Les accolades sont une façon d'indiquer un bloc, mais il existe une autre façon :

>> (1..5).each do |i|
?>   puts 2 * i
>> end
2
4
6
8
10
=> 1..5

Les blocs peuvent comprendre plus d'une ligne, et en comprennent souvent plus d'une. Dans le Tutoriel Rails nous suivrons la convention courante d'utiliser les accolades seulement pour les blocs d'une courte ligne et la syntaxe do..end pour les blocs à ligne longue ou à plusieurs lignes :

>> (1..5).each do |number|
?>   puts 2 * number
>>   puts '--'
>> end
2
--
4
--
6
--
8
--
10
--
=> 1..5

Ici j'ai utilisé number au lieu de  i juste pour souligner que n'importe quel nom de variable peut convenir.

Sans avoir de substantielles connaissances en programmation, il n'y pas de raccourci pour comprendre les blocs ; vous avez juste besoin d'en rencontrer beaucoup, et à la longue vous vous y ferez.11 Heureusement, les humains sont assez bons pour faire des généralisations à partir d'exemples concrets ; en voilà d'autres, en incluant deux qui utilisent la méthode map :

>> 3.times { puts "Betelgeuse!" }   # 3.fois prend un bloc sans variable.
"Betelgeuse!"
"Betelgeuse!"
"Betelgeuse!"
=> 3
>> (1..5).map { |i| i**2 }          # La notation ** correspond à 'puissance'.
=> [1, 4, 9, 16, 25]
>> %w[a b c]                        # Rappel : fait des tableaux de chaines.
=> ["a", "b", "c"]
>> %w[a b c].map { |char| char.upcase }
=> ["A", "B", "C"]

Comme vous pouvez le voir, la méthode map retourne le résultat après avoir appliqué le bloc donné à chaque élément d'un tableau ou d'un rang.

En passant, nous sommes maintenant en mesure de comprendre la ligne de Ruby que j'avais posée à la section 1.4.4 pour générer des sous-domaines aléatoires :

('a'..'z').to_a.shuffle[0..7].join

Déconstruisons-la pas à pas :

>> ('a'..'z').to_a                     # Un tableau de l'alphabet
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
>> ('a'..'z').to_a.shuffle             # Mélangeons-le
=> ["c", "g", "l", "k", "h", "z", "s", "i", "n", "d", "y", "u", "t", "j", "q",
"b", "r", "o", "f", "e", "w", "v", "m", "a", "x", "p"]
>> ('a'..'z').to_a.shuffle[0..7]       # Tirons les huit premiers éléments.
=> ["f", "w", "i", "a", "h", "p", "c", "x"]
>> ('a'..'z').to_a.shuffle[0..7].join  # join pour faire une chaine.
=> "mznpybuj"

4.3.3 Tables de hachage et symboles

Les tables de hachage (ou tableaux dynamiques) sont essentiellement une généralisation des tableaux-arrays : vous pouvez les considérer basiquement comme des tableaux, mais qui ne sont pas limités aux indices entiers (en fait, certains langages, spécialement Perl, appellent les tables de hachage des tableaux associatifs pour cette raison). Au lieu de ça, les indices d'une table de hachage, ou keys (clés), peuvent être presque n'importe quel objet. Par exemple, nous pouvons utiliser des chaines de caractères comme clés :

>> user = {}                          # {} est une table de hachage vide.
=> {}
>> user["first_name"] = "Michael"     # Clé "first_name", valeur "Michael"
=> "Michael"
>> user["last_name"] = "Hartl"        # Clé "last_name", valeur "Hartl"
=> "Hartl"
>> user["first_name"]                 # L'accès aux éléments fonctionne comme
=> "Michael"                          # sur les tableaux.
>> user                               # Une représentation littérale de la       
                                      # table de hachage
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}

Les tables de hachages sont indiquées avec des accolades contenant des paires clé-valeur ; une paire d'accolades sans clé-valeurs — c'est-à-dire {} — est une table vide. Il est important de noter que les accolades pour les tables de hachage n'ont rien à voir avec les accolades pour les blocs (oui, ça peut être source de confusion). Bien que les tables de hachage ressemblent aux tableaux, une différence importante est que les tables ne garantissent pas, en règle générale, de garder leurs éléments dans un ordre particulier.12 Si l'ordre importe, utilisez un tableau.

Plutôt que de définir les tables de hachage item après item en utilisant les crochets, il est facile d'utiliser leur représentation littérale :

>> user = { "first_name" => "Michael", "last_name" => "Hartl" }
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}

Ici j'ai utilisé la convention habituelle de Ruby qui consiste à placer une extra espace aux deux extrémités de la table de hachage — une convention ignorée par la sortie de la console… (ne me demandez pas pourquoi les espaces sont conventionnelles ; probablement quelques uns des premiers programmeurs influents aimaient l'aspect des extra espaces, et la convention a pris).

Jusqu'ici nous avons utilisé des chaines comme clé de hachage, mais en Rails il est bien plus courant d'utiliser plutôt des symbols (symboles). Les symboles ressemblent aux chaines de caractères, mais sont préfixés par le signe « deux points » au lieu d'être entourés par des guillemets ou des apostrophes. Par exemple, :nom est un symbole. Vous pouvez penser les symboles comme des chaines sans le bagage correspondant :13

>> "nom".split('')
=> ["n", "o", "m"]
>> :nom.split('')
NoMethodError: undefined method `split' for :nom:Symbol
(Traduction :
ErreurAucuneMethode : méthode `split' indéfinie pour le symbole :nom)
>> "foobar".reverse
=> "raboof"
>> :foobar.reverse
NoMethodError: undefined method `reverse' for :foobar:Symbol
(Traduction :
ErreurAucuneMethode : méthode `reverse' indéfinie pour le symbole :foobar)

Les symboles sont un type de données spécial de Ruby partagé avec seulement quelques autres langages, donc ils peuvent sembler bizarres au début, mais Rails les utilise beaucoup, donc vous vous y habituerez vite.

En termes de symboles comme clé de table de hachage, nous pouvons définir une table personne comme suit :

>> personne = { :nom => "Michael Hartl", :email => "michael@example.com" }
=> {:nom=>"Michael Hartl", :email=>"michael@example.com"}
>> personne[:nom]               # Accès à la valeur correspondant à :nom.
=> "Michael Hartl"
>> personne[:password]          # Accès à la valeur d'une clé indéfinie.
=> nil

Nous voyons ici, dans le dernier exemple, que la valeur pour une clé indéfinie est simplement la valeur nil (nul).

Les valeurs d'une table de hachage peuvent virtuellement être n'importe quoi, même d'autres tables de hachage, comme on peut le voir dans l'extrait 4.5.

Extrait 4.5. Tables imbriquées.
>> params = {}        # Défini une table appelée 'params' ('paramètres').
=> {}
>> params[:user] = { :nom => "Michael Hartl", :email => "mhartl@example.com" }
=> {:nom=>"Michael Hartl", :email=>"mhartl@example.com"}
>> params
=> {:user=>{:nom=>"Michael Hartl", :email=>"mhartl@example.com"}}
>>  params[:user][:email]
=> "mhartl@example.com"

Ces sortes de tables de table, ou tables imbriquées, sont intensivement utilisées par Rails, comme nous le verrons au début de la section 8.2.

Comme pour les tableaux et les rangs, les tables de hachage répondent à la méthode each. Par exemple, considérons une table appelée flash avec des clés pour deux conditions, :success (succès) et :error (erreur) :

>> flash = { :success => "Ça marche !", :error => "Raté… :-(" }
=> {:success=>"Ça marche !", :error=>"Raté… :-("}
>> flash.each do |key, value|
?>   puts "La clé #{key.inspect} a la valeur #{value.inspect}"
>> end
La clé :success a la valeur "Ça marche !"
La clé :error a la valeur "Raté… :-("

Notez que, tandis que la méthode each des tableaux prend un bloc avec une seule variable, each pour les tables de hachage en prend deux, une variable pour la clé et une variable pour la valeur. Ainsi, la méthode each pour les tables de hachage fait son itération à travers la table une paire clé-valeur à la fois.

L'exemple précédent utilise la méthode utile inspect, qui retourne une chaine avec une représentation littérale de l'objet qui l'appelle :

>> puts (1..5).to_a            # Rend une liste comme chaine.
1
2
3
4
5
>> puts (1..5).to_a.inspect    # Rend une liste littérale.
[1, 2, 3, 4, 5]
>> puts :nom, :nom.inspect
name
:nom
>> puts "Ça marche !", "Ça marche !".inspect
Ça marche !
"Ça marche !"

En passant, utiliser inspect pour imprimer un objet est assez courant pour qu'il y ait un raccourci pour le faire, la fonction p :

>> p :nom             # Identique à 'puts :nom.inspect'
:nom

4.3.4 CSS Revisité

Il est temps maintenant de revisiter les lignes de l'extrait 4.4 utilisées dans le layout pour inclure les feuilles de styles en cascade :

<%= stylesheet_link_tag 'blueprint/screen', :media => 'screen' %>
<%= stylesheet_link_tag 'blueprint/print',  :media => 'print' %>

Nous sommes maintenant tout près de pouvoir les comprendre. Comme mentionné brièvement à la section 4.1.2, Rails définit une fonction spéciale pour inclure les feuilles de styles, et :

stylesheet_link_tag 'blueprint/screen', :media => 'screen'

… est un appel à cette fonction. Mais il demeure deux mystères. Primo, où sont les parenthèses ? En Ruby, elles sont optionnelles ; ces deux lignes sont équivalentes :

# Les parenthèses optionnelles à l'appel de la fonction.
stylesheet_link_tag('blueprint/screen', :media => 'screen')
stylesheet_link_tag 'blueprint/screen', :media => 'screen'

Secondo, l'argument :media ressemble à une table de hachage, mais où sont les accolades ? Quand une table de hachage est le dernier argument d'une fonction, ses accolades sont optionnelles ; ces deux lignes sont donc équivalentes :

# Accolades optionnelles sur l'argument final.
stylesheet_link_tag 'blueprint/screen', { :media => 'screen' }
stylesheet_link_tag 'blueprint/screen', :media => 'screen'

Donc, nous voyons maintenant que chacune des lignes :

<%= stylesheet_link_tag 'blueprint/screen', :media => 'screen' %>
<%= stylesheet_link_tag 'blueprint/print',  :media => 'print' %>

… appellent la fonction stylesheet_link_tag avec deux arguments : une chaine de caractères, indiquant le chemin d'accès (relatif) au fichier de la feuille de styles CSS, et une table de hachage, indiquant le type de média concerné (’screen’ pour l'écran de l'ordinateur et ’print’ pour la version imprimée). Grâce au balisage <%= %>, les résultats sont insérés dans le template par ERb, et si vous regardez le code source de la page dans votre navigateur vous pourrez voir le code nécessaire pour inclure une feuille de styles (extrait 4.6).14

Extrait 4.6. Le code HTML produit par l'inclusion d'une CSS.
<link href="/stylesheets/blueprint/screen.css" media="screen" rel="stylesheet"
type="text/css" />
<link href="/stylesheets/blueprint/print.css" media="print" rel="stylesheet"
type="text/css" />

4.4 Classes Ruby

Nous avons dit auparavant que tout dans Ruby était objet, et dans cette section nous serons amenés à définir les nôtres. Ruby, comme beaucoup de langages orienté-objet, utilise les classes pour organiser les méthodes ; ces classes sont alors instanciées pour créer des objets. Si vous êtes débutant en programmation orienté-objet (POO), cela peut ressembler à du charabia, donc étudions quelques exemples concrets.

4.4.1 Constructeurs

Nous avons vu beaucoup d'exemples d'utilisation des classes pour instancier des objets, mais nous devons encore le faire de façon explicite. Par exemple, nous avons instancié une chaine de caractère en utilisant les guillemets, qui est le constructeur littéral de chaines :

>> s = "foobar"       # Un constructeur littéral de chaine utilisant les guillemets
=> "foobar"
>> s.class
=> String

Nous voyons ici que la chaine répond à la méthode class, et retourne simplement la classe à laquelle elle appartient (« String », Chaine).

Plutôt que d'utiliser un constructeur littéral, nous pouvons utiliser le contructeur nommé équivalent, ce qui implique d'appeler la méthode new sur le nom de la classe :15

>> s = String.new("foobar")   # Un constructeur nommé pour les chaines
=> "foobar"
>> s.class
=> String
>> s == "foobar"
=> true

C'est équivalent au constructeur littéral, mais c'est plus explicite sur ce que nous faisons.

Les Tableaux (Arrays) fonctionnent de la même façon que les chaines :

>> a = Array.new([1, 3, 2])
=> [1, 3, 2]

Les tables de hachage (Hash), en revanche, sont différentes. Tandis que le constructeur de tableau Array.new prend une valeur initiale pour le tableau, Hash.new prend une valeur par défaut pour la table, qui est la valeur de la table pour une clé inexistante :

>> h = Hash.new
=> {}
>> h[:foo]            # Essai d'accès à la valeur d'une clé :foo inexistante.
=> nil
>> h = Hash.new(0)    # pour que les clés inexistantes retournent 0 plutôt que nil.
=> {}
>> h[:foo]
=> 0

4.4.2 Héritage de classe

Quand on apprend les classes, il est utile de trouver la hiérarchie de classe en utilisant la méthode superclass :

>> s = String.new("foobar")
=> "foobar"
>> s.class                        # Trouver la classe de s.
=> String
>> s.class.superclass             # Trouver la superclasse de la classe String.
=> Object
>> s.class.superclass.superclass  # Ruby 1.9 -> BasicObject
=> BasicObject 
>> s.class.superclass.superclass.superclass
=> nil

Dans le diagramme de cette héritage hiérarchique dans lillustration 4.2, nous voyons que la superclasse de String (Chaine) est la classe Object et la superclasse de la classe Object est la classe BasicObject, mais BasicObject ne possède pas de superclasse. Ce modèle est vrai pour chaque objet Ruby : remontez la hiérarchie des classes assez loin, et chaque classe Ruby héritera en fin de compte de la classe BasicObject, qui n'a pas de superclasse elle-même. C'est l'explication technique de «tout en Ruby est objet».

string_inheritance_ruby_1_9
Illustration 4.2: La hiérarchie des héritages pour la classe String.

Pour comprendre les classes un peu plus profondément, il n'y a rien de mieux que de construire les nôtres. Construison une classe Mot possédant une méthode palindrome? qui retourne true (vrai) si le mot est un palindrome (s'il s'épelle de la même façon à l'endroit et à l'envers) :

>> class Mot
>>   def palindrome?(string)
>>     string == string.reverse
>>   end
>> end
=> nil

Nous pouvons l'utiliser comme suit :

>> w = Mot.new              # Fait un nouvel objet Mot.
=> #<Mot:0x22d0b20>
>> w.palindrome?("foobar")
=> false
>> w.palindrome?("level")
=> true

Si cet exemple vous semble un peu artificiel, tant mieux ; c'est à dessein. C'est étrange de créer une nouvelle classe juste pour créer une méthode qui prend une chaine de caractère comme argument. Puisqu'un mot est une chaine de caractères, il semble naturel que notre classe Mot hérite de la classe String, comme nous le voyons dans l'extrait 4.7 (vous devriez quitter la console et la relancer pour effacer l'ancienne définition de Mot).

Extrait 4.7. Définir une classe Mot dans la console.
>> class Mot < String             # Mot hérite de String.
>>   # Renvoie true si la chaine est son propre inverse.
>>   def palindrome?
>>     self == self.reverse        # self est la chaine elle-même.
>>   end
>> end
=> nil

Ici Mot < String est la syntaxe d'héritage Ruby (discuté brièvement à la section 3.1.2), qui s'assure que, en plus de la nouvelle méthode palindrome?, les mots héritent des mêmes méthodes que les chaines de caractères :

>> s = Mot.new("level")    # Fait un nouveau Mot, initialisé à  "level".
=> "level"                  
>> s.palindrome?            # Mot possèdent la méthode palindrome?.
=> true                     
>> s.length                 # Mot héritent aussi de toutes les méthodes des chaines.
=> 5

Puisque la classe Mot hérite de la classe String, nous pouvons utiliser la console pour voir la hiérarchie des classes explicitement :

>> s.class
=> Mot
>> s.class.superclass
=> String
>> s.class.superclass.superclass
=> Object

Cette hiérarchie est présentée dans l'illustration 4.3.

word_inheritance_ruby_1_9
Illustration 4.3: La hiérarchie de l'héritage pour une classe (non-intégré) Mot de l'extrait 4.7.

Dans l'extrait 4.7, notez que vérifier que le mot est son propre inverse implique d'avoir accès au mot à l'intérieur de la classe Mot. Ruby nous permet de le faire en utilisant le mot-clé self : à l'intérieur de la classe Mot, self est l'objet lui-même, ce qui signifie que nous pouvons utiliser :

self == self.reverse

… pour vérifier que le mot est un palindrome.16

4.4.3 Modifier les classes d'origine

Bien que l'héritage soit une idée puissante, dans le cas des palindromes il peut être plus naturel d'ajouter la méthode palindrome? à la classe String elle-même, ce qui, entre autres choses, nous permettrait d'appeler palindrome? sur une chaine littérale, ce que nous ne pouvons pas encore faire :

>> "level".palindrome?
NoMethodError: undefined method `palindrome?' for "level":String
(Traduction :
ErreurAbsenceMethode : méthode `palindrome?' indéfinie pour la chaine "level"

Assez étonnamment, Ruby vous laisse le faire ; les classes Ruby peuvent être ouvertes et modifiée, permettant au commun des mortels d'y ajouter ses propres méthodes :17

>> class String
>>   # Retourne vrai si la chaine est son propre inverse.
>>   def palindrome?
>>     self == self.reverse
>>   end
>> end
=> nil
>> "kayak".palindrome?
=> true

(Je ne sais pas ce qui est le plus sympa : que Ruby nous laisse ajouter des méthodes à des classes intégrées ou que « kayak » soit un palindrome.)

Modifier les classes intégrées est une technique puissante, mais avec un grand pouvoir vient une grande responsabilité, et il est considéré comme une mauvaise chose d'ajouter des méthodes aux classes intégrées sans avoir une réelle bonne raison de le faire. Rails a réellement de bonnes raisons ; par exmple, dans les applications web, nous voulons souvent nous assurer que les variables ne soient pas vierges (blank) — par exemple le nom d'un utilisateur doit être autre chose que des espaces ou autre espace blanc — donc Rails ajoute la méthode blank? à Ruby. Puisque la console Rails inclut automatiquement les extensions Rails, nous pouvons voir un exemple ici (celui-ci ne fonctionne pas en plein irb) :

>> "".blank?
=> true
>> "      ".empty?
=> false
>> "      ".blank?
=> true
>> nil.blank?
=> true

Nous voyons qu'une chaine constituée d'espaces n'est pas vide (empty), mais elle est vierge (blank). Notez aussi que nil est vierge ; puisque nil n'est pas une chaine de caractères, c'est une indication que Rails, en réalité, ajoute la méthode booléenne blank? à la classe de base String, qui est elle-même (comme nous l'avons vu au début de cette section) un Objet. Nous verrons d'autres exemples d'addition de Rails aux classes Ruby à la section 9.3.2.

4.4.4 Une classe contrôleur

Tous ces propos sur les classes et l'héritage peut avoir ravivé quelque souvenir, puisque nous les avons déjà vu auparavant, dans le contrôleur Pages (extrait 3.24):

class PagesController < ApplicationController

  def home
    @titre = "Accueil"
  end

  def contact
    @titre = "Contact"
  end

  def about
    @titre = "À Propos"
  end
end

Vous êtes maintenant en mesure d'apprécier, au moins vaguement, ce que ce code signifie : PagesController est une classe qui hérite de la classe ApplicationController, et arrive toute équipée des méthodes home (accueil), contact, et about (à propos), chacune d'elle définissant une variable d'instance @titre (titre). Puisque chaque session de console Rails charge l'environnement local Rails, nous pouvons même créer un contrôleur explicitement et examiner sa hiérarchir de classe :18

>> controller = PagesController.new
=> #<PagesController:0x22855d0>
>> controller.class
=> PagesController
>> controller.class.superclass
=> ApplicationController
>> controller.class.superclass.superclass
=> ActionController::Base
>> controller.class.superclass.superclass.superclass
=> ActionController::Metal
>> controller.class.superclass.superclass.superclass.superclass
=> AbstractController::Base
>> controller.class.superclass.superclass.superclass.superclass.superclass
=> Object

Le diagramme de cette hiérarchie est présenté dans l'illustration 4.4.

pages_controller_inheritance_rails_3
Illustration 4.4: La hiérarchir d'héritage du contrôleur Pages.

Nous pouvons même appeler les actions du contrôleur à l'intérieur de la console, tout comme des méthodes :

>> controller.home
=> "Accueil"

Cette valeur de retour "Accueil" vient de l'assignement @titre = "Accueil" dans l'action home.

Mais attendez — les actions n'ont pas de valeurs de retour, du moins pas celles qui nous intéressent. Pour ce qui est de l'action home, comme nous l'avons vu au chapitre 3, sa fonction est de rendre une page web. Et je suis certain de ne pas me souvenir avoir jamais appelé PagesController.new quelque part. Que se passe-t-il donc ?…

Ce qui se passe, c'est que Rails est écrit en Ruby, mais que Rails n'est pas Ruby. Certaines classes Rails sont utilisées comme des objets Ruby ordinaires, mais d'autres sont conçues pour les capacités magiques de Rails. Rails est sui generis, et devrait être étudié et compris indépendamment de Ruby. C'est pourquoi, si votre intérêt principal est de programmer des applications web, je vous recommande d'étudier Rails d'abord, puis d'apprendre Ruby avant de revenir à Rails.

4.4.5 La classe utilisateur

Nous terminons notre tour de Ruby avec une classe de notre propre cru, une classe utilisateur User qui anticipe le modèle utilisateur qui sera utilisé au chapitre 6.

Jusqu'ici nous avons entré les définitions des classes à la console, mais cela devient rapidement fatigant ; nous allons plutôt créer un fichier example_user.rb dans notre dossier racine Rails et allons le remplir avec le code de l'extrait 4.8 (souvenez-vous de la section 1.1.3 : la racine de Rails est le dossier racine de votre application ; par exemple, la racine Rails de ma propre application exemple est /Users/mhartl/rails_projects/sample_app).

Extrait 4.8. Code pour un exemple d'utilisateur.
example_user.rb
class User
  attr_accessor :nom, :email

  def initialize(attributes = {})
    @nom  = attributes[:nom]
    @email = attributes[:email]
  end

  def formatted_email
    "#{@nom} <#{@email}>"
  end
end

Il y a beaucoup de choses d'un coup ici, donc reprenons ce code pas à pas. La première ligne :

  attr_accessor :nom, :email

… crée un accesseur d'attribut (attribute accessor) pour le nom de l'utilisateur et son adresse email. Cela crée les méthodes «getter» (« obteneur ») et «setter» (« assigneur ») qui nous permettent comme leur nom l'indique d'obtenir (get) et d'assigner (set) les variables d'instance @nom et @email.

La première méthode, initialize, est spéciale en Ruby : c'est la méthode appelée quand nous exécutons le code User.new. Cette méthode particulière initialize reçoit un argument, attributes (attributs) :

  def initialize(attributes = {})
    @nom  = attributes[:nom]
    @email = attributes[:email]
  end

Ici la variable attributes a une valeur par défaut égale à une table de hachage vide, donc nous pouvons définir un utilisateur sans nom et sans adresse email (en vous souvenant de la section 4.3.3 : les tables de hachage retournent nil pour une clé inexistante, donc attributes[:nom] sera nil s'il n'y a pas de clé :nom, même chose pour attributes[:email]).

Enfin, notre classe définit une méthode appelée formatted_email qui utilise les valeurs assignées aux variables @nom et @email pour construire une version bien formatée de l'adresse email en utilisant l'interpolation de chaine (section 4.2.2) :

  def formatted_email
    "#{@nom} <#{@email}>"
  end

Lançons la console, appelons (require) le code de l'exemple d'utilisateur et essayons notre classe User ainsi définie :

>> require './example_user'     # C'est la façon de charger le code de example_user.
=> ["User"]
>> example = User.new
=> #<User:0x224ceec @email=nil, @nom=nil>
>> example.nom                 # nul puisque  attributes[:nom] est nul
=> nil
>> example.nom = "Exemple d'utilisateur"           # Assigne un nom non nul
=> "Exemple d'utilisateur"
>> example.email = "user@example.com"      # et une adresse email non nulle
=> "user@example.com"
>> example.formatted_email
=> "Exemple d'utilisateur <user@example.com>"

Ici le ’.’ signifie en Unix « dossier courant », et ’./example_user’ dit à Ruby de chercher un fichier d'exemple d'utilisateur dans ce dossier courant. Le code suivant crée un exemple d'utilisateur vide et définit le nom et l'adresse mail en assignant directement les attributs respectifs (l'assignement « depuis l'extérieur » est rendu possible par la ligne attr_accessor de l'extrait 4.8). Quand nous écrivons :

example.name = "Exemple d'utilisateur"

… Ruby assigne (set) la valeur Exemple d'utilisateur à la variable @nom (de même pour l'attribut email), que nous utilisons alors pour la méthode formatted_email.

Souvenez-vous d'après la section 4.3.4 que nous pouvons omettre les accolades pour une table de hachage en argument final d'une fonction, nous pouvons donc créer une autre utilisateur avec des valeurs prédéfinies en passant une table de hachage à la méthode initialize :

>> user = User.new(:nom => "Michael Hartl", :email => "mhartl@example.com")
=> #<User:0x225167c @email="mhartl@example.com", @nom="Michael Hartl">
>> user.formatted_email
=> "Michael Hartl <mhartl@example.com>"

Nous verrons en abordant le chapitre 8 qu'initialiser des objets en utilisant un attribut de type table de hachage est courant pour les applications Rails.

4.5 Exercises

  1. En utilisant l'extrait 4.9 comme guide, combinez les méthode split (découper), shuffle (mélanger), et join (joindre) pour écrire une fonction qui mélange les lettres d'une chaine de caractère donnée.
  2. En vous appuyant sur l'extrait 4.10, ajoutez une méthode shuffle à la classe String.
  3. Créez trois tables de hachage appelées personne1, personne2 et personne3, contenant les clés :prenom pour le prénom et :nom pour le patronyme. Créez alors des paramètres de telle sorte que params[:pere] soit la personne1, params[:mere] soit la personne2 et params[:enfant] soit la personne3. Vérifiez que, par exemple, params[:pere][:prenom] ait la bonne valeur.
  4. Trouvez une version en ligne de l'API Ruby et consultez la méthode de hachage (Hash) merge (fusionner).
Extrait 4.9. Squelette d'une fonction chaine shuffle.
>> def string_shuffle(s)
>>   s.split('').?.?
>> end
=> nil
>> string_shuffle("foobar")
Extrait 4.10. Squelette d'une méthode shuffle attachée à la classe String.
>> class String
>>   def shuffle
>>     self.split('').?.?
>>   end
>> end
=> nil
>> "foobar".shuffle
  1. Si un helper est spécifique à un contrôleur particulier, vous devrez le placer dans le fichier helper correspondant ; par exemple, les helpers du contrôleur Pages vont généralement dans le fichier app/helpers/pages_helper.rb. Dans notre cas, nous comptons que l'helper titre soit utilisé sur toutes les pages du site, et Rails possède un fichier helper spécial pour ça : app/helpers/application_helper.rb
  2. Je ne fournis pas de liens vers des API car ils ont une fâcheuse tendance à se démoder assez rapidement. Google reste votre guide. En passant, « API » signifie «Application Programming Interface» (Interface de Programmation des Applications). 
  3. Si vous êtes impatient, sentez-vous libre de consulter le Tutoriel de démarrage rapide de Blueprint CSS
  4. Souvenez-vous que l'invite de la console sera quelque chose comme ruby-1.9.2-head >, mais les exemples utilisent  >> puisque les version de Ruby varieront. 
  5. Comme pour l'API Rails, les liens vers les API Ruby sont vite dépassés, bien que moins rapidement. Google reste encore une fois votre guide. 
  6. Pour en savoir plus sur l'origine de « foo » et de « bar » — et, en particulier, la possible non-relation de « foobar » à « FUBAR » — voyez l'entrée « foo » dans le fichier Jargon
  7. Les programmeurs familiers de Perl ou de PHP peuvent comparer cela à l'interpolation automatique du signe dollar des variables dans des expressions telles que "foo $bar"
  8. Mes excuses par avance pour passer sans considération du terme fonction (function) à celui de méthode (method) tout au long de chapitre ; en Ruby, ce sont les mêmes choses : toutes les méthodes sont des fonctions, et toutes les fonctions sont des méthodes, puisque tout en Ruby est objet
  9. Bien, il restera encore une chose de côté que nous ne comprenons pas, qui est : comment Rails lie toutes ces choses ensemble : diriger les URLs vers les actions, rendre l'helper titre utilisable dans les vues, etc. C'est un sujet intéressant, et je vous encourage à le creuser, mais savoir comment Rails fonctione n'est pas nécessaire pour utiliser Rails (pour une compréhension plus profonde, je recommande The Rails 3 Way par Obie Fernandez). 
  10. La méthode second utilisée ici n'est pas une partie de Ruby lui-même, mais est ajouté par Rails. Ça fonctionne dans ce cas parce la console Rails inclut automatiquement les extensions Rails à Ruby. 
  11. Les experts en programmation, d'un autre côté, peuvent tirer profit du fait de savoir que ces blocs sont des closures (ou « fermetures »), qui sont des fonctions anonymes ponctuelles avec des données attachées. 
  12. Ruby 1.9 garantit actuellement que les tables de hachage gardent leurs éléments dans le même ordre d'entrée, mais il serait imprudent de compter sur un ordre particulier dans ces tables. 
  13. Comme résultat d'avoir moins de bagages, les symboles sont plus simples à se comparer entre eux ; les chaines de caractères ont besoin d'être comparées signe à signe tandis que les symboles peuvent être comparés d'un seul coup. Ça les rend idéal pour l'utilisation en tant que clé de table de hachage. 
  14. Vous voyez peut-être des nombres amusants, comme ?1257465942, après les noms des fichiers CSS. Ils sont insérés par Rails pour s'assurer que les navigateurs rechargeront le code CSS quand il changera sur le serveur. 
  15. Ces résultats peuvent varier en fonction de la version de Ruby que vous utilisez. Cet exemple présume que vous utilisez la version  1.9.2. 
  16. Pour en savoir plus sur les classes Ruby et le mot-clé self, consultez le pots RailsTips «Class and Instance Variables in Ruby» (Classe et variables d'instance en Ruby). 
  17. Pour les familiers de JavaScript, cette fonctionnalité est comparable à l'utilisation des objets de prototype de classe intégrés pour augmenter une classe (merci au lecteur Erik Eldridge de me l'avoir suggéré). 
  18. Vous n'avez pas besoin de savoir ce que font chacune des classes de la hiérarchie. Moi-même, je ne sais pas ce qu'elles font toutes, alors que je programme en Ruby on Rails depuis 2005. Cela signifie soit (a) que je suis tout bonnement incompétent, soit (b) que vous pouvez être un développeur Rails qualifié sans en connaitre les entrailles. J'espère pour vous et moi que c'est la dernière proposition qui est la bonne.