La méthode rescue_action_in_public définie dans ActionController::Rescue peut être réécrite, comme toutes les méthodes Ruby d'ailleurs, pour en changer son comportement. Elle est appelée à chaque fois qu'une exception est levée suite au chargement d'une page. Les chargements locaux (lorsque vous êtes en développement par exemple) n'appellent pas cette méthode. Si vous voulez mettre en place le même système pour les chargements locaux, il faudra passer par la méthode rescue_action_locally.

Par défaut, cette méthode attrape les exceptions RoutingError et UnknownAction et affiche la page statique disponible dans "public/404.html", pour toutes les autres exceptions, elle affiche la page statique disponible dans "public/500.html".

Nous allons faire en sorte que ces exceptions soient attrapées par notre code et qu'elles affichent nos pages dynamiques ce qui nous permettra d'utiliser nos layouts, CSS et du contenu dynamique plutôt qu'une simple page statique. Pour cela, il suffit d'ajouter le code suivant dans votre fichier "app/controller/application.rb" :

def rescue_action_in_public(exception)
case exception
when ::ActionController::UnknownAction, ::ActionController::RoutingError
render :file => File.join("#{RAILS_ROOT}", "public", "404.rhtml"), :layout => true, :status => "404 not found"
else
render :file => File.join("#{RAILS_ROOT}", "public", "500.rhtml"), :layout => true, :status => "500 error"
SystemNotifier.deliver_exception_notification(self, request, exception)
end
end

L'exception levée par le chargement de la page est passée à notre méthode, nous la stockons dans exception. Ensuite, selon le type d'exception, note méthode se comportera de différentes façons.

Si une exception de type ActionController::UnknownAction ou ActionController::RoutingError est levée, alors nous allons afficher notre page stockée dans "public/404.rhtml". Ce fichier est un template RHTML typique, vous pouvez donc y faire ce que vous voulez. Nous faisons également attention de bien affecter le statut "404" lors du rendu de cette page.

Pour toutes les autres erreurs (qui seront souvent des erreurs fatales qui remettent en cause notre code), nous afficherons le fichier "public/500.html" avec le statut "500". Nous allons également faire en sorte qu'un mail soit envoyé aux admins pour leur signaler l'erreur et leur donner l'ensemble des éléments (URL appelée, paramètres passés à la requête, etc) qui ont fait apparaître cette erreur. Nous utilisons pour cela SystemNotifier, une classe du module ActionMailer qui nous permet d'avoir un e-mail joliment formaté.

Grâce à ce petit bout de code, vous pourrez très facilement rendre vos pages d'erreurs plus attractives et utiles. Vous gagnerez par la même occasion en souplesse.

UPDATE :

Depuis Rails 2.1.0, une méthode plus rapide et plus propre permet d'arriver au même résultat (et même un peu mieux en fait) :

rescue_from ActionController::RoutingError, ActionController::UnknownAction, ActiveRecord::RecordNotFound do |exception|
  respond_to do |type|
    type.html { render :template => "shared/404", :layout => 'application', :status => 404 }
    type.all  { render :nothing => true, :status => 404 }
  end
end