Vous êtes sur la page 1sur 28

Tutoriel Ruby on Rails

par Michael Hartl


Accueil | News | Suivre

Rejoindre le contenu

Tutoriel Ruby on Rails


Apprendre Rails par l'exemple
Michael Hartl

Contenu
1. Chapitre 1 De zéro au déploiement
2. 1. 1.1 Introduction
2. 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
3. 1.2 Debout et au boulot
4. 1. 1.2.1 Environnements de développement
2. 1. IDEs
2. Éditeurs de texte et lignes de commande
3. Navigateurs
4. Note à propos des outils
3. 1.2.2 Ruby, RubyGems, Rails, et Git
4. 1. Installation de Rails (Windows)
2. Installer Git
3. Installer Ruby
4. Installer RubyGems
5. Installer Rails
5. 1.2.3 La première application
6. 1.2.4 Bundler
7. 1.2.5 Le serveur rails (rails server)
8. 1.2.6 Modèles-Vue-Contrôleur (MVC)
5. 1.3 Contrôle de versions avec Git
6. 1. 1.3.1 Installation et réglages
2. 1. Initialisation des réglages système
2. Initialisation des réglages du dépôt (repository)
3. 1.3.2 Ajout et mandat de dépôt
4. 1.3.3 Qu'est-ce que Git peut faire de bien pour vous ?
5. 1.3.4 GitHub
6. 1.3.5 Branch, edit, commit, merge
7. 1. Branch
2. Edit
3. Commit
4. Merge
5. Push
7. 1.4 Déploiement
8. 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
9. 1.5 Conclusion
3. Chapitre 2 Une application démo
4. 1. 2.1 Planifier l'application
2. 1. 2.1.1 Modéliser les utilisateurs
2. 2.1.2 Modéliser les micro-messages
3. 2.2 La ressource Utilisateurs (Users)
4. 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)
5. 2.3 La ressource Micro-messages (Microposts)
6. 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
7. 2.4 Conclusion
5. Chapitre 3 Pages statiques courantes
6. 1. 3.1 Pages statiques
2. 1. 3.1.1 Pages HTML statiques
2. 3.1.2 Les pages statiques avec Rails
3. 3.2 Premiers tests
4. 1. 3.2.1 Outils de test
2. 1. Auto-test
3. 3.2.2 TDD : Rouge, Vert, Refactor
4. 1. Spork
2. Rouge
3. Vert
4. Refactor
5. 3.3 Pages (un peu) dynamiques
6. 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
7. 3.4 Conclusion
8. 3.5 Exercices
7. Chapitre 4 Rails au goût Ruby
8. 1. 4.1 Motivation
2. 1. 4.1.1 Un helper pour le titre
2. 4.1.2 Feuilles de styles (CSS — Cascading Style Sheets)
3. 4.2 Chaines de caractères et méthodes
4. 1. 4.2.1 Commentaires
2. 4.2.2 Chaines de caractères
3. 1. Impression
2. Chaines de caractères « apostrophées »
4. 4.2.3 Objets et passage de message
5. 4.2.4 Définition de méthode
6. 4.2.5 Retour à l'« helper » de titre
5. 4.3 Autres structures de données
6. 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
7. 4.4 Classes Ruby
8. 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
9. 4.5 Exercices
9. Chapitre 5 Poursuivre la mise en page
10. 1. 5.1 Ajout de structure
2. 1. 5.1.1 Navigation du site
2. 5.1.2 Personnalisation CSS
3. 5.1.3 Partiels
3. 5.2 Liens pour la mise en page
4. 1. 5.2.1 Test d'intégration
2. 5.2.2 Routes Rails
3. 5.2.3 Nommer les routes
5. 5.3 Inscription de l'utilisateur : une première étape
6. 1. 5.3.1 Contrôleur Utilisateur
2. 5.3.2 URL d'inscription
7. 5.4 Conclusion
8. 5.5 Exercices
11. Chapitre 6 Modéliser et afficher les utilisateurs, partie I
12. 1. 6.1 Modèle utilisateur
2. 1. 6.1.1 Migrations de la base de données
2. 6.1.2 Le fichier modèle
3. 1. Annotation des modèles
2. Attributs accessibles
4. 6.1.3 Créer des objets Utilisateur
5. 6.1.4 Recherche dans les objets Utilisateurs
6. 6.1.5 Actualisation des objets Utilisateurs
3. 6.2 Validations utilisateur
4. 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é
5. 1. L'avertissement d'unicité
5. 6.3 Afficher les utilisateurs
6. 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
4. 1. Paramètres pour le déboggage
7. 6.4 Conclusion
8. 6.5 Exercices
13. Chapitre 7 Modéliser et afficher les utilisateurs, partie II
14. 1. 7.1 Mots de passe non sécurisés
2. 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
3. 7.2 Sécuriser les mots de passe
4. 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
5. 7.3 Meilleures vues d'utilisateurs
6. 1. 7.3.1 Tester la page de l'utilisateur (avec factories)
2. 7.3.2 Un nom et un Gravatar
3. 1. Un « helper » de Gravatar
4. 7.3.3 Une barre utilisateur latérale
7. 7.4 Conclusion
8. 1. 7.4.1 Dépôt Git
2. 7.4.2 Déploiement Heroku
9. 7.5 Exercices
15. Chapitre 8 Inscription
16. 1. 8.1 Formulaire d'inscription
2. 1. 8.1.1 Utiliser form_for
2. 8.1.2 Le formulaire HTML
3. 8.2 Échec de l'inscription
4. 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
5. 8.3 Succès de l'inscription
6. 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
7. 8.4 Test d'intégration RSpec
8. 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
9. 8.5 Conclusion
10. 8.6 Exercices
17. Chapitre 9 Connexion, déconnexion
18. 1. 9.1 Les sessions
2. 1. 9.1.1 Le contrôleur de session
2. 9.1.2 Formulaire d'identification
3. 9.2 Échec de l'identification
4. 1. 9.2.1 Examen de la soumission du formulaire
2. 9.2.2 Échec de l'identification (test et code)
5. 9.3 Succès de l'identification
6. 1. 9.3.1 L'action create finalisée
2. 9.3.2 Se souvenir de moi
3. 9.3.3 Utilisateur courant
7. 9.4 Déconnexion
8. 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
9. 9.5 Conclusion
10. 9.6 Exercices
19. Chapitre 10 Actualiser, afficher et supprimer des utilisateurs
20. 1. 10.1 Actualiser l'utilisateur
2. 1. 10.1.1 Formulaire de modification
2. 10.1.2 Permettre les modifications
3. 10.2 Protéger les pages
4. 1. 10.2.1 Utilisateurs identifiés requis
2. 10.2.2 Nécessité du bon utilisateur
3. 10.2.3 Redirection conviviale
5. 10.3 Afficher les utilisateurs
6. 1. 10.3.1 Liste des utilisateurs
2. 10.3.2 Exemples d'utilisateurs
3. 10.3.3 Pagination
4. 1. Test de la pagination
5. 10.3.4 Restructuration des partiels
7. 10.4 Supprimer des utilisateurs
8. 1. 10.4.1 Utilisateurs administrateurs
2. 1. Révision de attr_accessible
3. 10.4.2 L'action destroy (« supprimer »)
9. 10.5 Conclusion
10. 10.6 Exercices
21. Chapitre 11 Micro-messages d'utilisateurs
22. 1. 11.1 Le modèle Micropost (« Micro-message »)
2. 1. 11.1.1 Le modèle initial
2. 1. Attributs accessibles
3. 11.1.2 Associations Utilisateur/micro-messages
4. 11.1.3 Affinements du micro-message
5. 1. Portée par défaut
2. Dépendances de la suppression
6. 11.1.4 Validations du micro-message
3. 11.2 Afficher les micro-messages
4. 1. 11.2.1 Etoffement de la page de l'utilisateur
2. 11.2.2 Exemples de micro-messages
5. 11.3 Manipuler les micro-messages
6. 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
7. 11.4 Conclusion
8. 11.5 Exercices
23. Chapitre 12 Suivi des utilisateurs
24. 1. 12.1 Le modèle Relation (Relationship model)
2. 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
3. 12.2 Une interface web pour les auteurs et les lecteurs
4. 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
5. 12.3 L'état de l'alimentation
6. 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
7. 12.4 Conclusion
8. 1. 12.4.1 Extensions de l'application exemple
2. 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
3. 12.4.2 Guide vers d'autres ressources
9. 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 9 Connexion, déconnexion


