Académique Documents
Professionnel Documents
Culture Documents
Rejoindre le contenu
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 !
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
/*
* ------------------------------------------------------------
* "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.
* ------------------------------------------------------------
*/
Ce chapitre final contient l'étude du matériau le plus difficile du tutoriel, incluant une modélisation de données complexe et quelques
ruses Ruby/SQL pour créer l'état d'alimentation. Au travers de ces exemples, nous verrons comment Rails peut manipuler et même
intriquer les modèles de données, ce qui devrait vous être très utile lorsque vous développerez vos propres applications et leurs exigences
particulières. Pour faciliter la transition du tutoriel au développement personnel, la section 12.4 contiendra quelques suggestions
d'extensions du cœur de l'application-exemple, accompagnées de conseils à propos de ressources plus avancées.
Comme d'habitude, les utilisateurs de Git devraient créer une nouvelle branche sujet :
$ git checkout -b following-users
Les nouveaux concepts abordés dans ce chapitre étant particulièrement délicats, avant d'écrire la moindre ligne de code, nous allons nous
arrêter un moment pour faire un tour d'horizon de la notion de suivi d'utilisateur. Comme dans les chapitres précédents, à ce point de
départ, nous allons représenter les pages à l'aide de maquettes.1 Le flux de la page complète fonctionne comme suit : un utilisateur (ici
John Calvin) part de sa page de profil (illustration 12.1) et navigue sur les pages des autres utilisateurs (illustration 12.2) pour choisir un
utilisateur à suivre. John Calvin navigue sur la page de profil d'un second utilisateur, Thomas Hobbes (illustration 12.3), clique sur le
bouton « Suivre » pour suivre cet utilisateur. Cela change le bouton « Suivre » en bouton « Ne plus suivre », et incrémente de 1 le
nombre de « lecteurs » de Thomas Hobbes (illustration 12.4). Retournant à sa page d'accueil, John Calvin voit maintenant le nombre de
ses « auteurs suivis » et trouve les micro-messages de Thomas Hobbes dans son état d'alimentation (illustration 12.5). La suite de ce
chapitre est dévolu à faire fonctionner ce flux de page.
Illustration 12.1: Maquette de la page de profil de l'utilisateur courant. (taille normale)
Illustration 12.4: Maquette du profil avec un bouton pour arrêter de suivre l'utilisateur et incrémentation du nombre de
lecteurs. (taille normale)
Illustration 12.5: Maquette de la page d'accueil de l'utilisateur courant, avec l'état de l'alimentation et le compteur d'auteurs
suivis. (taille normale)
Cela suggère de modeler les utilisateurs suivis (following) comme dans l'illustration 12.6, avec une table following et une association
has_many. Puisque user.following devrait être une liste (array) d'utilisateurs, chaque rangée de la table following devrait être un
utilisateur, identifié par l'id followed_id (id_du_suivi), avec un id follower_id (id_du_suiveur) pour établir l'association.2 En plus,
puisque chaque rangée est censé être un utilisateur, nous aurions besoin d'inclure les autres attributs de l'utilisateur, nom, mot de passe,
etc.
Illustration 12.6: Implémentation naïve du suivi d'utilisateur.
Le problème avec le modèle de données de l'illustration 12.6 est qu'il est terriblement redondant : chaque rangée contient non seulement
chaque identifiant d'utilisateur suivi, mais aussi toutes ses autres informations — toute information qui se trouve déjà dans la table users.
Pire encore, pour modeler les « lecteurs » (followers), nous aurions besoin d'une autre table « lecteurs » (followers). Enfin, ce modèle de
données est un cauchemar à maintenir, puisque chaque fois qu'un utilisateur a changé (disons) son nom, nous avons besoin d'actualiser
non seulement l'enregistrement de l'utilisateur dans la table users mais aussi toutes les rangées contenant l'utilisateur dans nos deux
tables following et followers.
Notre problème ici est que nous sommes passés à côté d'une abstraction sous-jacente. Une façon de trouver l'abstraction adéquate
consiste à considérer l'implémentation possible de following dans l'application web. Rappelez-vous, section 6.3.3, que l'architecture
REST comprend des ressources qui sont créées et détruites. Cela nous conduit à nous poser deux questions : quand un utilisateur suit un
autre utilisateur, qu'est-ce qui est créé ? Quand un utilisateur ne suit plus un autre utilisateur, qu'est-ce qui est détruit ?
Après réflexion, nous voyons que dans ces cas l'application devrait soit créer ou détruire une relation (ou une connexion3) entre deux
utilisateurs. Un utilisateur, donc, « possèdes plusieurs :relations » (has_many :relationships), et possèdent plusieurs auteurs suivis
(following) et plusieurs lecteurs (followers) à travers ces relations. Effectivement, l'illustration 12.6 contient déjà le plus gros de
l'implémentation : puisque chaque utilisateur suivi est uniquement identifié par son followed_id, nous pourrions convertir following (les
lecteurs) en une table relationships (relations), omettre les détails de l'utilisateur, et utiliser l'identifiant followed_id (id_du_suivi) pour
retrouver l'utilisateur suivi dans la table users. Plus encore, en considérant la relation inverse, nous pourrions utiliser la colonne
follower_id (id_du_suiveur) pour extraire une liste des lecteurs de l'utilisateur.
Pour faire une liste des auteurs suivis par un utilisateur (following), il serait possible d'extraire une liste des attributs followed_id
(id_utilisateur_suivi) puis de garder chaque utilisateur associé à chaque identifiant relevé. Comme vous pouvez vous en douter
cependant, Rails possède une façon de rendre cette procédure bien plus pratique ; la technique en question est connue sous le nom
has_many :through (possède_plusieurs :à_travers).4 Comme nous le verrons dans la section 12.1.4, Rails nous permet de dire qu'un
utilisateur donné suit plusieurs utilisateurs à travers (through) une table de relations, en utilisant le code succint suivant :
has_many :following, :through => :relationships, :source => "followed_id"
Ce code peuple automatiquement user.following (utilisateur.auteurs_suivis) avec une liste (array) des utilisateurs suivis. Un diagramme
du modèle de données est présenté dans l'illustration 12.7.
Illustration 12.7: Modèle du suivi d'utilisateur à travers un modèle de relation intermédiaire. (taille normale)
Pour commencer l'implémentation, nous générons d'abord un modèle `Relationship` (Relations) comme suit :
$ rails generate model Relationship follower_id:integer followed_id:integer
Puisque nous devrons retrouver les relations grâce aux identifiants follower_id (id_lecteur) et followed_id (id_auteur_suivi), nous
devrions pour l'efficacité de la recherche ajouter une indexation sur chacune de ces colonnes, comme montré dans l'extrait 12.1.
Extrait 12.1. Ajout d'indexation à la table relationships.
db/migrate/<timestamp>_create_relationships.rb
t.timestamps
end
add_index :relationships, :follower_id
add_index :relationships, :followed_id
add_index :relationships, [:follower_id, :followed_id], :unique => true
end
def self.down
drop_table :relationships
end
end
L'extrait 12.1 inclut également un index composite qui force l'unicité des pairs de (follower_id, followed_id), de telle sorte que la
relation entre deux utilisateurs est forcément unique :
add_index :relationships, [:follower_id, :followed_id], :unique => true
(Comparez ce code à l'unicité de l'adresse mail de l'extrait 6.22.) Comme nous le verrons en commençant la section 12.1.4, notre
interface utilisateur ne permettra pas cela de se produire, mais ajouter cette unicité permet de générer une erreur si un utilisateur tente de
forcer la création d'une duplication de la relation (en utilisant par exemple un outil de commande en ligne comme cURL). Nous
pourrions aussi ajouter une validation d'unicité au modèle `Relationship`, mais puisque c'est toujours une erreur de créer la duplication
d'une relation, l'indexation unique est suffisante dans notre cas.
Pour créer la table relationships, nous migrons la base de données et préparons le test de cette base de données comme à notre
habitude :
$ rake db:migrate
$ rake db:test:prepare
Comme pour tout nouveau modèle, avant de poursuivre, nous devons définir ses attributs accessibles. Dans le cas du modèle
`Relationship`, l'attribut followed_id (id_utilisateur_suivi) devrait être accessible, puisque les utilisateurs vont créer des relations par le
biais du web, mais l'attribut follower_id (id_lecteur) ne doit pas l'être ; dans le cas contraire, un utilisateur mal intentionné pourrait
« forcer » des lecteurs à les suivre. Le résultat apparait dans l'extrait 12.2.
Extrait 12.2. Créer une relation avec un attribut followed_id accessible (mais pas follower_id).
app/models/relationship.rb
Comme avec les micro-messages dans la section 11.1.2, nous créerons de nouvelles relations en utilisant l'association d'utilisateur, avec
un code tel que :
user.relationships.create(:followed_id => ...)
Nous commençons avec un test, montré dans l'extrait 12.3, qui construit une variable d'instance @relationships (utilisé ci-dessous) en
nous assurant qu'elle puisse être enregistrée en utilisant la méthode save! (sauver!). Comme pour la méthode create! (créer!), la
méthode save! entraine une erreur (une exception) grâce au point d'exclamation si l'enregistrement échoue ; comparez cela à l'utilisation
de create! dans l'extrait 11.4.
Extrait 12.3. Test de la création de la Relation avec la méthode save!.
spec/models/relationship_spec.rb
require 'spec_helper'
describe Relationship do
before(:each) do
@follower = Factory(:user)
@followed = Factory(:user, :email => Factory.next(:email))
Nous devons également tester le modèle `User` pour vérifier l'existence de l'attribut relationships, comme dans l'extrait 12.4.
describe User do
.
.
.
describe "relationships" do
before(:each) do
@user = User.create!(@attr)
@followed = Factory(:user)
end
Arrivés à ce point, on pourrait s'attendre à ce que le code soit semblable à celui de la section 11.1.2, et il l'est, mais à une différence
majeure près : dans le cas du modèle `Micropost` (modèle des micro-messages) nous pouvions écrire :
class Micropost < ActiveRecord::Base
belongs_to :user
.
.
.
end
… et…
class User < ActiveRecord::Base
has_many :microposts
.
.
.
end
… parce que la table microposts (table des micro-messages) possède un attribut user_id pour identifier l'utilisateur (section 11.1.1). Un
identifiant utilisé de cette manière pour connecter les tables d'une base de données est connu sous le nom de clé étrangère (foreign key),
et puisque la clé étrangère pour le modèle `User` est user_id, Rails peut déduire l'association automatiquement : par défaut, Rails attend
une clé étrangère de la forme <class>_id, où <class> est la version minuscule du nom de la classe (donc la classe User implique la clé
étrangère user_id).5 Dans le cas présent, bien que nous traitions encore les utilisateurs, ils sont identifiés maintenant avec la clé étrangère
follower_id (id_utilisateur_lecteur), donc nous devons le dire explicitement à Rails, comme le montre l'extrait 12.5.6
À l'instar du modèle `Micropost`, le modèle `Relationship` possède avec les utilisateurs une relation belongs_to (appartient_à…) ; dans
ce cas, un objet `relationship` appartient d'une part à un utilisateur-lecteur (follower) et d'autre part à un utilisateur-auteur (followed), ce
que nous testons dans l'extrait 12.6.
describe Relationship do
.
.
.
describe "Méthodes de suivi" do
before(:each) do
@relationship.save
end
Pour écrire le code de l'application, nous définissons la relation belongs_to (appartient_à) normalement. Rails déduit les noms des clés
étrangères à partir des symboles correspondants (c'est-à-dire l'identifiant follower_id de :follower, et l'identifiant followed_id de
:followed), mais puisqu'il n'y a pas plus de modèle `Followed` que de modèle `Follower` nous avons aussi besoin de fournir le nom de
classe User. Le résultat est montré dans l'extrait 12.7.
L'association followed (utilisateur_suivi) n'est en fait pas nécessaire jusqu'à la section 12.1.5, même la construction parallèle
follower/followed est plus claire si nous implémentons les deux en même temps.
12.1.3 Validations
Avant de poursuivre, nous allons ajouter quelques validations au modèle `Relationship` pour le compléter. Les tests (extrait 12.8) et le
code de l'application (extrait 12.9) sont évidents.
describe Relationship do
.
.
.
describe "validations" do
12.1.4 Suivi
Nous en arrivons maintenant au cœur des associations `Relationship` : following (auteurs suivis) et followers (lecteurs). Nous
commençons avec following, comme dans l'extrait 12.10.
describe User do
.
.
.
describe "relationships" do
before(:each) do
@user = User.create!(@attr)
@followed = Factory(:user)
end
L'implémentation utilise has_many :through (possède_plusieurs :à_travers) pour la première fois : un utilisateur possède plusieurs
lecteurs à travers la relation, comme le montre l'illustration 12.7. Par défaut, dans une association has_many :through, Rails cherche une
clé étrangère correspondant à la version singulier de l'association ; en d'autres termes, un code comme :
has_many :followeds, :through => :relationships
… devrait assembler un tableau en utilisant le followed_id dans la table relationships. Mais, comme indiqué à la section 12.1.1,
user.followeds est plutôt maladroit ; il est de loin plus naturel de traiter « following » comme la forme plurielle de « followed », et
d'écrire plutôt user.following pour la table des utilisateurs suivis. Naturellement, Rails nous permet de sur-écrire la valeur par défaut,
dans ce cas en utilisant le paramètre :source (extrait 12.11), qui dit explicitement à Rails que la source de la table following est le set des
ids de followed.
Extrait 12.11. Ajout de l'association following du modèle User avec has_many :through.
app/models/user.rb
Pour créer la relation following, nous allons introduire une méthode utilisataire follow! de telle sorte que nous puissions écrire
user.follow!(other_user).7 Nous allons aussi ajouter une méthode booléen associée following? pour tester si un utilisateur en suit un
autre.8 Les tests dans l'extrait 12.12 montrent comment nous pouvons nous attendre à ce que ces méthodes soient utilisées dans la
pratique.
describe User do
.
.
.
describe "relationships" do
.
.
.
it "devrait avoir une méthode following?" do
@user.should respond_to(:following?)
end
Notez que nous avons remplacé la méthode include? vu dans l'extrait 11.31 par should include, en transformant effectivement :
@user.following.include?(@followed).should be_true
Cet exemple montre juste la flexibilité des conventions booléenne de RSpec ; même si include est déjà un mot-clé Ruby (utilisé pour
inclure un module, comme nous l'avons vu, par exemple, dans l'extrait 9.11), RSpec devine dans ce contexte que nous voulons tester
l'existence d'un élément dans une liste.
Dans le code de l'application, la méthode following? prend un utilisateur, appelle followed et vérifie si un follower (un lecteur) avec cet
identifiant existe dans la base de données ; la méthode follow! appelle create! à travers l'association relationships pour créer la relation
de suivi. Le résultat apparait dans l'extrait 12.13.9
def following?(followed)
relationships.find_by_followed_id(followed)
end
def follow!(followed)
relationships.create!(:followed_id => followed.id)
end
.
.
.
end
Notez que dans l'extrait 12.13 nous avons omis l'utilisateur lui-même, en écrivant juste :
relationships.create!(...)
self.relationships.create!(...)
L'utilisation d'un self explicite est largement une question de goût ici.
Bien entendu, les utilisateurs devraient être capables de ne plus suivre les autres utilisateurs autant que de les suivre, ce qui nous conduit
à la méthode quelque peu prévisible unfollow!, présentée dans l'extrait 12.14.10
Le code pour unfollow! est très simple : il trouve juste la relation par l'id followed et le détruit (extrait 12.15).11
Extrait 12.15. Arrêter le suivi d'un utilisateur en détruisant une relation utilisateur.
app/models/user.rb
def follow!(followed)
relationships.create!(:followed_id => followed.id)
end
def unfollow!(followed)
relationships.find_by_followed_id(followed).destroy
end
.
.
.
end
Illustration 12.9: Un modèle pour les utilisateurs lecteurs en utilisant un modèle Relationship inversé. (taille normale)
Nous commençons avec les tests, avec la certitude que la magie de Rails viendra à nouveau à notre aide (extrait 12.16).
Comme vous le suspectez certainement, nous ne ferons pas une table complète de base de données juste pour conserver la relation
inversée. Plutôt, nous exploiterons la symétrie sous-jaccente entre lecteurs et auteurs pour simuler une table reverse_relationships en
passant followed_id (auteur_id) comme clé primaire. En d'autres termes, là où l'association relationship utilise la clé étrangère
follower_id (lecteur_id) :
L'association followers (lecteurs) se contruit alors à travers la relationship inversée, comme le montre l'extrait 12.17.
(Comme dans l'extrait 12.5, le test de dependent :destroy est laissé en exercice (section 12.5).) Notez que nous avons en fait à inclure le
nom de classe pour cette association, c'est-à-dire :
Il est important de noter aussi que nous pourrions en fait omettre la clé :source dans ce cas, en utilisant simplement :
has_many :followers, :through => :reverse_relationships
… puisque Rails cherchera automatiquement une clé étrangère follower_id dans ce cas. J'ai gardé la clé :source pour mettre l'accent sur
le parallèle de structure avec l'association has_many :following, mais vous êtes libre de ne pas l'utiliser.
Avec le code de l'extrait 12.17, les associations lecteurs/auteur sont achevées, et tous les tests devraient réussir. Cette section a fait appel
de façon intensive à vos compétences en matière de modélisation de données, et il n'y aucun problème si vous mettez du temps à
assimiler ces concepts . En fait, l'une des meilleures façons de comprendre les associations est de les utiliser dans l'interface web, comme
nous le verrons dans la prochaine section.
Comme dans les chapitres précédents, il est très pratique d'utiliser une tâche Rake pour remplir la base de données d'échantillons de
relations. Cela nous permettra de concevoir l'aspect et la convivialité des pages web d'abord, différant les fonctionnalités de back-end
pour plus tard dans cette section.
La dernière fois que nous avons laissé le « peupleur » d'échantillons de données de l'extrait 11.20, il était devenu plutôt encombré, donc
nous allons commencer par définir des méthodes séparées pour créer des utilisateurs et des micro-messages, et ajouter alors des
échantillons de données de relations (relationships) en utilisant une nouvelle méthode make_relationships (creer_des_relations). Le
résultat est montré dans l'extrait 12.18.
Extrait 12.18. Ajout des relations auteurs/lecteur pour les échantillons de données.
lib/tasks/sample_data.rake
require 'faker'
namespace :db do
desc "Peupler la base de données avec des échantillons"
task :populate => :environment do
Rake::Task['db:reset'].invoke
make_users
make_microposts
make_relationships
end
end
def make_users
admin = User.create!(:nom => "Example User",
:email => "example@railstutorial.org",
:password => "foobar",
:password_confirmation => "foobar")
admin.toggle!(:admin)
99.times do |n|
nom = Faker::Nom.nom
email = "example-#{n+1}@railstutorial.org"
password = "password"
User.create!(:nom => nom,
:email => email,
:password => password,
:password_confirmation => password)
end
end
def make_microposts
User.all(:limit => 6).each do |user|
50.times do
content = Faker::Lorem.sentence(5)
user.microposts.create!(:content => content)
end
end
end
def make_relationships
users = User.all
user = users.first
following = users[1..50]
followers = users[3..40]
following.each { |followed| user.follow!(followed) }
followers.each { |follower| follower.follow!(user) }
end
def make_relationships
users = User.all
user = users.first
following = users[1..50]
followers = users[3..40]
following.each { |followed| user.follow!(followed) }
followers.each { |follower| follower.follow!(user) }
end
Nous nous arrangeons un peu arbitrairement pour que le premier utilisateur suive les 50 utilisateurs suivants, et puis faire que les
utilisateurs entre les identifiants de 4 à 41 suivent cet utilisateur en retour. Les relations en résultant sera suffisante pour développer
l'interface de l'application.
Pour exécuter le code de l'extrait 12.18, peuplons la base de données comme d'habitude :
$ rake db:populate
Un gros plan de la zone des statistiques, extrait de la maquette de l'illustration 12.1, est montré dans l'illustration 12.10. Ces statistiques
consistent en un compte des utilisateurs que l'utilisateur courant suit et du nombre d'utilisateurs qui le suivent, chacun d'eux avec un lien
vers la page d'affichage correspondante. Au chapitre 5, nous avons esquissé de tels liens avec un texte souche ’#’, mais c'était avant
d'avoir une expérience suffisantes des routes. Cette fois, bien que nous différerons les pages réelles à la section 12.2.3, nous allons faire
les routes maintenant, conformément à l'extrait 12.19. Ce code utilise la méthode :member à l'intérieur d'un bloc resource, que nous
n'avons jamais vu avant, mais voyons si vous pouvez deviner ce qu'il fait.
SampleApp::Application.routes.draw do
resources :users do
member do
get :following, :followers
end
end
.
.
.
end
Vous pouvez imaginer que les URLs pour les utilisateurs suivis (following) et suiveurs (followers) ressembleront à /users/1/following et
/users/1/followers et c'est exactement ce que fait le code dans l'extrait 12.19. Puisque les deux pages afficheront des données, nous
utilisons get pour faire en sorte que les URLs répondent à la requête GET (comme requis par la convention REST pour de telles pages), et
la méthode member signifie que les routes répondent aux URLs contenant l'id de l'utilisateur (l'autre possibilité, collection, fonctionne
sans id, de telle sorte que :
resources :users do
collection do
get :tigers
end
end
… devra répondre à l'URL /users/tigers — vraisemblablement pour afficher tous les tigres dans notre application. Pour plus de détails
sur de telles options de routage, voyez l'article du Rails Guides à propos de « Rails Routing from the Outside In »). Une table des routes
générées par l'extrait 12.19 est présentée dans la table 12.1 ; notez les routes nommées pour les pages des auteurs suivis (following) et
lecteurs (followers) que nous serons amenés à utiliser dans un instant.
Les routes une fois définies, nous sommes maintenant en mesure de construire les tests pour le partiel statistiques (nous aurions pu écrire
les tests d'abord, mais les routes nommées auraient été difficiles à motiver sans le fichier routes actualisé). Nous pourrions écrire les tests
pour la page de profil de l'utilisateur, puisque le partiel des statistiques apparaitra là, mais il apparaitra aussi sur la page d'accueil, et c'est
une bonne opportunité pour restructurer les tests de la page d'accueil pour prendre en compte les utilisateur identifiés. Le résultat apparait
dans l'extrait 12.20.
describe PagesController do
render_views
before(:each) do
@base_titre = "Ruby on Rails Tutorial Sample App"
end
.
.
.
describe "GET 'home'" do
before(:each) do
get :home
end
it "devrait réussir" do
response.should be_success
end
before(:each) do
@user = test_sign_in(Factory(:user))
other_user = Factory(:user, :email => Factory.next(:email))
other_user.follow!(@user)
end
Le cœur de ce test est l'attente que le compte de lecteurs et d'auteurs suivis apparaisse sur la page, accompagnés des bonnes URLs :
Nous avons utilisé ici la route nommée de la table 12.1 pour vérifier que les liens avaient les bons URLs.
Le code de l'application pour le partiel des statistiques est simplement une table HTML à l'intérieur d'un div comme dans l'extrait 12.21.
Ici les comptes utilisateur de lecteurs et d'auteurs suivis sont calculés à travers l'association en utilisant :
@user.following.count
… et :
@user.followers.count
Comparez ceci aux comptes de micro-messages de l'extrait 11.16, où nous avions écrit :
@user.microposts.count
Comme discuté dans la Box 9.4, cela ne fait rien quand @user n'est pas nil (comme sur la page de profil), mais quand il l'est (comme sur
la page d'accueil), il règle la valeur de @user à l'utilisateur courant.
Un détail final qui vaut le peine d'être noté : la présence des ids CSS sur certains éléments, comme dans :
Ceci anticipe l'implémentation Ajax de la section 12.2.5, qui accède aux éléments de la page en utilisant leur identifiant unique (id).
Avec le partiel en main, inclure les statistiques sur la page d'accueil est facile, comme montré dans l'extrait 12.22 (cela doit également
faire réussir le test de l'extrait 12.20). Le résultat est présenté dans l'illustration 12.11.
Illustration 12.11: La page d'accueil (/) avec les statistiques de suivi. (taille normale)
Nous « rendrons » le partiel des statistiques sur la page de profil dans un moment, mais construisons avant ça un partiel pour le bouton
suivre/ne plus suivre dont le code apparait dans l'extrait 12.23.
Cela ne fait rien d'autre que confier le vrai travail aux partiels follow (suivre) et unfollow (ne plus suivre), qui ont besoin de nouvelles
routes avec des règles pour la ressource Relationships, qui suivent l'exemple de la ressource Microposts (extrait 11.21), comme présenté
dans l'extrait 12.24.
SampleApp::Application.routes.draw do
.
.
.
resources :sessions, :only => [:new, :create, :destroy]
resources :microposts, :only => [:create, :destroy]
resources :relationships, :only => [:create, :destroy]
.
.
.
end
Le code des partiels suivre/ne plus suivre eux-mêmes est présenté dans l'extrait 12.25 et l'extrait 12.26.
Ces formulaires utilisent tous deux form_for pour manipuler un objet de modèle Relationship ; la différence principale entre les deux est
que l'extrait 12.25 construit une nouvelle relation, tandis que l'extrait 12.26 cherche la relation existente. Naturellement, la première
envoie une requête POST au contrôleur Relationships pour créer (create) une relation, tandis que la seconde envoie une requête DELETE
pour détruire (destroy) une relation (nous implémenterons ces actions à la section 12.2.4). Enfin, vous noterez que le formulaire
suivre/ne plus suivre n'a aucun contenu autre que le bouton, mais il a encore besoin d'envoyer le followed_id, ce que nous accomplissons
avec hidden_field (champ caché) ; cela produit un code HTML de la forme :
… qui place l'information voulu sur la page sans l'afficher sur la page du navigateur.
Nous pouvons maintenant inclure le formulaire de suivi et les statistiques de suivi sur la page de profil simplement en rendant les
partiels, comme montré dans l'extrait 12.27. Les profils avec les boutons « suivre » et « ne plus suivre », respectivement, apparaissent
dans l'illustration 12.12 et l'illustration 12.13.
Extrait 12.27. Ajout du formulaire de suivi et des statistiques de suivi à la page profil de l'utilisateur.
app/views/users/show.html.erb
Illustration 12.12: Un profil d'utilisateur avec un bouton « suivre » (/users/8). (taille normale)
Illustration 12.13: Un profil d'utilisateur avec un bouton « ne plus suivre » (/users/6). (taille normale)
Nous obtiendrons le fonctionnement de ces boutons bien assez tôt — en fait, nous le ferons de deux manières, la stardard (section 12.2.4)
et celle utilisant Ajax (section 12.2.5) — mais d'abord nous allons achever le code HTML de l'interface en construisant les pages des
lecteurs et des auteurs suivis par l'utilisateur.
Illustration 12.14: Une maquette de la page des auteurs suivis par l'utilisateur courant. (taille normale)
Illustration 12.15: Une maquette de la page des lecteurs de l'utilisateur courant. (taille normale)
Notre première étape consiste à faire fonctionner les liens vers les auteurs suivis (following) et les lecteurs (followers). Nous suivrons le
modèle de Twitter et devrons exiger une identification pour les deux pages. Pour les utilisateurs identifiés, les pages devraient posséder
des liens respectifs pour les auteurs suivis et les lecteurs. L'extrait 12.28 exprime ces attentes en code.12
Extrait 12.28. Test pour les actions following (auteurs suivis) et followers (lecteurs).
spec/controllers/users_controller_spec.rb
describe UsersController do
.
.
.
describe "Les pages de suivi" do
before(:each) do
@user = test_sign_in(Factory(:user))
@other_user = Factory(:user, :email => Factory.next(:email))
@user.follow!(@other_user)
end
La seule partie délicate de l'implémentation est de concevoir que nous avons besoin d'ajouter deux nouvelles actions au contrôleur
Users ; en s'appuyant sur les routes définies dans l'extrait 12.19, nous avons besoin de les appeler following (auteurs suivis) et followers
(lecteurs). Chaque action a besoin de définir un titre, trouver un utilisateur, récupérer soit @user.following ou @user.followers (dans une
forme paginée), et enfin de rendre la page attendue. Le résultat est présenté dans l'extrait 12.29.
def followers
@titre = "Followers"
@user = User.find(params[:id])
@users = @user.followers.paginate(:page => params[:page])
render 'show_follow'
end
.
.
.
end
Notez ici que les deux actions font un appel explicite à render, dans ce cas pour rendre une vue appelée show_follow (montrer_le_suivi)
que nous devons créer. La raison de cette vue commune est que le ERb est très proche dans les deux cas, et l'extrait 12.30 les couvre les
deux.
Extrait 12.30. La vue show_follow utilisée pour rendre les auteurs suivis et les lecteurs.
app/views/users/show_follow.html.erb
Il y a un second détail dans l'extrait 12.29 qui vaut la peine d'être noté : dans le but de protéger les pages des auteurs suivis et des lecteurs
d'un accès non autorisé, nous avons changé les authentifications d'avant fitrage pour utiliser :except plutôt que :only. Jusqu'ici dans ce
manuel, nous avons utilisé :only pour indiquer sur quelles actions le filtre devait s'appliquer ; avec l'addition des nouvelles actions
protégées, la balance a penché, et il est plus simple à présent d'indiquer quelles actions ne doivent pas être filtrées. Nous faisons cela avc
l'option :except pour le authenticate d'avant filtrage :
before_filter :authenticate, :except => [:show, :new, :create]
Avec ça, les tests devraient maintenant réussir, et les pages devraient être rendues comme dans l'illustration 12.16 (auteurs suivis) et
l'illustration 12.17 (lecteurs).
Illustration 12.16: Afficher les utilisateurs qui sont suivis par l'utilisateur courant. (taille normale)
Vous avez peut-être noter que même avec le partiel commun show_followers, les actions following et followers ont toujours beaucoup
de code dupliqué. Plus encore, le partiel show_followers lui-même partage des fonctionnalités avec la page d'affichage de l'utilisateur. La
section 12.5 inclut des exercices pour éliminer ces duplications.
require 'spec_helper'
describe RelationshipsController do
before(:each) do
@user = test_sign_in(Factory(:user))
@followed = Factory(:user, :email => Factory.next(:email))
end
before(:each) do
@user = test_sign_in(Factory(:user))
@followed = Factory(:user, :email => Factory.next(:email))
@user.follow!(@followed)
@relationship = @user.relationships.find_by_followed_id(@followed)
end
… simule la soumission du formulaire avec les champs cachés (hidden) donnés par :
Le code du contrôleur nécessaire pour faire réussir ces tests est remarquablement concis : nous récupérons juste l'utilisateur suivi ou
devant être suivi, et suivons ou ne-suivons-plus l'utilisateur en utilisant la méthode utilitaire concernée. L'implémentation complète
apparait dans l'extrait 12.32.
def create
@user = User.find(params[:relationship][:followed_id])
current_user.follow!(@user)
redirect_to @user
end
def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow!(@user)
redirect_to @user
end
end
Avec ça, le cœur de la fonctionnalité suivre/ne plus suivre est achevé, et n'importe quel utilisateur peut suivre (ou ne plus suivre)
n'importe quel autre utilisateur.
C'est exactement le genre de problème que peut résoudre Ajax, qui permet aux pages web d'envoyer des requêtes de façon asynchrone à
un serveur sans quitter la page.13 Ajouter de l'Ajax à des formulaires web étant plutôt courant aujourd'hui, Rails le rend facile à
implémenter. Actualiser les formulaires suivre/ne-plus-suivre des partiels est même particulière aisé ; changez seulement :
form_for
… en :
… et Rails automagiquement utilise Ajax.14 Les partiels actualisés sont présentés dans l'extrait 12.33 et l'extrait 12.34.
Le code HTML généré par ce ERb n'est pas très expressif, mais par curiosité, jetons-y un coup d'œil :
Cela définit la variable data-remote="true" à l'intérieur de la balise du formulaire (form), qui demande à Rails de permettre au formulaire
d'être traité par JavaScript. En utilisant une simple propriété HTML au lieu d'insérer du code Javascript (comme dans les précédentes
versions de Rails), Rails 3 suit la philosophie du JavaScript non intrusif.
Ayant actualisé le formulaire, nous avons besoin maintenant de faire que le contrôleur Relationship réponde aux requêtes Ajax. Nous
allons commencer avec une simple paire de tests. Tester Ajax est quelque peu délicat, et le faire à fond est un sujet à part entière, très
vaste, mais nous pouvons commencer avec le code de l'extrait 12.35. Il utilise la méthode xhr (pour « XmlHttpRequest ») pour lancer
une requête Ajax ; comparez-la aux méthodes get, post, put et delete utilisées dans les précédents tests. Nous vérifions alors que les
actions create et destroy fassent les choses attendues quand on lance les requêtes Ajax (pour écrire une suite de tests plus profonde pour
les applications utilisant intensivement Ajax, jetez un œil à Selenium et Watir).
Extrait 12.35. Tests pour les réponses du contrôleur Relationships aux requêtes Ajax.
spec/controllers/relationships_controller_spec.rb
describe RelationshipsController do
.
.
.
describe "POST 'create'" do
.
.
.
it "devrait créer une relation en utilisant Ajax" do
lambda do
xhr :post, :create, :relationship => { :followed_id => @followed }
response.should be_success
end.should change(Relationship, :count).by(1)
end
end
Comme le demande les tests, le code de l'application utilise les mêmes actions create et delete pour répondre aux requêtes Ajax que
celles pour répondre aux requêtes HTML ordinaires POST et DELETE. Tout ce que nous avons à faire est de répondre à une requête HTML
normale avec une redirection (comme à la section 12.2.4) et répondre à une requête Ajax avec JavaScript.15 Le code du contrôleur est
présenté dans l'extrait 12.36 (voyez plus loin à la section 12.5 un exercice montrant une façon même plus compact d'accomplir la même
chose).
def create
@user = User.find(params[:relationship][:followed_id])
current_user.follow!(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow!(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
end
Ce code utilise respond_to pour prendre l'action adaptée au type de requête.16 La syntaxe est potentiellement déroutante, et il est
important de comprendre que dans :
respond_to do |format|
format.html { redirect_to @user }
format.js
end
Dans le cas d'une requête Ajax, Rails appelle automatiquement un fichier de code Ruby embarqué JavaScript (.js.erb) portant le même
nom que l'action, c'est-à-dire create.js.erb ou destroy.js.erb. Comme vous pouvez vous en douter, les fichiers nous permettent de
mixer du JavaScript et du Ruby embarqué pour exécuter des actions sur la page courante. Ce sont ces fichiers que nous devons créer et
modifier dans le but d'actualiser la page de profil de l'utilisateur après la demande de suivi (ou de non-suivi).
À l'intérieur d'un fichier JS-ERb, Rails fournit automatiquement les helpers Prototype JavaScript pour manipuler la page en utilisant le
Document Object Model (DOM). Le framework Prototype fournit un grand nombre de méthodes pour manipuler le DOM, mais ici nous
n'aurons besoin que de deux de ces méthodes. D'abord, nous avons besoin de connaitre la syntaxe Prototype dollar (« $ ») pour accéder à
un élément DOM qui se base sur l'id CSS unique. Par exemple, pour manipuler l'élément d'identifiant follow_form, nous devrons utiliser
la syntaxe :
$("follow_form")
(Souvenez-vous — extrait 12.23 — que c'est un div qui entoure le formulaire, pas la balise form elle-même.) La seconde méthode dont
nous avons besoin est update, qui actualise le code HTML à l'intérieur de l'élément concerné avec le contenu de son argument. Par
exemple, pour remplacer entièrement le formulaire de suivi par la chaine de caractère "foober", nous devons écrire :
$("follow_form").update("foobar")
Contrairement aux fichiers en « plain JavaScript », les fichiers JS-ERb permettent aussi d'utiliser du Ruby embarqué, ce que nous
appliquons dans le fichier create.js.erb pour actualiser le formulaire de suivi avec le partiel unfollow (qui devrait s'afficher après la
réussite d'une définition de suivi) et actualiser le compte de lecteurs. Le résultat est montré dans l'extrait 12.37.
Extrait 12.37. Le code Ruby embarqué JavaScript pour créer une relation de suivi.
app/views/relationships/create.js.erb
Le fichier destroy.js.erb est analogue (extrait 12.38). Notez que, comme dans l'extrait 12.37, nous devons utiliser escape_javascript
pour « échapper » le résultat en insérant le code HTML.
Extrait 12.38. Le code Ruby JavaScript (RJS) pour détruire une relation de suivi.
app/views/relationships/destroy.js.erb
Avec cela, nous devrions naviguer vers la page de profil de l'utilisateur et vérifier que vous pouvez suivre ou arrêter de suivre sans que la
page n'ait besoin d'être rafraichie.
L'utilisation d'Ajax en Rails est un vaste sujet qui évolue sans cesse, donc nous ne pourrons qu'en effleurer la surface ici, mais (comme
pour le reste du matériel abordé dans ce tutoriel) notre traitement vous fournit de bonnes bases pour étudier ensuite des ressources plus
avancées. Il est spécialement important de noter que, en addition du framework Prototype, le framework JavaScript jQuery connait
beaucoup d'attraction dans la communauté Ruby. L'implémentation des fonctions Ajax de cette section en utilisant jQuery est laissé
comme exercice ; voyez la section 12.5.
En prévision des lourdes charges que nous allons avoir à porter, il est spécialement important d'avoir une vision claire de là où nous
allons. Une maquette finale de l'état de l'alimentation de l'utilisateur, contruite d'après le proto-feed de la section 11.3.3, est présentée
dans l'illustration 12.18.
Illustration 12.18: Une maquette de la page d'accueil de l'utilisateur avec un état de l'alimentation (version anglaise). (taille normale)
Illustration 12.19: L'alimentation pour un utilisateur (id 1) suivant les utilisateurs d'id 2, 7, 8 et 10.
Puisque nous avons besoin d'une manière de trouver tous les micro-messages des utilisateurs suivis par un utilisateur donné, nous allons
projeter d'implémenter une méthode appelée from_users_followed_by, que nous utiliserons comme suit :
Micropost.from_users_followed_by(user)
Bien que nous ne sachions pas encore comment l'implémenter, nous pouvons déjà écrire des tests pour from_users_followed_by, comme
dans l'extrait 12.39.
describe Micropost do
.
.
.
describe "from_users_followed_by" do
before(:each) do
@other_user = Factory(:user, :email => Factory.next(:email))
@third_user = Factory(:user, :email => Factory.next(:email))
@user.follow!(@other_user)
end
it "ne devrait pas inclure les micro-messages des utilisateurs non suivis" do
Micropost.from_users_followed_by(@user).should_not include(@third_post)
end
end
end
La clé ici est de construire des associations dans le bloc before(:each) et ensuite de tester les trois exigences : inclusion des micro-
messages des utilisateurs suivis, des micro-messages de l'utilisateur courant et exclusion des micro-messages des utilisateurs non suivis.
L'alimentation elle-même se trouve dans le modèle User (section 11.3.3), donc nous devrons ajouter un test additionnel aux specs du
modèle User de l'extrait 11.31, comme montré dans l'extrait 12.40 (notez que nous avons remplacé ici l'utilisation de include? de
l'extrait 11.31 par le code plus compact include, convention introduite dans l'extrait 12.12).
describe User do
.
.
.
describe "association micro-messages" do
.
.
.
describe "état de l'alimentation" do
Implémenter l'alimentation sera facile ; nous la confierons simplement à Micropost.from_users_followed_by, comme le montre
l'extrait 12.41.
La première étape consiste à réfléchir au type de requête dont nous avons besoin. Ce que nous voulons faire, c'est sélectionner de la table
microposts tous les micro-messages avec des identifiants de suivi correspondant à l'utilisateur donné (ou l'utilisateur lui-même). Nous
pouvons écrire cela, schématiquement, comme suit :
SELECT * FROM microposts
WHERE user_id IN (<list of ids>) OR user_id = <user id>
En écrivant ce code, nous devinons que SQL supporte un mot-clé IN qui nous permet de tester la définition de l'inclusion (et il le fait !).
Souvenez-vous, d'après la proto-alimentation de la section 11.3.3, que Active Record utilise la méthode where pour accomplir ce type de
sélection vue ci-dessus, comme illustré dans l'extrait 11.32. Là, notre sélection est très simple ; nous récupérons juste les micro-messages
ayant un user_id (identifiant_dutilisateur) correspondant à l'utilisateur courant :
(Ici nous avons utilisé la convention Rails user plutôt que user.id dans la condition ; Rails utilise automatiquement l'attribut id. Nous
avons également omis le début Micropost. puisque nous comptons placer cette méthode dans le modèle Micropost lui-même.)
Nous tirons de ces conditions que nous aurons besoin d'un tableau (array) d'ids que l'utilisateur donné suit (ou quelque chose
d'équivalent). Une façon de le faire consiste à utiliser la méthode Ruby map, utilisable sur un objet de type « enumerable », c'est-à-dire
n'importe quel objet (tel qu'un Array — Tableau — ou un Hash — Table de hachage —) constitué d'une collection d'éléments.17 Nous
avons vu un exemple de cette méthode à la section 4.3.2 ; elle fonctionne comme cela :
$ rails console
>> [1, 2, 3, 4].map { |i| i.to_s }
=> ["1", "2", "3", "4"]
Des situations comme celle illustrée ci-dessus, où la même méthode (par exemple to_s) sera invoquée sur chaque élément, est assez
courante pour avoir une notation raccourcie utilisant une esperluette « & » et un symbole correspondant à la méthode :18
Nous pouvons utiliser cette notation pour construire le tableau nécessaire des ids des utilisateurs suivis en appelant id sur chaque élément
de user.following. Par exemple, pour le premier utilisateur dans la base de données ce tableau apparait comme suit :
>> User.first.following.map(&:id)
=> [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
43, 44, 45, 46, 47, 48, 49, 50, 51]
Micropost.from_users_followed_by(user)
… implique une méthode de classe dans la classe Micropost (une construction vue la dernière fois dans la classe User à la section 7.12).
Une mise en œuvre pour aller dans ce sens est présentée dans l'extrait 12.42.
Bien que les discussions conduisant à l'extrait 12.42 ait été formulées en termes hypothétiques, cela fonctionne, en vérité ! En fait, c'est
assez efficace pour la plupart des cas pratiques. Mais ce n'est pas l'implémentation finale ; voyons si vous pouvez deviner pourquoi avant
de passer à la section suivante (indice : qu'en est-il si un utilisateur suit 5000 autres utilisateurs ?)
… place tous les utilisateurs suivis en mémoire, et crée un tableau de la longueur totale de la liste de suivis. Puisque la condition dans
l'extrait 12.42 vérifie en fait seulement l'inclusion dans une définition, il doit exister une façon plus efficace de le faire, et en effet SQL
est justement optimisée pour de telles opérations. Ensuite, la méthode de l'extrait 12.42 récupère toujours tous les micro-messages et les
fourre dans un tableau Ruby. Bien que ces micro-messages soient paginés dans la vue (extrait 11.33), le tableau est toujours plein.19 Ce
que nous voulons réellement, c'est une pagination qui ne récupère que 30 items à la fois.
La solution pour ces deux problèmes implique de convertir l'alimentation de la méthode de classe vers un champ d'application (un
scope), qui est une méthode Rails pour restreindre les sélections dans la base de données à certaines conditions. Par exemple, pour qu'une
méthode sélectionne tous les utilisateurs administrateurs de notre application, nous pourrions ajouter un champ d'application au modèle
User comme suit :
La raison principale qui fait que les champs d'application sont meilleurs que les méthodes de classe « pleines » (plain class methods) est
qu'elle peuvent être chainées à d'autres méthodes, de telle sorte que, par exemple :
User.admin.paginate(:page => 1)
… pagine en fait les administrateurs dans la base de données ; si (pour quelque raison bizarre) le site possède 100 administrateurs, le
code ci-dessus ne récupérera que les 30 premiers.
Le champ d'application pour l'alimentation est un tout petit peu plus complexe que celui illustré ci-dessus : il a besoin d'un argument,
nommément, l'utilisateur dont nous devons générer l'alimentation. Nous pouvons faire cela avec une fonction anonyme, ou fonction
lambda (dont nous avons parlé à la section 8.4.2), comme le montre l'extrait 12.43.20
private
# Retourne une condition SQL pour les utilisateurs suivis par un utilisateur donné.
# Nous incluons aussi les propres micro-messages de l'utilisateur.
def self.followed_by(user)
followed_ids = user.following.map(&:id).join(", ")
where("user_id IN (#{followed_ids}) OR user_id = :user_id",
{ :user_id => user })
end
end
Puisque les conditions sur le champ d'application de from_users_followed_by sont plutôt longues, nous avons défini une fonction
auxiliaire pour les traiter :
def self.followed_by(user)
followed_ids = user.following.map(&:id).join(", ")
where("user_id IN (#{followed_ids}) OR user_id = :user_id",
{ :user_id => user })
end
La syntaxe par point d'interrogation est bien, mais quand nous voulons insérer la même variable à différents endroits, la seconde syntaxe,
utilisant une table, est plus pratique.
Box 12.1.Pourcentage-Parenthèses
Vous pouvez penser %() comme équivalent aux guillemets, mais capable de faire des chaines de caractères multi-lignes (si vous avez
besoin d'un façon de produire une chaine de caractères multi-lignes sans espaces-blancs initiaux, faire une recherche Google sur les
termes « ruby here document »). Puisque %() supporte l'interpolation de chaine, il est particulièrement utile quand vous avez besoin
d'utiliser des guillemets dans une chaine et de l'interpoler en même temps. Par exemple, le code :
… produit :
Pour obtenir le même résultat avec des chaines entre guillemets, vous auriez besoin d'échapper les guillemets internes avec des « \ »
comme dans :
>> "La variable \"foo\" est égale à \"#{foo}\"."
Dans ce cas, la syntaxe %() est plus pratique puisque elle vous donne le même résultat sans échappement explicite.
La discussion ci-dessus implique d'ajouter une seconde occurrence de user_id dans la requête SQL, et en effet c'est le cas. Nous pouvons
remplacer le code Ruby :
(Lisez le Box 12.1 pour une explication de la syntaxe %()). Ce code contient une sous-selection SQL et, « internalement », l'entière
sélection pour l'utilisateur 1 ressemblerait à quelque chose comme :
Cette sous-sélection s'arrange pour que la définition logique soit imposée à l'intérieur de la base de données, ce qui est plus efficace.21
Sur cette base, nous sommes prêt pour l'implémentation finale de l'alimentation, comme vu dans l'extrait 12.44.
private
# Renvoie une condition SQL pour les utilisateurs suivis par l'utilisateur donné.
# Nous incluons aussi ses propres micro-messages.
def self.followed_by(user)
followed_ids = %(SELECT followed_id FROM relationships
WHERE follower_id = :user_id)
where("user_id IN (#{followed_ids}) OR user_id = :user_id",
{ :user_id => user })
end
end
Ce code met en branle une redoutable combinaison de Rails, Ruby et SQL, mais il accomplit le boulot, et le fait bien.22
def home
@titre = "Accueil"
if signed_in?
@micropost = Micropost.new
@feed_items = current_user.feed.paginate(:page => params[:page])
end
end
.
.
.
end
Illustration 12.20: La page d'accueil avec un état d'alimentation fonctionnel (version anglaise). (taille normale)
12.4 Conclusion
Avec l'addition d'un état d'alimentation, nous en avons terminé avec le cœur de l'Application Exemple de ce Tutoriel Ruby on Rails.
L'application inclut des exemples de toutes les fonctionnalités principales de Rails, comme les modèles, les vues, les contrôleurs, les
templates, les partiels, les filtres, les validations, les fonctions de rappel, les associations has_many/belongs_to et has_many :through, la
sécurité, le testing et le déploiement. Malgré cette liste impressionnante, il y a beaucoup plus à apprendre sur Rails. Comme première
étape dans la poursuite de cet apprentissage, cette section propose quelques extensions à faire au cœur de votre application, ainsi que des
suggestions d'apprentissages ultérieurs.
Avant de commencer à aborder ces extensions de l'application, c'est une bonne idée de fusionner vos changements et de déployer
l'application online :
$ git add .
$ git commit -m "Ajout du suivi des utilisateurs"
$ git checkout master
$ git merge following-users
$ git push heroku
$ heroku rake db:migrate
Ne vous étonnez pas si tout semble compliqué au premier abord ; la non connaissance d'une nouvelle fonctionnalité peut être très
intimidant. Pour vous aider à commencer, laissez-moi vous donner quelques précieux conseils. Primo, avant d'ajouter une fonctionnalité
à une application Rails, jetez une coup d'œil aux archives Railscasts pour voir si Ryan Bates n'aurait pas déjà couvert le sujet.25 Si ce
railscast existe, commencer par le regarder peut vous faire économiser un temps considérable. Secondo, faites toujours des recherches
Google sur la fonctionnalité que vous vous proposez d'installer pour trouver tous les posts de blog et les tutoriels concernés. Le
développement d'application web est difficile, et cela peut aider d'apprendre à partir de l'expérience (et des erreurs) d'autrui.
Beaucoup des fonctionnalités suivantes constituent un véritable challenge, et j'ai donné quelques indices sur les outils dont vous pourriez
avoir besoin pour les implémenter. Même avec ces indices, elles sont beaucoup plus compliquées que les exercices concluant les
chapitres de ce livre, donc ne vous découragez surtout pas si vous ne parvenez pas à les implémenter qu'au prix d'un effort considérable.
Les contraintes du temps ne m'offrent pas la possibilité de faire de l'assistance personnalisée, mais si votre demande peut avoir un intérêt
significatif, je pourrais peut-être réaliser dans le futur des articles ou des screencasts autonomes sur certaines de ces extensions ; rendez-
vous sur le site web du tutoriel Rails, à l'adresse http://www.railstutorial.org/ et souscrivez à la lettre d'information pour être informé des
prochaines actualisations.
Réponses
Twitter permet à ses utilisateurs de faire des « @réponses » (replies), qui sont des micro-messages dont les premiers caractères sont le
login de l'utilisateur précédé du signe arobase « @ ». Ces posts n'apparaissent que sur l'alimentation de l'utilisateur en question ou les
utilisateurs qui suivent cet utilisateur. Implémentez une version simplifiée, en restreignant l'affichage des @replies à la seule alimentation
du receveur (l'utilisateur qui reçoit le message) et de l'émetteur (l'utilisateur qui a écrit la réponse). Cela peut impliquer d'ajouter une
colonne in_reply_to dans la table microposts et un champ d'application (scope) including_replies supplémentaire au modèle
Micropost.
Puisque le utilisateur de votre application n'ont pas de logins uniques, vous devrez aussi décider d'une façon de les représenter. Une
option consiste à utiliser une combinaison de l'id et du nom, tel que @1-michael-hartl. Une autre consiste à ajouter un username unique au
processus d'inscription et de l'utiliser ensuite en @replies.
Messagerie
Twitter supporte la messagerie directe (et privée) en préfixant un micro-message avec la lettre « d ». Implémentez cette fonctionnalité à
l'Application Exemple. La solution impliquera certainement un modèle Message et une recherche régulière sur les nouveaux micro-
messages.
Notifications de suivi
Implémentez une fonctionnalité pour envoyer un mail aux utilisateurs quand ils reçoivent de nouveaux lecteurs. Rendez alors cet envoi
optionnel, de telle sorte que les utilisateurs puissent le suspendre s'ils le souhaitent.
Entre autres choses, ajouter cette fonctionnalité requiert d'apprendre à envoyer un mail avec Rails. Il existe un Railscast on sending email
pour vous aider à commencer. Soyez informé que la librairie Rails principale pour envoyer des emails, Action Mailer, a connu une
importante révision avec Rails 3 comme le montre le Railscast on Action Mailer in Rails 3.
En général, si les utilisateurs de votre application oublient leur mot de passe, ils n'ont aucun moyen de le retrouver. À cause du hachage à
sens unique du mot de passe sécurisé du chapitre 7, notre application ne peut pas renvoyer par email le mot de passe de l'utilisateur, mais
il peut lui envoyer un lien vers un formulaire d'initialisation. Introduisez une ressource PasswordReminders pour implémenter cette
fonctionnalité. Pour chaque initialisation, vous devriez créer un « jeton unique » (unique token) et l'envoyer à l'utilisateur par mail.
Visiter l'URL avec ce jeton devrait leur permettre de ré-initialiser leur mot de passe avec une valeur de leur choix.
Confirmation de l'inscription
Mise à part la vérification par expression régulière de l'adresse email, l'Application Exemple n'a pour le moment aucun moyen de vérifier
la validité d'une adresse mail d'utilisateur. Ajoutez une vérification de cette adresse pour confirmer l'inscription. Cette nouvelle
fonctionnalité devrait créer des utilisateurs dans un état « inactif », envoyer une URL d'activation et changer alors leur état vers « actif »
à la visite de l'URL. Vous pourriez faire une recherche sur les termes state machines in Rails pour vous aider à exécuter la transition
inactif/actif.
Alimentation RSS
Pour chaque utilisateur, implémentez une alimentation RSS pour leurs micro-messages. Implémentez ensuite une alimentation RSS pour
leur état d'alimentation, en restreignant optionnellement l'accès à cette alimentation en utilisant un système d'authentification. Le
Railscast on generating RSS feeds vous aidera à commencer.
API REST
De nombreux sites proposent une API (Application Programmer Interface, pour « Interface de programmation ») pour qu'une partie
tierce puisse obtenir (get), poster (post), placer (put) et effacer (delete) les ressources de l'application. Implémentez une telle API REST
pour votre Application Exemple. La solution impliquera d'ajouter des blocs respond_to (section 12.2.5) à de nombreuses actions de
contrôleur de l'application ; celles-ci devraient répondre aux requêtes en XML. Attention au problème de sécurité ; l'API devrait être
uniquement accessible aux seuls utilisateurs autorisés.
Recherche
Pour le moment, il n'y a pas d'autres moyens pour les utilisateurs de se retrouver que de consulter la page d'index ou de consulter les
alimentations des autres utilisateurs. Implémentez une fonctionnalité de recherche pour y remédier. Ajoutez alors une autre fonctionnalité
de recherche sur les micro-messages. Le Railscast on simple search forms vous aidera à commencer. Si vous déployez votre application
en utilisant un hôte partagé ou un serveur dédié, je vous suggère d'utiliser Thinking Sphinx (en suivant Railscast on Thinking Sphinx). Si
vous déployez l'application sur Heroku, vous devriez suivre les instructions de Heroku full text search.
Ruby on Rails Tutorial screencasts : j'ai préparé un cours complet par screencast s'appuyant sur ce livre. En addition pour couvrir
tout le matériel de ce livre, les screencasts sont plein d'astuces, de trucs, et de démos « voyez-comment-ça-fonctionne » qui sont
difficiles de rendre par l'écrit. Ils sont accessibles sur le Ruby on Rails Tutorial website, par Safari Books Online et par InformIT.
Railscasts : je ne saurais trop insister sur la grande qualité des Railscasts. Je suggère de commencer en visitant le site Railscasts
episode archive et de cliquer sur le premier sujet qui retiendra votre attention.
Scaling Rails : l'un des sujets que nous n'avons presque pas abordé dans ce Tutoriel Ruby on Rails concerne la performance,
l'optimisation et la mise à l'échelle (scaling). Heureusement, la plupart des sites ne rencontrent pas des problèmes de scaling, et
utiliser quoi que ce soit au-delà de Rails serait probablement de l'optimisation prématurée. Si vous rencontrez des problèmes de
performance, la série Scaling Rails par Gregg Pollack du Envy Labs est un bon endroit par où commencer. Je recommande aussi
de visiter les applications de surveillance de site Scout et New Relic.26 Et, comme vous pouvez vous en douter maintenant, il
existe des Railscasts sur ces sujets, parmi lesquels le profilage, la mise en cache, et les tâches de fond.
Livres Ruby and Rails : comme mentionné au chapitre 1, je recommande Beginning Ruby par Peter Cooper, The Well-Grounded
Rubyist par David A. Black et The Ruby Way par Hal Fulton, ainsi que The Rails 3 Way par Obie Fernandez pour en savoir plus sur
Rails.
PeepCode : j'ai mentionné plusieurs screencasters commerciaux au chapitre 1, mais le seul dont j'ai une solide expérience est
PeepCode. Les screencasts à PeepCode sont de très haute qualité, et je les recommande chaudement.
12.5 Exercices
1. Ajoutez des tests pour dependent :destroy dans le modèle Relationship (extrait 12.5 et extrait 12.17) en suivant l'exemple de
l'extrait 11.11.
2. La méthode respond_to vu dans l'extrait 12.36 peut en fait hisser des actions à l'intérieur du contrôleur Relationships lui-même, et
les blocs respond_to peuvent être remplacés par une méthode Rails appelée respond_with. Démontrez que le code résultant, montré
dans l'extrait 12.46, est correct en vérifiant que la suite de tests continue de réussir (pour les détails sur cette méthode, faites une
recherche Google sur les termes « rails respond_with »).
3. Les actions following et followers de l'extrait 12.29 contiennent encore de considérables redondances de code. Vérifiez que la
méthode show_follow de l'extrait 12.47 élimine bien cette duplication (voyez si vous pouvez deviner ce que la méthode send fait,
comme dans, par exeemple @user.send(:following)).
4. Restructurez l'extrait 12.30 en ajoutant des partiels au code actuel des pages des lecteurs et des auteurs suivis, de la page d'accueil
et de la page d'affichage de l'utilisateur.
5. En suivant le modèle de l'extrait 12.20, écrivez des tests pour les statistiques dans la page de profil.
6. Écrivez un test d'intégration pour le suivi et l'arrêt de suivi de l'utilisateur.
7. Ré-écrivez les méthodes Ajax de la section 12.2.5 en utilisant jQuery à la place de Prototype. Astuce : vous voudrez peut-être lire
la section jQuery de Mikel Lindsaar’s blog post about jQuery, RSpec, and Rails 3.
def create
@user = User.find(params[:relationship][:followed_id])
current_user.follow!(@user)
respond_with @user
end
def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow!(@user)
respond_with @user
end
end
def followers
show_follow(:followers)
end
def show_follow(action)
@titre = action.to_s.capitalize
@user = User.find(params[:id])
@users = @user.send(action).paginate(:page => params[:page])
render 'show_follow'
end
.
.
.
end