Pepper :on => Rails

Carnets de développeurs Ruby on Rails

Gérer les erreurs 404 proprement avec Ruby On Rails

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

Si vous désirez pousser un peu plus loin cette méthode, vous pouvez bien sûr définir vos propres exceptions et en gérer les retours :

# lib/exceptions.rb
module Exceptions
  class RestrictedToUsers < StandardError; end
end
 
# app/controllers/some_controller.rb
def show
  raise Exceptions::RestrictedToUsers if !user
  # ... reste de votre code
end
 
# app/controllers/application.rb
rescue_from Exceptions::RestrictedToUsers do |exception|
  respond_to do |type|
    type.html { render :template => "shared/errors/restricted_to_users", :layout => 'application' }
  end
end

Ajouter un système de pagination à votre application rails

Dans cet exemple, nous imaginons une application de type blog : nous disposons d’une liste d’articles à afficher sur une page d’index, et nous voudrions y insérer un système de pagination.

Commençons par créer le helper de pagination, RAILS_ROOT/app/helpers/pagination_helper.rb :

module PaginationHelper
  def paginate_for(max,limit=5)
    number_of_pages = (max%limit == 0) ? max/limit : (max/limit)+1
    return if number_of_pages == 1
    r = "<ul class='paginate'>"
    1.upto(number_of_pages) do |i|
      if (params[:page].to_i == i) or (params[:page].nil? and i == 1)
        r += "<li><span class='current'>#{i}</span></li>"
      else
        r += "<li>#{link_to(i, :controller => params[:controller], :action => params[:action], :page => i)}</li>"
      end
 
    end
    r += "</ul>"
    (r)
  end
end

Du côté du controlleur (RAILS_ROOT/app/controllers/blogs_controller.rb), nous avons besoin de deux données essentielles : le nombre d’articles à afficher (@blogs_size), et le nombre d’articles affichés par page (NUMBER_OF_BLOG_POST_PER_PAGE).

Nous calculons également l’offset en fonction de la page où nous nous situons.

class BlogsController < ApplicationController
 
  def index
    offset = (params[:page].nil?) ? 0 : (params[:page].to_i-1)*NUMBER_OF_BLOG_POST_PER_PAGE
    @blogs_size = Blog.count_by_sql("SELECT COUNT(*) FROM blogs")
    @blogs = Blog.find(:all, :order => "created_at DESC", :limit => NUMBER_OF_BLOG_POST_PER_PAGE, :offset => offset)
 
    respond_to do |format|
      format.html # index.html.erb
    end
  end
 
end

Pour terminer, il ne reste qu’à faire appel a notre helper dans la vue RAILS_ROOT/app/views/blogs/index.html.erb, avant et/ou après la liste des articles :

<%= paginate_for(@blogs_size, NUMBER_OF_BLOG_POST_PER_PAGE) %>

strftime sans 0 (zéros) inutiles

Si mon objet foo a été créé le 3 février et que je souhaite afficher sa date de création, je vais faire ceci…

<%= foo.created_at.strftime('%d %B') %> # -> '03 February'

Pour éviter le 0 inutile devant le 3 et ainsi obtenir « 3 February », il convient d’ajouter le modificateur de préfixe ‘-1′ dans l’expression passée en argument.

<%= foo.created_at.strftime('%-1d %B') %> # -> '3 February'

(testé avec Rails 2.1.2)

Ne pas afficher les mots de passes dans les logs Rails

En voilà un vrai problème de sécurité ! Lorsque vous demandez à vos visiteurs de spécifier un mot de passe (ou une confirmation de mot de passe) celui-ci est transmis à votre application sous forme de paramètre.

Le souci est que Rails ajoute tous les paramètres reçus/envoyés dans le fichier de log. Les mots de passes de vos utilisateurs se retrouvent donc en clair dans vos fichiers de logs.

Petit exemple, un formulaire d’identification:
login-form1

Si j’envoie ce formulaire, voici ce qu’on retrouvera dans mon fichier de log:

Parameters: {"commit"=>"M'identifier", "password"=>"azerty12", "email"=>"vinc@pepperkoffee.com", "authenticity_token"=>"b9f0915b77ae33e6ca39cca16a1ecd65857b6e20", "action"=>"create", "controller"=>"sessions"}

On retrouve en clair mon mot de passe « azerty12« .

Pour éviter cela, nous allons simplement ajouter cette ligne dans le fichier RAILS_ROOT/app/controllers/application.rb:
UPDATE: Cette ligne est présente mais commentée dans Rails 2.2, décommentez-la.

class ApplicationController < ActionController::Base
    ...
    filter_parameter_logging "password"
    ...
end

Envoyons une nouvelle fois notre formulaire, voici ce que nous trouvons dans les logs:

Parameters: {"commit"=>"M'identifier","password"=>"[FILTERED]", "email"=>"vinc@pepperkoffee.com", "action"=>"create", "authenticity_token"=>"b9f0915b77ae33e6ca39cca16a1ecd65857b6e20", "controller"=>"sessions"}

Notre mot de passe a été remplacé par « [FILTERED]« .

Sachez que filter_parameter_logging reçoit, notamment, son argument comme une expression régulière. Ce qui signifie que si un champ de votre formulaire s’appelle « password_confirmation », il sera aussi caché dans vos logs.

Pour plus d’informations sur filter_parameter_logging, consulter la documentation.

Vous avez certainement entendu parlé de Facebook ? Ce célèbre réseau social où tout le monde est inscrit …

Il faut savoir que Facebook propose depuis peu une API de connexion du nom de Facebook Connect.

On peut comparer Facebook Connect à OpenID. Les deux reposent sur l’idée d’une identification/inscription unique. Vous vous inscrivez sur Facebook et vos identifiants et mots de passe peuvent ensuite être utilisés pour vous connecter à d’autres sites.