Maintenant que les nouveaux utilisateurs peuvent s'inscrire sur notre site (chapitre 8), il est temps de donner à ces utilisateurs enregistrés
la possibilité de se connecter (de s'identifier) et se déconnecter. Cela nous permettra d'ajouter des personnalisations du layout basées sur
l'état de l'internaute visitant le site, utilisateur identifié ou simple visiteur. Par exemple, dans ce chapitre nous actualiserons l'entête avec
les liens d'identification et de déconnexion et le lien pour rejoindre le profil ; au chapitre 11, nous utiliserons l'identité d'un utilisateur
connecté pour créer des micro-messages associés à cet utilisateur, et au chapitre 12 nous permettrons à l'utilisateur de suivre les micro-
messages d'autres utilisateurs de l'application.

Avoir des utilisateurs connectés nous permettra aussi d'implémenter un modèle de sécurité restreignant l'accès de certaines pages à des
utilisateurs particuliers. Par exemple, comme nous le verrons au chapitre 10, seuls les visiteurs connectés seront en mesure d'accéder à la
page de modification des informations utilisateur. Le système d'identification (de connexion) rendra aussi possible certains privilèges
pour les utilisateurs administrateurs, comme la possibilité (chapitre 10) de détruire des utilisateurs de la base de données.

Comme dans les chapitres précédents, nous ferons notre travail sur une branche sujet et fusionnerons les changements à la fin :
$ git checkout -b sign-in-out

9.1 Les sessions


Une session est une connexion semi-permanente entre deux ordinateurs, tels qu'un ordinateur client jouant un navigateur web et un
serveur jouant Rails. Il existe plusieurs modèles de comportement de session sur le web : « oublier » la session à la fermeture du
navigateur, utiliser une option « se souvenir de moi » pour des sessions persistantes, et se souvenir des sessions jusqu'à ce que l'utilisateur
se déconnecte explicitement.1 Nous opterons pour la dernière de ces options : quand un utilisateur s'identifie, nous nous souviendrons de
son statut « pour toujours »,2 n'effaçant sa session que lorsqu'il se déconnectera explicitement de lui-même.

Il est pratique de modeler les sessions comme une ressource REST : nous aurons une page d'identification pour les nouvelles sessions
(new), l'identification créera une session (create), et la déconnexion la détruira (destroy). Nous aurons par conséquent besoin d'un
contrôleur Sessions avec les actions new (nouvelle), create (créer) et destroy (détruire). Contrairement au cas du contrôleur Users, qui
utilise une base de données (via le modèle User) pour les données persistantes, le contrôleur Sessions utilisera un cookie, qui est un petit
morceau de texte placé sur le navigateur de l'utilisateur. Le plus gros du travail de l'identification consiste à construire cette machinerie
d'authentification basée sur les cookies. Dans cette section et la suivante, nous allons nous préparer à ce travail en construisant un
contrôleur Sessions, un formulaire d'identification et les actions de contrôleur correspondantes (le plus gros ce travail est similaire à
l'inscription de l'utilisateur du chapitre 8). Nous terminerons alors l'identification de l'utilisateur avec le code nécessaire manipulant les
cookies à la section 9.3.

9.1.1 Contrôleur Sessions

Les éléments de l'inscription et de l'identification correspondent aux actions REST particulières du contrôleur Sessions : le formulaire
d'identification est traité par l'action new (couverte par cette section), et plus précisément l'identification est traitée en envoyant une
requête POST à l'action create (section 9.2 et section 9.3), et la déconnexion est traitée en envoyant une requête DELETE à l'action destroy
(section 9.4) (rappelez-vous de l'association des verbes HTTP avec les actions REST de la table 6.2.) Puisque nous savons que nous
avons besoin d'une action new, nous pouvons la créer en générant le contrôleur Session (comme pour le contrôleur Users de
l'extrait 5.23):3
$ rails generate controller Sessions new
$ rm -rf spec/views
$ rm -rf spec/helpers

Maintenant, comme avec le formulaire d'inscription à la section 8.1, nous créons un nouveau fichier pour le spec du contrôleur Sessions
et ajoutons une paire de tests pour l'action new et la vue correspondante (extrait 9.1) (ce modèle devrait commencer à vous être familier
maintenant).

Extrait 9.1. Tests pour l'action new et la vue de la session.


spec/controllers/sessions_controller_spec.rb

require 'spec_helper'

describe SessionsController do
render_views

describe "GET 'new'" do

it "devrait réussir" do
get :new
response.should be_success
end

it "devrait avoir le bon titre" do


get :new
response.should have_selector("titre", :content => "S'identifier")
end
end
end

Pour faire réussir ces tests, nous avons d'abord besoin d'ajouter une route pour l'action new, et tant que nous y serons, nous créerons toutes
les actions nécessaire au long de ce chapitre. Nous suivons de façon générale l'exemple de l'extrait 6.26, mais dans ce cas nous
définissons seulement les actions particulières dont nous avons besoin, c'est-à-dire new, create et destroy, et ajoutons aussi les routes
nommées pour l'identification et la déconnexion (extrait 9.2).

Extrait 9.2. Ajout d'une ressource pour obtenir les actions RESTful pour les sessions.
config/routes.rb

SampleApp::Application.routes.draw do
resources :users
resources :sessions, :only => [:new, :create, :destroy]

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


match '/signin', :to => 'sessions#new'
match '/signout', :to => 'sessions#destroy'
.
.
.
end

Comme vous pouvez le voir, les méthodes resources (ressources) peuvent prendre une table d'options, qui dans ce cas possède une clé
:only et une valeur égale à un tableau des actions à laquelle doit répondre le contrôleur Sessions. Les ressources définies dans
l'extrait 9.2 fournissent des URLs et des actions similaires à celles des utilisateurs (Table 6.2), comme montré dans la table 9.1.

Requête HTTP URL Route nommée Action But


GET /signin signin_path new page pour une nouvelle session (identification)
POST /sessions sessions_path create crée une nouvelle session
DELETE /signout signout_path destroy efface la session (déconnexion)
Table 9.1: Routes RESTful fournies par les règles de sessions de l'extrait 9.2.

Nous pouvons obtenir la réussite du second test de l'extrait 9.1 en ajoutant la variable d'instance titre adéquate à l'action new, comme dans
l'extrait 9.3 (qui définit aussi les actions create et destroy pour référence future).

Extrait 9.3. Ajout du titre pour la page d'identification.


app/controllers/sessions_controller.rb

class SessionsController < ApplicationController

def new
@titre = "S'identifier"
end

def create
end

def destroy
end
end

Avec ça, les tests de l'extrait 9.1 devraient réussir, et nous sommes prêts à construire le formulaire d'identification.

9.1.2 Formulaire d'identification


Le formulaire d'identification (ou, de façon équivalente, le formulaire de nouvelle session) est similaire en apparence au formulaire
d'inscription, à l'exception prêt que nous n'avons que deux champs (email et mot de passe) au lieu de quatre. Une maquette est présentée
dans l'illustration 9.1.

Illustration 9.1: Une maquette du formulaire d'identification. (taille normale)

Rappelez-vous, de l'extrait 8.2, que le formulaire d'inscription utilise l'helper form_for, prenant en argument la variable d'instance
utilisateur @user :
<%= form_for(@user) do |f| %>
.
.
.
<% end %>

La différence principale avec le formulaire de nouvelle session est que nous n'avons pas de modèle Session, et ainsi pas de variable
@session analogue à la variable @user. Cela signifie que, en construisant le formulaire de nouvelle session, nous devons donner à
form_for légèrement plus d'informations . En particulier, alors que :

form_for(@user)

… permet à Rails de déduire que action du formulaire devrait être le POST de l'URL /users, dans le cas des sessions nous avons besoin
d'indiquer le nom de la ressource tout comme l'URL appropriée :
form_for(:session, :url => sessions_path)

Puisque nous authentifions les utilisateurs avec les adresses mail et les mots de passe, nous avons besoin d'un champ pour chacun à
l'intérieur du formulaire ; le résultat apparait dans l'extrait 9.4.

Extrait 9.4. Code pour le formulaire d'identification.


app/views/sessions/new.html.erb

<h1>Identification</h1>

<%= form_for(:session, :url => sessions_path) do |f| %>


