Raison et principe
Cet excellent article du site A List Apart (en anglais) explique en détail pourquoi il est plus judicieux de proposer l’annulation d’une action plutôt que d’en demander confirmation. Pour résumer, à force d’habitude nous ne lisons plus les messages de confirmation et cliquons machinalement sur “Ok”. Une solution est alors d’accepter directement l’action effectuée par l’utilisateur et de lui proposer la possibilité de l’annuler en cas d’erreur, comme lors de la suppression d’un email sur Gmail.
ActsAsArchive permet de déplacer les enregistrements supprimés de la table d’origine dans une table à part. Si la structure de la table d’origine est modifiée dans une migration, celle de la table d’archivage l’est également. Il offre également la possibilité de restaurer un enregistrement.
Installation
Nous ajoutons au fichier config/environment.rb la gem ActsAsArchive, dépaquetons le code et versionnons le tout :
# config/environnement.rb
config.gem 'acts_as_archive'
$ sudo rake gems:install
$ rake gems:unpack:dependencies
$ git add .
$ git commit -am "ActsAsArchive"
Archivage d’un enregistrement
Nous commençons par indiquer au modèle que les suppressions sont gérées par ActsAsArchive :
# app/models/post.rb
class Post < ActiveRecord::Base
acts_as_archive
end
Puis nous générons un fichier de migration pour créer la table d’archivage :
script/generate migration add_acts_as_archive_to_posts
# db/migrate/xxxx_add_acts_as_archive_to_posts.rb
class AddActsAsArchiveToPosts < ActiveRecord::Migration
def self.up
ActsAsArchive.update Post
end
def self.down
drop_table :archived_posts
end
end
rake db:migrate
La table archived_posts est créée, elle possède la même structure que posts et sera mise à jour automatique si la structure de posts est modifiée dans une future migration.
À partir de maintenant, tout appel à delete, destroy ou delete_all sur une instance de Post déplace le(s) enregistrement(s) concerné(s) dans la table archived_posts.
Annulation de la suppression
ActsAsArchive fournit une méthode restore_all permettant de restaurer des enregistrements supprimés. Un id peut lui être passé en paramètre pour spécifier un enregistrement particulier. Une première possibilité pour effectuer une annulation serait de créer une action restore sur le contrôler PostsController. Pour nous conformer à la méthode REST, nous allons plutôt considérer cette annulation comme la suppression d’un ArchivedPost (action destroy du contrôleur ArchivedPostsController. Nous générons ce contrôleur et y ajoutons le code nécessaire :
script/generate controller archived_posts
# app/controllers/archived_posts_controller.rb
class ArchivedPostsController < ApplicationController
def destroy
Post.restore_all(['id = ?', params[:id]])
flash[:success] = "Le post a bien été restauré."
redirect_to posts_path and return
end
end
Il ne nous reste plus qu’à afficher un lien vers l’action d’annulation dans le message flash de suppression d’un post :
# app/controllers/posts_controller.rb
def destroy
@post = Post.find(params[:id])
if @post.destroy
flash[:success] = "Le post a bien été supprimé. " +
@template.button_to("Annuler", {:controller => :archived_posts, :action => :destroy, :id => @post.id}, :method => :delete)
redirect_to posts_path and return
end
end
La variable @template permet d’accéder aux méthodes de helpers disponibles dans les vues pour générer le bouton de suppression. Dans la vue index du controller PostsController, nous pouvons alors remplacer le lien de suppression avec confirmation par un simple bouton :
<% @posts.each do |post| %>
<%= post.title %>
<%= button_to "Supprimer", {:action => :destroy, :id => post.id}, :method => :delete %>
<% end %>