10-10-2009, 09:51 AM
(Modification du message : 21-02-2010, 01:13 PM par Sephi-Chan.)
Créer un espace d'administration
Cet article concerne la version 2.x de Ruby on Rails.
Ruby on Rails propose un système de routes très puissant. Outre la simplicité de mise en place de servie Web REST, ces routes permettent de créer des espaces.
En combinant cela avec les contrôleurs, nous allons voir comment mettre en place un espace d'administration.
Pour se faire, nous admettrons avoir 5 contrôleurs :
L'héritage va beaucoup nous servir ici : comme tous les contrôleurs de la partie administration héritent de AdministrationController, il suffit de mettre la restriction d'accès dans celui-ci et ça s'appliquera automatiquement aux sous-classes !
De la même manière, faire un système qui permet de mettre le site en maintenance sera très simple, il suffira de restreindre l'accès au contrôleur PublicController et paf, toute la partie publique sera innaccessible (et vous pourrez bien entendu lever cette exception pour certains contrôleurs particuliers si vous souhaitez qu'ils restent accessible pendant la mainteance) !
Voici l'arboresscence de notre dossier app/ (comme souvent, épuré pour l'exemple).
On a donc des contrôleurs, qui sont des classes, les contrôleurs ApplicationController, PublicController et UsersController sont placés à la racine du répertoire controllers/, alors que AdministrationController et l'autres UsersController sont placé dans le répertoire controllers/administration/ (que l'on crée nous-même).
Voici le code basique de nos contrôleurs. Les méthodes portent des noms suffisemment explicites, leur implémentation n'a donc pas vraiment d'importance pour cet article.
Comme vous pouvez le voir, les before_filter sont surpuissants ! Avec ça, on sécurise tous les contrôleurs qui étendent le contrôleur sécurisé en une poignée de ligne (si après ça quelqu'un dit que la POO sert à rien… ).
Nous avons donc le socle de notre espace administration. Passons maintenant aux routes pour rendre tout ça sympa !
Le but de la manœuvre est d'avoir une application RESTful (là où c'est utile) et de pouvoir distinguer facilement l'espace dans lequel on est. Quelques exemples d'URL que l'on aura.
Cet article concerne la version 2.x de Ruby on Rails.
Ruby on Rails propose un système de routes très puissant. Outre la simplicité de mise en place de servie Web REST, ces routes permettent de créer des espaces.
En combinant cela avec les contrôleurs, nous allons voir comment mettre en place un espace d'administration.
Pour se faire, nous admettrons avoir 5 contrôleurs :
- ApplicationController : le contrôleur frontal de l'application, tout ce qui y est définit est global à l'application.
- AdministrationController : le contrôleur de la zone administration. Ce contrôleur aura des filtres qui permettront de sécuriser automatiquement notre espace d'administration. Il hérite de ApplicationController et tous les contrôleurs des pages de l'administration héritent de lui.
- PublicController : le contrôleur de la zone publique. Il hérite de ApplicationController et tous les contrôleurs (en dehors de ceux de l'administration) héritent de lui.
- UsersController : le contrôleur qui gère les utilisateurs côté publique (c'est ce contrôleur qui contiendra les actions concernant les comptes utilisateurs (lister, afficher, créer, modifier, etc.).
- UsersController : tout comme le précédent UsersController, à la différence près que celui-ci sera dans la zone administration (nous verrons comment le distinguer de celui de la partie publique).
L'héritage va beaucoup nous servir ici : comme tous les contrôleurs de la partie administration héritent de AdministrationController, il suffit de mettre la restriction d'accès dans celui-ci et ça s'appliquera automatiquement aux sous-classes !
De la même manière, faire un système qui permet de mettre le site en maintenance sera très simple, il suffira de restreindre l'accès au contrôleur PublicController et paf, toute la partie publique sera innaccessible (et vous pourrez bien entendu lever cette exception pour certains contrôleurs particuliers si vous souhaitez qu'ils restent accessible pendant la mainteance) !
Voici l'arboresscence de notre dossier app/ (comme souvent, épuré pour l'exemple).
.
`-- app
|-- controllers
| |-- administration
| | |-- administration_controller.rb
| | |-- users_controller.rb
| |-- application_controller.rb
| |-- public_controller.rb
| `-- users_controller.rb
|-- models
| |-- user.rb
`-- views
|-- administration
| |-- administration
| | `-- index.html.erb
| |-- users
| | |-- new.html.erb
| | |-- show.html.erb
| | |-- edit.html.erb
| | |-- index.html.erb
|-- public
| `-- index.html.erb
|-- layouts
| |-- administration.html.erb
| |-- public.html.erb
| `-- application.html.erb
`-- users
|-- edit.html.erb
|-- index.html.erb
|-- new.html.erb
`-- show.html.erb
On a donc des contrôleurs, qui sont des classes, les contrôleurs ApplicationController, PublicController et UsersController sont placés à la racine du répertoire controllers/, alors que AdministrationController et l'autres UsersController sont placé dans le répertoire controllers/administration/ (que l'on crée nous-même).
Voici le code basique de nos contrôleurs. Les méthodes portent des noms suffisemment explicites, leur implémentation n'a donc pas vraiment d'importance pour cet article.
# application_controller.rb
class ApplicationController < ActionController::Base
# current_user is now accessible in both controllers and views.
helper_method :current_user
protected
# Return the current user.
def current_user
# ...
end
# Require logged in user.
def require_user
# ...
end
# Require logged out user.
def require_no_user
# ...
end
end
# public_controller.rb
class PublicController < ApplicationController
before_filter how_maintenance, :if => :website_is_in_maintenance?
layout 'public'
def index
# Used as homepage for the public side of the website.
end
protected
# To set the maintenance mode, just create a file called maintenance.txt in tmp/.
def website_is_in_maintenance?
File.exists?(Rails.root.join('tmp', 'maintenance'))
end
def show_maintenance
render :file => 'views/maintenance.html.erb',
:layout => 'maintenance'
return
end
end
# users_controller.rb
class UsersController < PublicController
# ...
end
# administration/administration_controller.rb
class Administration::AdministrationController < ApplicationController
before_filter :require_authentication
layout 'administration'
def index
# Used as homepage for the administration of the website.
end
protected
def require_authentication
unless current_user && current_user.is_administrator?
render :file => 'views/forbidden.html.erb',
:layout => 'error',
tatus => 403
return
end
end
end
# administration/users_controller.rb
class Administration::UsersController < Administration::AdministrationController
# ...
end
Comme vous pouvez le voir, les before_filter sont surpuissants ! Avec ça, on sécurise tous les contrôleurs qui étendent le contrôleur sécurisé en une poignée de ligne (si après ça quelqu'un dit que la POO sert à rien… ).
Nous avons donc le socle de notre espace administration. Passons maintenant aux routes pour rendre tout ça sympa !
Le but de la manœuvre est d'avoir une application RESTful (là où c'est utile) et de pouvoir distinguer facilement l'espace dans lequel on est. Quelques exemples d'URL que l'on aura.
- /users/new : la page de création d'un compte.
- /users/1 : la page qui affiche le profil de l'utilisateur d'ID 1.
- /users/1.xml : la page qui affiche le profil de l'utilisateur d'ID 1 sous forme de document XML.
- /users/1.png : une page qui renvoie un document de mime type image/png. Pour par exemple faire une signature dynamique que l'utilisateur d'ID pourra mettre sur les forums !
- /users/1/edit : la page d'édition du profil de l'utilisateur d'ID 1. Ici, on utilisara généralement un before_filter pour être sur que l'utilisateur 1 est bien le current_user, puisque l'utilisateur connecté peut modifier uniquement son profil.
- /administrations/users : la page d'administration qui liste tous les utilisateurs.
- /administration/users/1/edit : la page d'administration qui permet de modifier le profil de l'utilisateur d'ID 1.
- /administrations/ : la page par défaut de la partie administration. On peut mettre ce qu'on veut.
Pour mettre ça en place, nous allons modifier le fichier config/routes.rb pour y mettre ce contenu :
ActionController::Routing::Routes.draw do |map|
map.root :controller => 'public', :action => 'index'
map.resources :users
map.namespace :administration do |administration|
administration.root :controller => 'administration'
administration.resources :users
end
end
Et oui, c'est très simple et ça parle tout seul ! Avec root, on définit la racine du namespace global. Ainsi, quand on va sur la page principale du site, c'est l'action index du contrôleur PublicController qui est rendue.
Ensuite, on définit une ressource users. Par défaut, cette ressource utilisera le contrôleur du même nom : UsersController. C'est ça qui nous permettra d'avoir des URLs cool comme montrées plus haut.
Puis on définit un namespace, ou espace de nom. Il sera baptisé administration. On y accédera en ajoutant /administration/ à l'adresse du site.
Dans ce namespace, on définit un contrôleur par défaut et une ressource users, comme on l'a fait pour la partie publique.
Et c'est tout !
Cerise sur le gateau, définir ces routes a pour effet de créer une poignée d'aides de vue (view helpers) pour vous. Vous pouvez les voir en tapant un rake routes dans un terminal.
Ces view helpers vous permettent de générer des URLs.
Ce tableau est composé de 4 colonnes :- La première est le nom de la route. C'est ce nom là que vous pourrez utiliser dans vos vues, en le suffixant de _path ou de _url selon que vous voulez un chemin relatif ou absolu.
- La seconde, c'est la méthode HTTP pour accéder. On est dans le cadre d'une application REST, la même URL peut pointer sur plusieurs choses selon la méthode HTTP qu'on utilise. Ainsi, envoyer une requête GET sur l'URL /users/1 revient à demander d'afficher l'utilisateur d'ID 1, alors que la même URL avec la méthode DELETE supprime la ressource.
- La troisième est le modèle de l'URL, ce à quoi ressemblera l'URL dans la barre. On utilise des placeholders pour les identifiants des ressources. Quand on lit :id, ça veut dire que la variable params[:id] sera disponnible dans l'action correspondante. Ça veut aussi dire qu'il faut donner cet identifiant à la route, quand on l'utilise.
- La quatrième permet affiche le couple contrôleur/action qui sera appelé en se rendant à l'URL indiquée.
root / {:controller=>"public", :action=>"index"}
users GET /users(.:format) {:controller=>"users", :action=>"index"}
POST /users(.:format) {:controller=>"users", :action=>"create"}
new_user GET /users/new(.:format) {:controller=>"users", :action=>"new"}
edit_user GET /users/:id/edit(.:format) {:controller=>"users", :action=>"edit"}
user GET /users/:id(.:format) {:controller=>"users", :action=>"show"}
PUT /users/:id(.:format) {:controller=>"users", :action=>"update"}
DELETE /users/:id(.:format) {:controller=>"users", :action=>"destroy"}
administration_root /administration {:controller=>"administration/administration", :action=>"index"}
administration_users GET /administration/users(.:format) {:controller=>"administration/users", :action=>"index"}
POST /administration/users(.:format) {:controller=>"administration/users", :action=>"create"}
new_administration_user GET /administration/users/new(.:format) {:controller=>"administration/users", :action=>"new"}
edit_administration_user GET /administration/users/:id/edit(.:format) {:controller=>"administration/users", :action=>"edit"}
administration_user GET /administration/users/:id(.:format) {:controller=>"administration/users", :action=>"show"}
PUT /administration/users/:id(.:format) {:controller=>"administration/users", :action=>"update"}
DELETE /administration/users/:id(.:format) {:controller=>"administration/users", :action=>"destroy"}
Voici un exemple d'utilisation de ces view helpers :
<%# views/public/index.html.erb %>
<ul id="menu">
<li><%= link_to 'Administration', administration_root_path %></li>
<% if current_user %>
<li><%= link_to 'Afficher votre profil', current_user %></li>
<li><%= link_to 'Modifier votre profil', edit_user_path(current_user) %></li>
<% else %>
<li><%= link_to 'Créer un compte', new_user_path %></li>
<% end %>
</ul>
Comme vous pouvez le voir, on utilise le nom donné dans la première colonne, auquel on fait suffixer _path (ou _url, si vous voulez des URLs absolues).
Si besoin, on donne des arguments. Dans notre cas, si le visiteur est connecté (et qu'il est l'utilisateur d'ID 23), on va afficher un lien vers sa page d'édition.
En passant un objet User à notre view helper, il va automatiquement savoir qu'on cherche à faire un lien vers cet utilisateur et générer le lien /users/23/edit.
Pour le lien d'affichage du profil, c'est encore plus magique ! En passant directement notre objet User, Rails va lui-même deviner la route pour afficher cette ressource, grâce à ses conventions.
Quand quelqu'un tentera d'accéder à la page /administration/ ou /administration/users/index (ou toute autre ressource de l'espace administration), la requête HTTP passera par le côntrôleur AdministrationController qui va alors tester si l'utilisateur a le droit d'être ici et le rejeter si ça n'est pas le cas en rendant une vue qui lui explique et un code HTTP 403 (Forbidden).
On peut imaginer d'autres comportements, mais la base est là !
Comme vous pouvez le voir, la puissance de Ruby on Rails, c'est d'utiliser massivement les conventions de nommage, ce qui permet de se passer de beaucoup de configuration !
Si ce sujet vous a intéressé — que Ruby vous tente ou non — je vous serais reconnaissant de me donner votre ressenti afin que je puisse l'améliorer.
Sephi-Chan - La première est le nom de la route. C'est ce nom là que vous pourrez utiliser dans vos vues, en le suffixant de _path ou de _url selon que vous voulez un chemin relatif ou absolu.