<div class="field">
<%= f.label :email, "eMail" %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password, "Mot de passe" %><br />
<%= f.password_field :password %>
</div>
<div class="actions">
<%= f.submit "S'identifier" %>
</div>
<% end %>

<p>Pas encore inscrit ? <%= link_to "S'inscrire !", signup_path %></p>

Avec le code de l'extrait 9.4, le formulaire d'identification apparait comme dans l'illustration 9.2.

Illustration 9.2: Le formulaire d'identification (/sessions/new). (taille normale)

Bien que nous allions bientôt perdre l'habitude de regarder le code HTML généré par Rails (et faire plutôt confiance aux helpers pour
faire leur travail), pour le moment jetons-y un coup d'œil (extrait 9.5).

Extrait 9.5. HTML pour le formulaire d'identification produit par l'extrait 9.4.
<form action="/sessions" method="post">
<div class="field">
<label for="session_email">eMail</label><br />
<input id="session_email" name="session[email]" size="30" type="text" />

</div>
<div class="field">
<label for="session_password">Mot de passe</label><br />
<input id="session_password" name="session[password]" size="30"
type="password" />
</div>
<div class="actions">
<input id="session_submit" name="commit" type="submit" value="S'identifier" />
</div>
</form>

En comparant l'extrait 9.5 avec l'extrait 8.5, vous devriez pouvoir deviner que soumettre ce formulaire produira une table params où
params[:session][:email] et params[:session][:password] correspondent au champs email et mot de passe. Traiter cette soumission
— et, en particulier, authentifier les utilisateurs en se basant sur l'email et le mot de passe soumis — est le projet des deux prochaines
sections.

9.2 Échec de l'identification


Comme dans le cas de la création d'utilisateurs (signup), la première étape dans la création de sessions (signin) est de traiter les entrées
invalides. Nous allons commencer par revoir ce qui se passe quand un formulaire est soumis, et nous arranger alors pour afficher un
message d'erreur utile dans le cas de l'échec de l'identification (comme présenté dans la maquette de l'illustration 9.3). Enfin, nous
poserons les bases d'une identification réussie (section 9.3) en évaluant chaque soumission de l'identification en se basant sur la validité
de sa combinaison email/mot de passe.

Illustration 9.3: Une maquette de l'échec de l'identification. (taille normale)

9.2.1 Examen de la soumission du formulaire


Commençons par définir une action create minimale pour le contrôleur Sessions (extrait 9.6), qui ne fait rien d'autre que rendre la vue
new. Soumettre le formulaire /sessions/new avec des champs vierges puis renvoyer le résultat vu dans l'illustration 9.4.

Extrait 9.6. Une version préliminaire de l'action create de Sessions.


app/controllers/sessions_controller.rb

class SessionsController < ApplicationController


.
.
.
def create
render 'new'
end
.
.
.
end
Illustration 9.4: L'échec de l'identification initiale, avec create comme dans l'extrait 9.6. (taille normale)

L'inspection attentive des informations de débuggage de l'illustration 9.4 montre que, comme soulevé à la fin de la section 9.1.2, de la
soumission résulte une table params contenant l'email et le mot de passe sous la clé :session :

--- !map:ActiveSupport::HashWithIndifferentAccess
commit: S'identifier
session: !ActiveSupport::HashWithIndifferentAccess
password: ""
email: ""
authenticity_token: BlO65PA1oS5vqrv591dt9B22HGSWW0HbBtoHKbBKYDQ=
action: create
controller: sessions

Comme dans le cas de l'inscription de l'utilisateur (illustration 8.6) ces paramètres forment une table imbriquée comme celle que nous
avons vue dans l'extrait 4.5. En particulier, params contient une table imbriquée de la forme :
{ :session => { :password => "", :email => "" } }

Cela signifie que :


params[:session]

… est lui-même une table :


{ :password => "", :email => "" }

Comme résultat :
params[:session][:email]

… est l'adresse mail soumise et :


params[:session][:password]

… est le mot de passe soumis.

En d'autres termes, à l'intérieur de l'action create, la table params contient toutes les informations dont nous avons besoin pour
authentifier les utilisateurs par l'email et le mot de passe. Ça n'est pas le fait du hasard, mais nous avons déjà développé exactement la
méthode nécessaire : User.authenticate à la section 7.2.4 (extrait 7.12). En se souvenant que authenticate retourne nil pour une
authentification invalide, notre stratégie pour l'identification des utilisateurs peut être résumée comme suit :

def create
user = User.authenticate(params[:session][:email],
params[:session][:password])
if user.nil?
# Crée un message d'erreur et rend le formulaire d'identification.
else
# Authentifie l'utilisateur et redirige vers la page d'affichage.
end
end

9.2.2 Échec de l'identification (test et code)


Dans le but de traiter un échec d'identification, d'abord nous avons besoin de déterminer que c'est un échec. Les tests suivent l'exemple
des tests analogues pour l'inscription de l'utilisateur (extrait 8.6), comme vu dans l'extrait 9.7.

Extrait 9.7. Tests pour un échec d'identification.


spec/controllers/sessions_controller_spec.rb

require 'spec_helper'

describe SessionsController do
render_views
.
.
.
describe "POST 'create'" do

describe "invalid signin" do

before(:each) do
@attr = { :email => "email@example.com", :password => "invalid" }
end

it "devrait re-rendre la page new" do


post :create, :session => @attr
response.should render_template('new')
end

it "devrait avoir le bon titre" do


post :create, :session => @attr
response.should have_selector("title", :content => "S'identifier")
end

it "devait avoir un message flash.now" do


post :create, :session => @attr
flash.now[:error].should =~ /invalid/i
end
end
end
end

Le code de l'application nécessaire pour faire réussir ces tests est présenté dans l'extrait 9.8. Comme promis à la section 9.2.1, nous
extrayons l'adresse mail soumise et le mot de passe de la table params, et les passons ensuite à la méthode User.authenticate. Si
l'utilisateur n'est pas authentifié (c'est-à-dire : si la méthode d'authentification renvoie nil), nous définissons le titre et rendons à nouveau
le formulaire d'identification.4 Nous traiterons l'autre branche de la déclaration if-else à la section 9.3 ; pour le moment, nous allons juste
laissé un commentaire descriptif.

Extrait 9.8. Code pour une tentative ratée d'identification.


app/controllers/sessions_controller.rb

class SessionsController < ApplicationController


.
.
.
def create
user = User.authenticate(params[:session][:email],
params[:session][:password])
if user.nil?
flash.now[:error] = "Combinaison Email/Mot de passe invalide."
@titre = "S'identifier"
render 'new'
else
# Authentifie l'utilisateur et redirige vers sa page d'affichage.
end
end
.
.
.
end

Rappelez-vous de la section 8.4.2 : nous avons affiché les erreurs de l'inscription en utilisant les messages d'erreur du modèle User.
Puisque la session n'est pas un modèle Active Record, cette stratégie ne fonctionnera pas ici, donc à la place nous déposons un message
dans le flash (ou, plus exactement, dans flash.now ; voyez le Box 9.1). Grâce au message flash affiché dans le layout du site
(extrait 8.16), le message flash[:error] sera automatiquement affiché ; grâce aux CSS Blueprint, il aura automatiquement une belle
stylisation (illustration 9.5).

Box 9.1.Flash . now


Il existe une subtile différence enetre flash et flash.now. La variable flash est conçue pour être utilisée avant une redirection, et elle
persiste sur la page résultante de la-dite requête — c'est-à-dire qu'elle apparait une fois, disparait une fois, et disparait quand vous cliquez
un autre lien. Malheureusement, cela signifie que si nous ne redirigeons pas la page, et qu'au contraire nous ne faisons que la rendre
(comme dans extrait 9.8), le message flash persiste pour les deux requêtes : il apparait sur la page rendue mais il attend toujours pour une
rediction (c'est-à-dire une seconde requête), et ainsi apparait une nouvelle fois si vous cliquez un lien.

Pour éviter ce comportement bizarre, en rendant (render) plutôt qu'en redirigeant (redirect) nous utilisons flash.now plutôt que flash.
L'objet flash.now est spécialement conçu pour afficher les messages flash sur les pages rendues. Si vous vous demandez un jour
pourquoi un message flash est affiché à un endroit que vous n'attendiez pas, il y a de fortes chances qu'il faille mettre ce message dans
flash.now plutôt que dans flash.

Illustration 9.5: Une identification ratée (avec un message flash). (taille normale)

9.3 Réussite de l'identification


Ayant traité un échec de l'identification, nous avons besoin maintenant, effectivement, de pouvoir authentifier un utilisateur. La maquette
de l'illustration 9.6 donne une idée de là où nous allons — la page de profil de l'utilisateur, avec des liens de navigation.5 Même si cela
n'est pas évident du premier regard, parvenir à ce résultat requiert quelques-uns des défis de programmation Ruby les plus avancés, aussi,
accrochez-vous jusqu'au bout et préparez-vous à soulever du poids (ou à en prendre à force de libérer votre frustration sur les paquets de
chips. NdT). Heureusement, la première étape est facile — compléter l'action create du contrôleur Sessions est un jeu d'enfant.
Malheureusement, c'est aussi une escroquerie.
Illustration 9.6: Une maquette du profil utilisateur après une identification réussie (avec des liens de navigation actualisés).
(taille normale)