L’avantage de Facebook Connect réside dans le fait que beaucoup de gens sont déjà inscrits sur le site, évidemment …

Je vais donc vous proposer un tutoriel complet permettant de mettre en place une identification via Facebook Connect sur votre site.
Lire la suite »

Détecter les robots d’indexation avec Ruby on Rails

Dans certaines situations, il peut être intéressant de pouvoir distinguer les utilisateurs humains des robots d’indexations des moteurs de recherche. Dans cet article, nous allons voir comment effectuer cette distinction en quelques lignes seulement.

Ajoutez la méthode suivante dans votre contrôleur d’application (RAILS_ROOT/app/controllers/application.rb) :

# Return true if the user agent is a bot.  
def robot?  
  bot = /(Baidu|bot|Google|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg|ia_archiver|MJ12)/i  
  request.user_agent =~ bot  
end

Désormais, il vous sera possible d’utiliser cette méthode dans n’importe quel contrôleur. Par exemple :

def show
   render :update do |page|
     if robot?
       page.alert("je suis un robot")
     else
       page.alert("je ne suis pas un robot")
     end
   end
end

La liste des robots définie dans la variable bot est bien entendu non-exhaustive, libre à vous de la compléter selon vos propres besoins.

Ajouter des mots clès dans vos URL

Le lien standard de Ruby on Rails se compose sous cette forme:

http://domain.com/:controller/:action/:id

Imaginons une table « films » avec son controller « films » ainsi que son model « film« . Un film est identifié par les colonnes « ID » et « TITLE« .
L’URL permettant de visualiser le film « Le parrain » dont l’ID est 14 serait:

http://domain.com/films/show/14

Il serait intéressant d’ajouter le titre du film à cette URL, comme ceci:

http://domain.com/films/show/14-le-parrain

Pour se faire, rien de plus simple, dans le model « film« , ajoutez ceci:

class Film < ActiveRecord::Base
    ...
    def to_param
        "#{id}-#{title.gsub(/[^a-z1-9]+/i, '-')}"
    end
    ...
end

Modifiez ensuite l’argument :id du link_to:

# Avant
link_to 'Le parrain', :controller => :films, :action => :show, :id => @film.id
# Après
link_to 'Le parrain', :controller => :films, :action => :show, :id => @film

En spécifiant l’objet à l’argument :id vous obtiendrai le lien contenant le titre du film.

Lightview est un script JavaScript permettant d’afficher une pop-up dynamique par-dessus une page web. Nous nous en servons beaucoup sur l’un des projets en cours.

Dans l’une des « lightviews » dont le contenu est chargé en AJAX, nous affichons une liste d’objets paginée à l’aide du gem/plugin will_paginate.

Nous voulons que les liens internes de la lightview chargent eux aussi du contenu en AJAX, dans la même lightview. C’est tout à fait possible, la documentation de Lightview vous expliquera comment faire.

Cependant, le plugin will_paginate ne permet pas directement de spécifier les attributs ‘class’, ‘rel’ et ‘title’ des liens de paginations générés. Ce doit être possible en modifiant le LinkRenderer du plugin, mais il y a plus simple.

Pour que les liens générés par will_paginate agissent eux-mêmes en AJAX dans la lightview, voici comment faire. Insérez le code ci-dessous au bas de votre vue.

<div id="pagination-wrapper">
	<%= will_paginate @posts %>
</div>
 
<script type="text/javascript">
//<![CDATA[
	pagination_links = $('pagination-wrapper').getElementsByTagName('a');
	for (i = 0; i < pagination_links.length; i++) {
		pagination_links[i].setAttribute('class', 'lightview');
		pagination_links[i].setAttribute('rel', 'ajax');
		pagination_links[i].setAttribute('title', 'Mon super blog :: :: autosize: true, ajax: { evalScripts: true, method: \'get\' }');
	}
 
	Lightview.updateViews();
//]]>
</script>

Recevoir un eMail à chaque erreur dans votre application Rails

Un plugin plus que pratique, indispensable. exception_notification vous enverra un eMail à chaque erreur qui se produit dans votre application.

Pour l’installer :

script/plugin install git://github.com/rails/exception_notification.git

Ensuite, ajoutez cette ligne dans le fichier RAILS_ROOT/app/controllers/application.rb:

class ApplicationController < ActionController::Base
    include ExceptionNotifiable
    ...
end

Enfin, dans votre fichier d’environement (RAILS_ROOT/config/environment.rb):

ExceptionNotifier.exception_recipients = %w(email@domaine.com autre_email@domaine.be)

Vous spécifiez donc autant d’adresses eMail que vous le souhaitez, dès qu’une exception se produit dans votre application, vous recevrez un email détaillé.

Démarrer avec Phusion Passenger et Ruby Enterprise Edition (REE)

On dit beaucoup de bien de Phusion Passenger couplé à Ruby Enterprise Edition. Si vous voulez vous lancer dans l’aventure, voici les commandes de base à exécuter (sous Debian).

sudo apt-get install ruby rdoc irb ruby1.8-dev
sudo gem install passenger
sudo passenger-install-apache2-module
cd /home/michael/sources
wget { dernière version de ruby enterprise }
tar xvzf { fichier que vous venez de télécharger }
sudo ./ruby-enterprise-X.X.X/installer
sudo /opt/ruby-enterprise-X.X.X-YYYYMMDD/bin/passenger-install-apache2-module

L’installer est très bien fait et vous donnera les noms exacts des éventuels paquets manquants.

Après, il ne reste plus qu’à lire la doc: Phusion Passenger et Ruby Enterprise Edition. Chance pour nous, elle est plutôt bien faite.

« Billets précédents  Page suivante »