9.3.1 L'action create achevée


Remplir l'aire non occupée pour le moment par le commentaire d'identification (extrait 9.8) est simple : à la réussite de l'identification,
nous identifions l'utilisateur en utilisant la fonction sign_in et nous le re-dirigeons vers sa page de profil (extrait 9.9). Nous voyons
maintenant pourquoi c'est une escroquerie : hélas, sign_in n'existe pas pour le moment. L'écrire nous occupera pendant toute cette
section.

Extrait 9.9. L'action create du contrôleur Sessions achevée (mais pas encore fonctionnelle).
app/controllers/sessions_controller.rb

class SessionsController < ApplicationController


.
.
.
def create
user = User.authenticate(params[:session][:email],
params[:session][:password])
if user.nil?
flash.now[:error] = "Combinaison Email/Mot de passe invalide."
@titre = "S'identifier"
render 'new'
else
sign_in user
redirect_to user
end
end
.
.
.
end

Même si la fonction sign_in fait défaut, nous pouvons quand même écrire les tests (extrait 9.10) (nous remplirons le corps du premier
test à la section 9.3.3).

Extrait 9.10. Ajouter des tests pour l'identification de l'utilisateur (sera complet à la section 9.3.3).
spec/controllers/sessions_controller_spec.rb

describe SessionsController do
.
.
.
describe "POST 'create'" do
.
.
.
describe "avec un email et un mot de passe valides" do

before(:each) do
@user = Factory(:user)
@attr = { :email => @user.email, :password => @user.password }
end

it "devrait identifier l'utilisateur" do


post :create, :session => @attr
# Remplir avec les tests pour l'identification de l'utilisateur.
end

it "devrait rediriger vers la page d'affichage de l'utilisateur" do


post :create, :session => @attr
response.should redirect_to(user_path(@user))
end
end
end
end

Ces tests ne réussissent pas encore, mais c'est une bonne base.

9.3.2 Se souvenir de moi


Nous sommes maintenant en mesure de commencer à implémenter notre modèle d'identification, nommément, en se souvenant de l'état
« pour toujours » de l'identification de l'utilisateur et en effaçant la session seulement quand l'utilisateur se déconnecte explicitement de
lui-même. Les fonctions d'identification elles-mêmes finiront par franchir la ligne du traditionnel Modèle-Vue-Contrôleur ; en particulier,
plusieurs fonctions d'identification auront besoin d'être accessibles dans les contrôleurs tout comme dans les vues. Vous vous souvenez
sans doute, section 4.2.5, que Ruby fournit un module pratique pour emballer les fonctions ensemble et les inclure à plusieurs endroits, et
c'est ce que nous projetons pour les fonctions d'identification. Nous pourrions faire un tout nouveau module pour l'authentification, mais
le contrôleur Sessions nous arrive déjà équipé d'un module, nommément SessionsHelper. Plus encore, les helpers sont automatiquement
inclus dans les vues Rails, donc tout ce que nous avons besoin de faire pour utiliser les fonctions de l'helper Sessions dans les contrôleurs
est d'inclure le module dans le contrôleur Application (extrait 9.11).

Extrait 9.11. Inclure le module helper Sessions dans le contrôleur Application.


app/controllers/application_controller.rb

class ApplicationController < ActionController::Base


protect_from_forgery
include SessionsHelper
end

Par défaut, tous les helpers sont accessibles dans les vues (views) mais pas dans les contrôleurs. Nous avons besoin des méthodes de
l'helper Sessions aux deux endroits, donc nous devons l'inclure explicitement.

Box 9.2.Sessions et cookies

Parce que HTTP est un protacole sans état, les applications web requiérant l'identification des utilisateurs implémentent une façon de
pister chaque parcours d'utilisateur de page en page. Une technique pour maintenir l'état de l'identification de l'utilisateur consiste à
utiliser la traditionnelle session Rails (via la fonction spéciale sessions) pour enregistrer un rappel symbolique (remember_token) égal à
l'identifiant (id) de l'utilisateur :

session[:remember_token] = user.id

Cette objet session rend l'id de l'utilisateur accessible de page en page en l'enregistrant dans un cookie qui expire à la fermeture du
navigateur. Sur chaque page, l'application a juste besoin d'appeler :

User.find_by_id(session[:remember_token])

… pour récupérer l'utilisateur. Grâce à la façon dont Rails traite les sessions, ce processus est sécurisé ; si un utilisateur malicieux essaie
de « parodier » l'id d'un utilisateur, Rails détectera une discordance en s'appuyant sur le session id particulier généré pour chaque session.

Pour nos choix de conception de l'application, qui implique des sessions persistantes — c'est-à-dire des états d'identification qui durent
même lorsque le navigateur est fermé — enregistrer l'identifiant de l'utilisateur constitue un trou de sécurité. Dès que nous cassons le lien
entre l'id de session particulier et l'id de l'utilisateur enregistré, un utilisateur malveillant pourrrait s'identifier comme cet utilisateur avec
un remember_token égal à l'id de l'utilisateur. Pour fixer ce défaut, nous générons un rappel symbolique unique, sécurisé, pour chaque
utilisateur, basé sur l'id et le salt (sel) de l'utilisateur. Plus encore, un rappel symbolique permanent devrait aussi représenter un trou de
sécurité — en inspectant les cookies du navigateur, un utilisateur malveillant pourrait trouver le rappel symbolique et l'utiliser alors pour
s'identifier depuis n'importe quel autre ordinateur, n'importe quand. Nous résolvons cela en ajoutant un timestamp (une signature de
temps) au rappel symbolique, et ré-initialisons ce rappel chaque fois que l'utilisateur s'identifie sur l'application. Il en résulte une session
persistante essentiellement imperméable à l'attaque.
Nous sommes maintenant prêts pour le premier élément d'identification, la fonction sign_in elle-même. Notre méthode d'authentification
consiste à placer un rappel symbolique (remember token) comme cookie sur le navigateur de l'utilisateur (Box 9.2), et de l'utiliser ensuite
pour trouver l'enregistrement de l'utilisateur dans la base de données chaque fois que l'utilisateur navigue de page en page (implémentée
à la section 9.3.3). Le résultat (extrait 9.12) pousse deux choses dans la pile : la table cookies et current_user.6 Laissons-les maintenant
s'exprimer…

Extrait 9.12. La fonction sign_in complète (mais pas encore fonctionnelle).


app/helpers/sessions_helper.rb

module SessionsHelper

def sign_in(user)
cookies.permanent.signed[:remember_token] = [user.id, user.salt]
self.current_user = user
end
end

L'extrait 9.12 introduit l'utilitaire cookies fourni par Rails. Nous pouvons utiliser cookies comme si c'était une table ; chaque élément
dans le cookie est lui-même une table de deux éléments, une value (une valeur) et une date optionnelle expires. Par exemple, nous
pourrions implémenter l'identification de l'utilisateur en plaçant un cookie avec une valeur égale à l'id de l'utilisateur qui expire dans
vingt ans :

cookies[:remember_token] = { :value => user.id,


:expires => 20.years.from_now.utc }

(Ce code utilise l'un des helpers de temps pratique de Rails, comme discuté dans la Box 9.3.) Nous pourrions alors récupérer l'utilisateur
avec un code comme :
User.find_by_id(cookies[:remember_token])

Bien sûr, cookies n'est pas vraiment une table, puisque renseigner un cookie, en fait, enregistre une petite pièce de texte sur le navigateur
(comme vu dans l'illustration 9.7), mais une part de la beauté de Rails est qu'il vous laisse oublier ce genre de détail et se concentre sur
l'écriture de l'application.

Illustration 9.7: Un rappel symbolique sécurisé. (taille normale)

Malheureusement, utiliser l'id de l'utilisateur de cette manière n'est pas très sûr, pour des raisons discutées dans le Box 9.2 : un utilisateur
malveillant pourrait simuler un cookie avec l'id donnée, et ainsi permettre l'accès du système à n'importe quel utilisateur. La solution
traditionnelle avant Rails 3 était de créer un rappel symbolique sécurisé associé au modèle User à utiliser à la place de l'id de l'utilisateur
(voyez par exemple la version Rails 2.3 du Tutoriel Rails). Cette façon de faire est devenue si courante que Rails 3 l'implémente
maintenant pour nous en utilisant cookies.permanent.signed :
cookies.permanent.signed[:remember_token] = [user.id, user.salt]

L'assignement de la valeur du côté droit est un tableau consistant en un identifiant unique (c'est-à-dire l'id de l'utilisateur) et une valeur
sécurisée utilisée pour créer une signature digitale pour empêcher le genre d'attaque décrite à la section 7.2. En particulier, puisque nous
avons pris la peine de créer un salt sécurisé à la section 7.2.3, nous pouvons réutiliser cette valeur ici pour signer le rappel symbolique.
Sous le capot, utiliser permanent fait que Rails règle l'expiration à 20.years.from_now, et signed rend le cookie sécurisé, de telle sorte que
l'id de l'utilisateur n'est jamais exposé dans le navigateur (nous verrons comment récupérer l'utilisateur en utilisant le rappel symbolique
à la section 9.3.3).

Le code ci-dessus montre l'importance d'utiliser new_record? dans l'extrait 7.10 pour sauver le sel seulement à la création de l'utilisateur.
Dans le cas contraire, ce salt changerait chaque fois que l'utilisateur est enregistré, empêchant de récupérer la session de l'utilisateur de
la section 9.3.3.

Box 9.3.Les cookies expirent 20.years.from_now (20.ans.plus_tard)

Vous vous souvenez sans doute, de la section 4.4.2, que Ruby vous laisse ajouter des méthodes à n'importe quelle classe, même les
classes intégrées. Dans la section mentionnée, nous avons ajouté une méthode palindrome? à la classe String (et découvert en résultat
que "kayak" était un palindrome), et nous avons vu aussi comment Rails ajoutait une méthode blank? à la classe Object (Objet) (de telle
sorte que "".blank?, " ".blank? et nil.blank? sont toutes true). Le code du cookie dans l'extrait 9.12 (qui définit internalement un
cookie qui expire 20.years.from_now) donne encore un autre exemple de cette pratique à travers l'un des helpers de temps Rails, qui sont
des méthodes ajoutées à la classe Fixnum (la classe de base pour les nombres) :
$ rails console
>> 1.year.from_now
=> Sun, 13 Mar 2011 03:38:55 UTC +00:00
>> 10.weeks.ago
=> Sat, 02 Jan 2010 03:39:14 UTC +00:00

Rails ajoute d'autres helpers aussi :

>> 1.kilobyte
=> 1024
>> 5.megabytes
=> 5242880

Elles sont utiles pour les validations de téléchargement, rendant plus facile de restreindre, disons, la taille des images téléchargées à
5.megabytes.

Bien qu'elle doive être utilisée avec précaution, la flexibilité qui autorise d'ajouter des méthodes aux classes intégrées permet des ajouts
extraordinairement naturels à Ruby. En effet, toute l'élégance de Rails découle en fin de compte de la malléabilité du langage sous-jacent
Ruby.

9.3.3 Utilisateur courant


Dans cette section, nous apprendrons comment obtenir et définir la session de l'utilisateur courant. Regardons à nouveau la fonction
sign_in pour voir où nous en sommes :

module SessionsHelper

def sign_in(user)
cookies.permanent.signed[:remember_token] = [user.id, user.salt]
self.current_user = user
end
end

Concentrons-nous maintenant sur la deuxième ligne :7

self.current_user = user

Le but de cette ligne est de créer une variable current_user, accessible aussi bien dans les contrôleurs que dans les vues, ce qui permettra
des constructions telles que :

<%= current_user.nom %>

… ou :

redirect_to current_user

Le but principal de cette section est de définir current_user.

Pour décrire le comportement de la machinerie d'identification restant à implémenter, nous allons d'abord remplir le test pour
l'identification de l'utilisateur (extrait 9.13).

Extrait 9.13. Remplir le test pour l'identification de l'utilisateur.


spec/controllers/sessions_controller_spec.rb

describe SessionsController do
.
.
.
describe "POST 'create'" do
.
.
.
describe "avec un email et un mot de passe valides" do

before(:each) do
@user = Factory(:user)
@attr = { :email => @user.email, :password => @user.password }
end

it "devrait identifier l'utilisateur" do


post :create, :session => @attr
controller.current_user.should == @user
controller.should be_signed_in
end

it "devrait rediriger vers la page d'affichage de l'utilisateur" do


post :create, :session => @attr
response.should redirect_to(user_path(@user))
end
end
end
end

Le nouveau test utilise la variable controller (contrôleur) (accessible à l'intérieur des tests Rails) pour vérifier que la variable
current_user est réglée à l'utilisateur identifié, et que l'utilisateur est identifié :

it "devrait identifier l'utilisateur" do


post :create, :session => @attr
controller.current_user.should == @user
controller.should be_signed_in
end

La deuxième ligne peut être un peu déroutante à ce stade, mais vous pouvez deviner en vous fondant sur la convention RSpec pour les
méthodes booléenne que :
controller.should be_signed_in

… est équivalent à :
controller.signed_in?.should be_true

C'est une allusion au fait que nous devrons définit une méthode signed_in? qui retourne true (vrai) si un utilisateur est identifié et false
(faux) dans le cas contraire. Plus encore, la méthode signed_in? sera attachée au contrôleur, pas à l'utilisateur, ce qui explique pourquoi
nous écrivons controller.signed_in? au lieu de current_user.signed_in? (si aucun utilisateur n'est identifié, comment pourrions-nous
appelé signed_in? sur lui ?).

Pour commencer à écrire le code de current_user, notez que la ligne :


self.current_user = user

… est un assignement. Ruby possède une syntaxe spéciale pour définir de telle fonction d'assignement, montré dans l'extrait 9.14.

Extrait 9.14. Définir l'assignement de current_user.


app/helpers/sessions_helper.rb

module SessionsHelper

def sign_in(user)
.
.
.
end

def current_user=(user)
@current_user = user
end
end

Cela peut sembler déroutant, mais ça définit simplement une méthode current_user= expressément conçue pour traiter l'assignement de
current_user. Son premier argument est la partie droite de l'assignement, dans ce cas l'utilisateur qui doit être identifié. Le corps d'une
ligne de cette méthode définit juste une variable d'instance @current_user, en enregistrant effectivement l'utilisateur pour un usage
ultérieur.

En Ruby ordinaire, nous pourrions définir une deuxième méthode, current_user, conçue pour retourner la valeur de @current_user
(extrait 9.15).

Extrait 9.15. Un définition tentante mais inutile de current_user.


module SessionsHelper

def sign_in(user)
.
.
.
end

def current_user=(user)
@current_user = user
end

def current_user
@current_user # Inutile ! N'utilisez pas cette ligne.
end
end

Si nous faisions ça, nous répliquerions en effet la fonctionnalité de attr_accessor, vue pour la première fois à la section 4.4.5 et utilisée
pour créer un password (mot de passe) virtuel à la section 7.1.1.8 Le problème est que ça échoue complètement pour résoudre notre
problème : avec le code de l'extrait 9.15, l'état de l'identification de l'utilisateur serait oublié : dès que l'utilisateur rejoindrait une autre
page — poof ! — la session cesserait et l'utilisateur serait automatiquement déconnecté.

Pour éviter ce problème, nous pouvons trouver la session utilisateur correspondant au cookie créé par le code de l'extrait 9.12, comme
montré dans l'extrait 9.16.

Extrait 9.16. Trouver l'utilisateur courant par remember_token.


app/helpers/sessions_helper.rb

module SessionsHelper
.
.
.
def current_user
@current_user ||= user_from_remember_token
end

private

def user_from_remember_token
User.authenticate_with_salt(*remember_token)
end

def remember_token
cookies.signed[:remember_token] || [nil, nil]
end
end

Ce code utilise plusieurs fonctionnalités plus avancées de Ruby, donc prenons un moment pour les examiner.

D'abord, l'extrait 9.16 utilise l'opérateur d'assignement courant mais pour le moment obscur ||= (« ou égal ») (Box 9.4). Son effet est de
régler la variable d'instance à l'utilisateur correspondant au rappel symbolique, mais seulement si @current_user n'est pas défini.9 En
d'autres mots, la construction :

@current_user ||= user_from_remember_token

… appelle la méthode user_from_remember_token la première fois que current_user est appelé, mais retourne @current_user au cours des
invocations suivantes sans appeler user_from_remember_token.10

Box 9.4.Qu'est-ce que c'est que *$@! ||= ?

La construction ||= est très « Ruby-ichienne » —c'est-à-dire qu'elle est hautement caractéristique du langage Ruby — et donc importante
à apprendre si vous projetez de faire plus de programmation Ruby. Bien qu'au début elle puisse sembler mystérieuse, ou égal est facile à
comprendre par analogie.

Nous commençons par noter un idiome commun pour changer une variable déjà définie. De nombreux programmes informatiques
utilisent l'incrémentation de variable, comme dans :
x = x + 1

La plupart des langages fournissent un raccourci sémantique pour cette opération ; en Ruby (et en C, C++, Perl, Python, Java, etc.), il
apparait ainsi :

x += 1

Des constructions analogues existent de la même façon pour d'autres opérateurs :

$ rails console
>> x = 1
=> 1
>> x += 1
=> 2
>> x *= 3
=> 6
>> x -= 7
=> -1
Dans chaque cas, le modèle est que x = x O y et x O= y sont équivalents pour n'importe quel opérateur O.

Une autre pattern courante de Ruby est d'assigner une variable si elle est null (nil) mais de la laisser telle quelle dans le cas contraire. En
vous souvenant de l'opérateur ou || vu à la section 4.2.3, nous pouvons écrire cela comme suit :

>> @user
=> nil
>> @user = @user || "l'utilisateur"
=> "l'utilisateur"
>> @user = @user || "un autre utilisateur"
=> "l'utilisateur"

Puisque nil est faux dans un contexte booléen, le premier assignement est nil || "l'utilisateur", qui est évalué à "l'utilisateur" ;
de façon similaire, le second assignement est "l'utilisateur" || "un autre utilisateur", qui est aussi évalué à "l'utilisateur" —
puisque les chaines de caractères sont true (vrai) dans un contexte booléen, la série d'expression || est interrompue dès qu'une première
expression n'est pas nulle (cette pratique d'évaluer les expressions || de gauche à droite et de s'arrêter à la première valeur vraie est
connue sous le nom de évaluation court-circuit).

En comparant les sessions de console pour une variété d'opérateurs, nous voyons que @user = @user || value suit le modèle x = x O y
avec || à la place de O, ce qui suggère la construction équivalente suivante :
>> @user ||= "L'utilisateur"
=> "the user"

Et voilà !

L'extrait 9.16 utilise aussi l'opérateur *, qui nous permet d'utiliser un tableau à deux éléments comme argument pour une méthode
attendant deux variables, comme nous pouvons le voir dans la session de console :
$ rails console
>> def foo(bar, baz)
?> bar + baz
?> end
=> nil
>> foo(1, 2)
=> 3
>> foo(*[1, 2])
=> 3

La raison pour laquelle c'est nécessaire dans l'extrait 9.16 est que cookies.signed[:remember_me] retourne un tableau de deux éléments
— l'id de l'utilisateur et le salt — mais, (en suivant les conventions de Ruby) nous voulons que la méthode authenticate_with_salt
prenne deux arguments, donc elle peut être invoquée avec :
User.authenticate_with_salt(id, salt)

(Il n'existe pas de raison fondamentale pour que authenticate_with_salt ne puisse prendre un tableau comme argument, mais ce ne
serait pas idiomatiquement correct en Ruby.)

Enfin, dans la méthode d'helper remember_token définie par l'extrait 9.16, nous utilisons l'opérateur || pour retourner un tableau de
valeurs nulles si cookies.signed[:remember_me] lui-même est nul :

cookies.signed[:remember_token] || [nil, nil]

La raison d'être de ce code est que le support pour les tests des cookies d'identification est encore jeune, et la valeur nil pour le cookie
occasionne des cassures de test fallacieux. Retourner plutôt [nil, nil] règle ce problème.11

L'étape finale pour obtenir que le code de l'extrait 9.16 fonctionne est de définir une méthode de classe authenticate_with_salt. Cette
méthode, qui est analogue à la méthode originale authenticate définie dans l'extrait 7.12, est montrée dans l'extrait 9.17.

Extrait 9.17. Ajout d'une méthode authenticate_with_salt au modèle User.


app/models/user.rb

class User < ActiveRecord::Base


.
.
.

def self.authenticate(email, submitted_password)


user = find_by_email(email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end

def self.authenticate_with_salt(id, cookie_salt)


user = find_by_id(id)
(user && user.salt == cookie_salt) ? user : nil
end
.
.
.
end

Ici, authenticate_with_salt commence par trouver l'utilisateur par son id unique, et vérifie alors que le salt enregistré dans le cookie
est bien celui de l'utilisateur.

Il est important de noter que cette implémentation de authenticate_with_salt est identique à la fonction du code suivant, qui est plus
proche de la méthode authenticate :
def self.authenticate_with_salt(id, cookie_salt)
user = find_by_id(id)
return nil if user.nil?
return user if user.salt == cookie_salt
end

Dans les deux cas, la méthode retourne l'utilisateur si user est pas nil et si le sel de l'utilisateur correspond au sel du cookie, et retourne
nil dans le cas contraire. D'un autre côté, le code tel que :

(user && user.salt == cookie_salt) ? user : nil

… est courant en Ruby idiomatique correct, donc j'ai pensé que c'était une bonne idée de l'introduire. Ce code utilise l'étrange mais utile
ternary operator (opérateur ternaire) pour « compresser » une construction if-else en une seule ligne (Box 9.5).

Box 9.5.10 types de gens

Il y a 10 genres de personnes dans le monde : ceux qui aiment à l'opérateur ternaire, ceux qui ne l'aiment pas, et ceux qui ne le
connaissent pas (si vous faites partie de la troisième catégorie, vous n'y appartiendrez plus longtemps).

Quand vous faites beaucoup de programmation, vous apprenez rapidement que l'un des blocs de contrôle des plus courants ressemble à :
if boolean? # Si ça est vrai
faire_une_chose
else # sinon
faire_autre_chose
end # fin

Ruby, comme la plupart des autres langages (incluant C/C++, Perl, PHP et Java), vous permettent de remplacer ça part une expression
plus compacte en utilisant l'opérateur ternaire (appelé ainsi parce qu'il est constitué de trois parties) :
boolean? ? faire_une_chose : faire_autre_chose

Vous pouvez aussi utiliser l'opérateur ternaire pour remplacer l'assignement :


if boolean?
var = foo
else
var = bar
end

… devient alors :

var = boolean? ? foo : bar

L'opérateur ternaire est courant en Ruby idiomatique, donc c'est une bonne idée de chercher les opportunités de l'utiliser.

À ce point où nous en sommes, le test d'identification réussit presque ; la seule chose restant à faire est de définir la méthode booléenne
signed_in?. Heureusement, c'est facile avec l'utilisation de l'opérateur « not » (pas) ! : un utilisateur est identifié si current_user n'est
pas nil (extrait 9.18).

Extrait 9.18. La méthode d'helper signed_in?.


app/helpers/sessions_helper.rb

module SessionsHelper
.
.
.
def signed_in?
!current_user.nil?
end

private
.
.
.
end

Bien qu'elle soit déjà utile pour le test, nous allons revoir la méthode signed_in? pour un meilleur usage encore à la section 9.4.3 et
encore au chapitre 10.

Avec ça, tous les tests devraient réussir.


9.4 Déconnexion
Comme discuté à la section 9.1, notre modèle d'authentification est de garder les utilisateurs identifiés jusqu'à ce qu'ils se déconnectent
explicitement. Dans cette section, nous allons ajouter les fonctionnalités nécessaires à la déconnexion. Une fois cela fait, nous ajouterons
quelques tests d'intégration pour mettre à l'épreuve notre machinerie de déconnexion.

9.4.1 Détruire les sessions


Jusqu'ici, les actions du contrôleur Sessions ont suivi la convention REST en utilisant new pour une page d'identification et create pour
achever l'identification. Nous allons poursuivre sur cette voie en utilisant une action destroy pour effacer la sessions, c'est-à-dire pour se
déconnecter.

Pour tester l'action déconnexion, nous avons d'abord besoin d'un moyen de s'identifier dans le test. La façon la plus facile de faire ça est
d'utiliser l'objet controller vu à la section 9.3.3 et d'utiliser l'helper sign_in pour identifier l'utilisateur donné. Pour pouvoir utiliser la
fonction test_sign_in en résultant dans tous nos tests, nous devons la placer dans le fichier helper spec, comme vu dans l'extrait 9.19.12

Extrait 9.19. Une fonction test_sign_in pour simuler l'identification de l'utilisateur à l'intérieur des tests.
spec/spec_helper.rb

.
.
.
Rspec.configure do |config|
.
.
.
def test_sign_in(user)
controller.sign_in(user)
end
end

Après avoir joué test_sign_in, current_user ne sera pas nil, donc signed_in? renverra true (vrai).

Avec cet helper spec en main, le test pour la déconnexion est simple : s'identifier avec un utilisateur (d'usine) et alors invoquer l'action
destroy et vérifier que l'utilisateur se trouve déconnecté (extrait 9.20).

Extrait 9.20. Un test de destruction de la session (déconnexion utilisateur).


spec/controllers/sessions_controller_spec.rb

describe SessionsController do
.
.
.
describe "DELETE 'destroy'" do

it "devrait déconnecter un utilisateur" do


test_sign_in(Factory(:user))
delete :destroy
controller.should_not be_signed_in
response.should redirect_to(root_path)
end
end
end

Le seul élément nouveau ici est la méthode delete, qui répond à la requête HTTP DELETE (en analogie aux méthodes get et post vues
dans les précédents tests), comme l'exigent les conventions REST (Table 9.1).

Comme avec l'identification de l'utilisateur, qui est reliée à la fonction sign_in, la déconnexion de l'utilisateur confie juste le dur boulot à
la fonction sign_out (extrait 9.21).

Extrait 9.21. Détruire une session (déconnexion utilisateur).


app/controllers/sessions_controller.rb

class SessionsController < ApplicationController


.
.
.
def destroy
sign_out
redirect_to root_path
end
end

Comme avec les autres éléments d'authentification, nous placerons sign_out dans le module helper Sessions (extrait 9.22).

Extrait 9.22. La méthode sign_out dans le module helper Sessions.


app/helpers/sessions_helper.rb
module SessionsHelper

def sign_in(user)
cookies.permanent.signed[:remember_token] = [user.id, user.salt]
self.current_user = user
end
.
.
.
def sign_out
cookies.delete(:remember_token)
self.current_user = nil
end

private
.
.
.
end

Comme vous pouvez le voir, la méthode sign_out effectivement annule la méthode sign_in en effaçant le rappel symbolique (remember
token) et en réglant l'utilisateur courant à nil.13

9.4.2 Connexion à l'inscription


En principe, nous en avons fini avec l'authentification, mais telle que se présente l'application actuellement, il n'y a pas de liens pour les
actions d'identification et de déconnexion. Plus encore, les tout nouveaux utilisateurs enregistrés peuvent être déroutés de ne pas être
identifiés par défaut à l'inscription.

Nous allons fixer ce second problème d'abord, en commençant par tester qu'un nouvel utilisateur est automatiquement identifié
(extrait 9.23).

Extrait 9.23. Tester qu'un nouvel utilisateur soit aussi identifié.


spec/controllers/users_controller_spec.rb

require 'spec_helper'

describe UsersController do
render_views
.
.
.
describe "POST 'create'" do
.
.
.
describe "success" do
.
.
.
it "devrait identifier l'utilisateur" do
post :create, :user => @attr
controller.should be_signed_in
end
.
.
.
end
end
end

Avec la méthode sign_in de la section 9.3, faire que le test réussisse en identifiant un utilisateur est facile : ajoutez juste sign_in @user
juste après avoir sauver l'utilisateur dans la base de données (extrait 9.24).

Extrait 9.24. Identifier l'utilisateur après son inscription.


app/controllers/users_controller.rb

class UsersController < ApplicationController


.
.
.
def create
@user = User.new(params[:user])
if @user.save
sign_in @user
flash[:success] = "Bienvenue dans l'Application Exemple !"
redirect_to @user
else
@titre = "Sign up"
render 'new'
end
end
9.4.3 Changement des liens de la mise en page

Nous en arrivons finalement à une application fonctionnelle pour tous nos travaux d'identification et d'inscription : nous allons changer
les liens du layout selon l'état de l'identification. En particulier, comme le montre la maquette de l'illustration 9.6, nous allons faire en
sorte que les liens changent suivant que l'utilisateur est identifié ou déconnecté, et nous allons ajouter aussi un lien vers la page de profil
pour un utilisateur identifié.

Nous commençons avec les deux tests d'intégration : un pour vérifier que le lien "S'inscrire" est visible pour un utilisateur non
identifié, et un pour vérifier que le lien Déconnexion soit visible pour un utilisateur identifié ; ces deux cas vérifient que le lien conduise à
l'URL appropriée. Nous placerons ces tests dans le test des liens du layout que nous avons créé à la section 5.2.1 ; le résultat apparait
dans l'extrait 9.25.

Extrait 9.25. Tests pour les liens de connexion/déconnexion dans le layout du site.
spec/requests/layout_links_spec.rb

describe "Liens du layout" do


.
.
.
describe "quand pas identifié" do
it "doit avoir un lien de connexion" do
visit root_path
response.should have_selector("a", :href => signin_path,
:content => "S'identifier")
end
end

describe "quand identifié" do

before(:each) do
@user = Factory(:user)
visit signin_path
fill_in :email, :with => @user.email
fill_in "Mot de passe", :with => @user.password
click_button
end

it "devrait avoir un lien de déconnxion" do


visit root_path
response.should have_selector("a", :href => signout_path,
:content => "Déconnexion")
end

it "devrait avoir un lien vers le profil"


end
end

Ici le bloc before(:each) identifie en visitant la page d'identification et en soumettant une paire email/mot de passe valide.14 Nous
faisons cela plutôt que d'utiliser la fonction test_sign_in de l'extrait 9.19 parce que test_sign_in ne fonctionne pas à l'intérieur des tests
d'intégration pour certaines raisons (voir la section 9.6 pour un exercice pour construire une fonction integration_sign_in à l'usage des
tests d'intégration).

Le code de l'application utilise une structure de branchement si-alors à l'intérieur du code Ruby embarqué, en utilisant la méthode
signed_in? définie dans l'extrait 9.18 :

<% if signed_in? %>


<li><%= link_to "Déconnexion", signout_path, :method => :delete %></li>
<% else %>
<li><%= link_to "S'identifier", signin_path %></li>
<% end %>

Remarquez que le lien de déconnexion passe un argument table indiquant qu'il devrait soumettre avec une requête HTTP DELETE.15 Avec
ce fragment ajouté, l'entête complète du partiel apparait comme dans l'extrait 9.26.

Extrait 9.26. Changer les liens du layout links pour l'utilisateur identifié.
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>
<% if signed_in? %>
<li><%= link_to "Déconnexion", signout_path, :method => :delete %></li>
<% else %>
<li><%= link_to "S'identifier", signin_path %></li>
<% end %>
</ul>
</nav>
</header>
Dans l'extrait 9.26 nous avons utilisé l'helper logo des exercices du chapitre 5 (section 5.5) ; dans le cas où vous n'auriez pas fait cet
exercice, la réponse apparait dans l'extrait 9.27.

Extrait 9.27. Un helper pour le logo du site.


app/helpers/application_helper.rb

module ApplicationHelper
.
.
.
def logo
image_tag("logo.png", :alt => "Application Exemple", :class => "round")
end
end

Enfin, ajoutons le lien vers le profil. Le test (extrait 9.28) et le code de l'application (extrait 9.29) sont tous deux extrêmement simples.
Remarquez que l'URL du lien vers le profil est simplement current_user,16 qui est notre première utilisation de cette méthode utile (ça
ne sera pas la dernière).

Extrait 9.28. Un test pour le lien du profil.


spec/requests/layout_links_spec.rb

describe "Liens du layout" do


.
.
.
describe "quand identifié" do
.
.
.
it "devrait avoir un lien vers le profil" do
visit root_path
response.should have_selector("a", :href => user_path(@user),
:content => "Profil")
end
end
end

Extrait 9.29. Ajout du lien vers le profil.


app/views/layouts/_header.html.erb

<header>
<%= link_to logo, root_path %>
<nav class="round">
<ul>
<li><%= link_to "Accueil", root_path %></li>
<% if signed_in? %>
<li><%= link_to "Profil", current_user %></li>
<% end %>
<li><%= link_to "Aide", help_path %></li>
<% if signed_in? %>
<li><%= link_to "Déconnexion", signout_path, :method => :delete %></li>
<% else %>
<li><%= link_to "S'identifier", signin_path %></li>
<% end %>
</ul>
</nav>
</header>

Avec le code de cette section, un utilisateur identifié voit maintenant les deux liens de déconnexion et de profil, comme voulu
(illustration 9.8).
Illustration 9.8: Un utilisateur identifié avec les liens déconnexion et profil (version anglaise). (taille normale)

9.4.4 Test d'intégration pour l'identification et la déconnexion


Pierre angulaire de notre dur labeur sur l'authentification, nous allons finir avec les tests d'intégration pour l'identification et la
déconnexion (placé dans le fichier users_spec.rb pour le côté pratique). Le testing d'intégration RSpec est suffisamment expressif pour
que l'extrait 9.30 ne demande qu'une toute petite explication ; j'aime spécialement l'utilisation de click_link "Déconnexion", qui ne fait
pas que simuler le clic sur le lien de déconnexion dans le navigateur mais produit également une erreur si ce lien n'existe pas — testant
ainsi l'URL, la route nommée, le texte du lien et le changement des liens dans le laxyout en une seule ligne. Si ça n'est pas un test
d'integration, je ne sais pas ce que c'est !

Extrait 9.30. Un test d'intégration pour l'identification et la déconnxion.


spec/requests/users_spec.rb

require 'spec_helper'

describe "Users" do

describe "signup" do
.
.
.
end

describe "identification/déconnexion" do

describe "l'échec" do
it "ne devrait pas identifier l'utilisateur" do
visit signin_path
fill_in "eMail", :with => ""
fill_in "Mot de passe", :with => ""
click_button
response.should have_selector("div.flash.error", :content => "Invalid")
end
end

describe "le succès" do


it "devrait identifier un utilisateur puis le déconnecter" do
user = Factory(:user)
visit signin_path
fill_in "eMail", :with => user.email
fill_in "Mot de passe", :with => user.password
click_button
controller.should be_signed_in
click_link "Déconnexion"
controller.should_not be_signed_in
end
end
end
end

9.5 Conclusion
Nous avons couvert un grand nombre de points dans ce chapitre, transformant notre application prometteuse mais informe en site capable
d'accomplir une pleine suite de registration et d'identification. Tout ce qu'il reste à faire pour achever l'authentification est de restreindre
l'accès aux pages en s'appuyant sur l'état de l'identification et l'identité de l'utilisateur. Nous accomplirons ces tâches tout en donnant à
l'utilisateur la possibilité de modifier ses informations et en donnant aux administrateurs la faculté de supprimer des utilisateurs.

Mais avant de poursuivre, fusionnons nos changements dans la branche maitresse de notre repository :
$ git add .
$ git commit -m "Fini avec l'identification et la deconnexion"
$ git checkout master
$ git merge sign-in-out

9.6 Exercises
Le deuxième et troisième exercices sont plus difficiles que d'habitude. Les résoudre nécessitera des recherches externes (par exemple la
consultation de l'API Rails et des recherches Google), mais ils peuvent être sautés sans perte de la continuité du livre.

1. Plusieurs des spécifications d'intégration utilisent le même code pour identifier un utilisateur. Remplacez ce code par une fonction
integration_sign_in dans l'extrait 9.31 et vérifiez que les tests continuent de réussir.
2. Utilisez la session plutôt que les cookies de telle sorte que les utilisateurs puissent être automatiquement déconnectés quand ils
ferment leur navigateur..17 Astuce : faites une recherche Google sur les termes « Rails session ».
3. (avancé) Certains sites utilisent une connexion sécurisée (HTTPS) pour leurs pages d'identification. Cherchez en ligne pour
apprendre comment utiliser HTTPS en Rails, et sécurisez ensuite les actions new et create du contrôleur Sessions. Challenge
supplémentaire : écrivez des tests pour les fonctionnalités HTTPS (note : je vous suggère de faire cet exercice seulement en mode
développement, ce qui ne nécessitera pas d'obtenir un certificat SSL ou de régler la machinerie de cryptage SSL. En réalité,
déployer un site « SSL-enabled » est beaucoup plus difficile).

Extrait 9.31. Une fonction pour identifier les utilisateurs à l'intérieur des tests d'intégration.
spec/spec_helper.rb

.
.
.
Rspec.configure do |config|
.
.
.
def test_sign_in(user)
controller.sign_in(user)
end

def integration_sign_in(user)
visit signin_path
fill_in "eMail", :with => user.email
fill_in "Mot de passe", :with => user.password
click_button
end
end

« chapitre 8 Inscription chapitre 10 Actualiser, afficher et supprimer des utilisateurs »

1. Un autre modèle courant est de faire expirer la session après un certain laps de temps. C'est spécialement approprié sur les sites qui
contiennent des données sensibles, comme les banques et les comptes financiers. ↑
2. Nous verrons à la section 9.3.2 combien de temps signifie en réalité « pour toujours ». ↑
3. Si on lui donne les actions create et destroy, le script generate construira les vues pour ces actions, ce dont nous n'avons pas
besoin. Bien sûr, nous pourrions détruire ces vues, mais j'ai décidé de les omettre de la commande generate et de plutôt définir ces
actions à la main. ↑
4. Dans le cas où vous vous demanderiez pourquoi nous utilisons user plutôt que @user dans l'extrait 9.8, c'est parce que cette
variable user n'est jamais demandée dans aucune vue, donc il n'y a pas de raison d'utiliser une variable d'instance ici (utiliser @user
fonctionnerait aussi, cependant). ↑
5. Image provenant de http://www.flickr.com/photos/hermanusbackpackers/3343254977/. ↑
6. Sur certains systèmes, vous pouvez avoir besoin d'utiliser self.current_user = user pour faire réussir les tests à venir. ↑
7. Parce que le module helper Sessions est inclus dans le contrôleur Application, la variable self ici est le contrôleur lui-même. ↑
8. En fait, les deux sont exactement équivalents ; attr_accessor est simplement une façon pratique de créer automatiquement des
méthodes telles que getter et setter. ↑
9. Classiquement, cela signifie assigner les valeurs qui sont intialement nil, mais notez que les valeurs false seront également sur-
définies par l'opérateur ||=. ↑
10. Cette technique d'optimisation pour éviter de répéter des appels de fonction est connue sous le noim de mémoization. ↑
11. Cela ressemble à l'histoire de la queue qui remue le chien, mais c'est le prix à payer pour rester à la pointe. ↑
12. Si vous utilisez Spork, elle sera placée à l'intérieur du bloc Spork.prefork. ↑
13. Vous pouvez apprendre beaucoup sur des choses telles que cookies.delete en lisant l'entrée « cookies » dans l'API Rails (puisque
les liens vers l'API Rails tendent à être très vite dépassés, utilisez votre moteur de recherche pour trouver une version actualisée). ↑
14. Notez que nous pouvons utiliser les symboles à la place des chaines de caractères pour les labels, par exemple fill_in :email au
lieu de fill_in "Email". Nous utilisons ce dernier dans l'extrait 8.22, mais maintenant cela ne devrait pas vous surprendre que
Rails nous permette d'utiliser plutôt les symboles. ↑
15. Les navigateurs web ne peuvent pas, en vérité, transmettre une requête DELETE ; Rails la simule avec JavaScript. ↑
16. De la section 7.3.3 vous vous souvenez que nous pouvons nous lier directement à un objet utilisateur et permettre à Rails de
deviner l'URL adéquate. ↑
17. Ce qui est un peu déroutant, c'est que nous avons utilisé les cookies pour implémenter les sessions, et la session est implémentée
avec les cookies ! ↑