Nuage de tag avec Ruby On Rails : acts_as_taggable

Posted by on 3 juin 2007

Voici un petit billet sur une technique de classification très populaire dans la mouvance web2 : les tags ! Je ne vais pas faire ici leur apologie ni retracer leur “récente” histoire (à ce sujet je lance une petite question : quel a été le premier site à faire usage des tags ? J’ai cherché 2 minutes sans succès !)… Une chose est sûre, les tags sont apparus pour créer une classification alternative à celle des classiques catégories que l’ont retrouve sur une grande majorité de sites. Leur utilisation permet souvent de créer une navigation transversale sur un site et offre donc au lecteur plusieurs suites possibles à la lecture d’une information…

Partant de ce constat, j’ai décidé d’introduire un système de “tags” dans mon dernier projet. Voici donc comment récupérer, installer et utiliser un plugin permettant de mettre en oeuvre très rapidement et très efficacement un tel système sur une application existante.

Deux mots sur le plugin

En cherchant comment mettre des tags sur une application Rails, on arrive rapidement sur le plugin acts_as_taggable qui semble convenir tout à fait à nos besoins. Cependant en cherchant un peu plus, on apprend rapidement l’existence d’un autre plugin basé sur le premier et sensiblement amélioré : acts_as_taggable_on_steroids.

Cet article se basera donc sur ce dernier plugin dont le README peut être lu ici.

Installation

Rien de sorcier dans cette étape, vous devez sûrement maintenant être familier de l’installation des plugins, il suffit d’aller dans le répertoire de votre application et de lancer la commande suivante :

    script/plugin install http://svn.viney.net.nz/things/rails/plugins/acts_as_taggable_on_steroids

Voilà, l’article pourrait s’arrêter là car le README du plugin est assez explicite, cependant nous allons poursuivre jusqu’à l’affichage de notre nuage de tags… Comme d’habitude pour faire tout ça je vais m’appuyer sur un exemple, et depuis peu j’en ai un tout prêt sous la main : celui de l’article “Ma première application Rails” ! Cette application gère simplement une base de données de CDs et permet à un visiteur d’ajouter un commentaire sur un album.

L’application n’est pas très jolie certes, mais elle a le mérite d’être fonctionnelle ;), on va donc y ajouter un système de tags… Par exemple on peut imaginer que chaque album peut appartenir à plusieurs genres musicaux mais que ces genres musicaux (et c’est souvent le cas) ne sont pas forcément très bien définis. (Il existe des groupes de Hard, des groupes de Rock, des groupes de Pop, mais aussi des groupes de Pop Rock ou encore des groupe de Hard Rock Mélodique, Emocore, Ska Rock Médiéval j’en passe et des meilleures!) Bref, pour organiser les genres il semble intéressant de pouvoir laisser un champ de libre saisie et pour pouvoir permettre au visiteurs de chercher des albums par genre, il serait en plus génial que ces genres soient en fait des tags !

Structure de données

Avant tout il faut créer la table qui accueillera la gestion des tags, pour cela nous allons ajouter une migration à notre projet :

 script/generate migration add_tags
  exists  db/migrate
  create  db/migrate/004_add_tags.rb

éditons db/migrate/004_add_tags.rb et ajoutons y le contenu suivant :

class AddTags < ActiveRecord::Migration
  def self.up
    create_table :tags, :force => true do |t|
      t.column :name, :string
    end
    create_table :taggings, :force => true do |t|
      t.column :tag_id, :integer
      t.column :taggable_id, :integer
      t.column :taggable_type, :string
      t.column :created_at, :datetime
    end
  end

  def self.down
    drop_table :tags
    drop_table :taggings
  end
end

Ensuit il suffit de lancer la migration :

rake db:migrate

Ajout des tags au modèle CD

Il faut maintenant indiquer à notre modèle CD qu’il “dépend” du plugin actsastaggable en l’ajoutant comme ci dessous :

class Cd < ActiveRecord::Base
  has_many :comments
  validates_presence_of :title, :author
  acts_as_taggable
end

Pensez à redémarrer votre serveur mongrel ou webrick. (En fait il faut faire cela dès qu’un plugin est ajouté, car Rails à besoin de l’activer lors de l’initiatlisation.)

Intégration des tags au formulaire d’ajout de CDs

Editez le fichier views/cd/_form.rhtml et ajoutez la ligne pour les genres comme ci dessous :

<%= error_messages_for 'cd' %>

<!--[form:cd]-->
<p><label for="cd_title">Title</label><br/>
<%= text_field 'cd', 'title'  %></p>

<p><label for="cd_author">Author</label><br/>
<%= text_field 'cd', 'author'  %></p>

<p><label for="cd_description">Description</label><br/>
<%= text_field 'cd', 'description'  %></p>

<p><label for="cd_year">Year</label><br/>
<%= text_field 'cd', 'year'  %></p>

<p><label for="cd_buy_date">Buy date</label><br/>
<%= datetime_select 'cd', 'buy_date'  %></p>

<p><label for="cd_tag_list">Genres (tags)</label><br/>
<%=text_field 'cd', 'tag_list'%></p>
<!--[eoform:cd]-->

Ok ! A partir de ce moment là, le sytème est en place et fonctionnel, vous pouvez naviguer, ajoutez ou éditer un album et les tags seront bien pris en compte. Reste maintenant à afficher sur la page d’accueil l’ensemble des tags (genres) existants sous forme d’un nuage de tag tel qu’on le voit sur la majorité des sites.

Ajout du nuage de tag (ou tag-cloud)

Pour arriver à nos fins, il va falloir (dans l’action list de notre controlleur principal) récupérer la liste des tags existants. Voici donc comment faire :

class CdController < ApplicationController
 ...
 def list
   @cd_pages, @cds = paginate :cds, :per_page => 10
   @tags = Cd.tag_counts(:order=>'tags.name asc')
 end
 ...
end

Ainsi la liste des tags devient accessible dans notre vue, il reste encore à la formatter pour qu’elle devienne un véritable “Tag Cloud”…

Nous allons pour cela ajouter une méthode à notre helpers/cd_helper.rb. Cette méthode n’est pas de moi ;), je l’ai trouvée sur ce très bon article du blog Juixe TechKnow.

Elle prend en entrée une liste de tags et une liste de classes css, elle détermine le nombre minimun d’occurence d’un tag puis le nombre maximum, a partir de ces données la méthode défini un diviseur qui permettra de positionner chaque tag dans sa classe CSS.

module CdHelper
  def tag_cloud(tags, classes)
    max, min = 0, 0 
    tags.each { |t| 
      max = t.count.to_i if t.count.to_i > max 
      min = t.count.to_i if t.count.to_i < min 
    }

    divisor = ((max - min) / classes.size) + 1 

    tags.each { |t|
      yield t.name, classes[(t.count.to_i - min) / divisor]
    }
  end
end

En parallèle, il faut définir quelques classes css comme suit dans le fichier public/stylesheets/scaffold.css.

/* Nuage de Tag*/

.nube1 {font-size: 1.0em;}
.nube2 {font-size: 1.2em;}
.nube3 {font-size: 1.4em;}
.nube4 {font-size: 1.6em;}
.nube5 {font-size: 1.8em;}
.nube6 {font-size: 2.0em;}

Enfin dans notre vue (app/views/cd/list.rhtml) il ne reste qu’a afficher le nuage de tags en ajoutant le code ci dessus en début de fichier :

<h1>Tag Cloud des genres</h1>

<% tag_cloud @tags, %w(nube1 nube2 nube3 nube4 nube5) do |name, css_class|  %>
      <%= link_to name, {},:class => css_class %>
<% end %>

<h1>Listing cds</h1>
...

Capture-Cd: list - Mozilla Firefox

Vous pouvez voir ci contre le résultat de l’affichage de la page d’accueil, vous pouvez ainsi voir l’ensemble des tags affichés avec une taille différente selon leur utilisation. Évidemment pour avoir ce résultat, j’ai éditer chaque album afin de leur ajouter une liste de genres…


14 Comments on Nuage de tag avec Ruby On Rails : acts_as_taggable

Respond | Trackback

  1. Anonyme dit :

    Faire un nuage de tag avec Ruby On Rails (tag cloud)…

    Voici un petit billet sur une technique de classification trs populaire dans la mouvance web2 : les tags ! Voici donc comment rcuprer, installer et utiliser un plugin permettant de mettre en oeuvre trs rapidement et trs efficacement un tel systme sur …

  2. […] Update 05/06/07 : Je viens d’écrire un nouvel article sur comment faire un nuage de tag avec ruby on rails, il fait suite à cet article et reprend le même exemple. […]

  3. jblanche dit :

    Excellent, C’est en place dans mon dernier projet et c’est surement une heure ou deux de gagner. Reste plus qu’à construire le controller de recherche par tags! Merci encore

  4. yoann dit :

    Je vais devenir un inconditionnel de ton blog ET de Rails…

  5. Salut, merci pour ton tuto.

    J’ai failli partir dans cette direction moi aussi pour développer http://www.livetribune.com (site ouvrant prochainement), mais j’ai finalement opté pour l’option suivante: utiliser l’API delicious pour externaliser mes tags sur delicious. Selon moi, les avantages sont: simple à faire; n’utilise aucun CPU sur ma machine pour les requêtes; me donne des backlinks; me permet de faire des recherches sémantiques en cherchant dans les tags voisins de proche en proche, tj sans utiliser de CPU chez moi; facilite la possiblité pour des tierces parti de développer des mashups sur mon site…

    Pour cela, vous pouvez vous inspirer de cet article: http://ruby.about.com/od/tutorials/ss/delicious_tags_9.htm bien qu’utiliser l’API JSON soit peut être plus intelligent.

    Je pourrai vous fournir des retours quand je serai passé en prod, mais d’ici là, vos commentaires sur cette solution sont les bien venus.

    Raphaël.

  6. Philippe dit :

    Excellent tuto, une chose à regretter, c’est que tu n’est pas écrit plus de tutorials… Si tu pouvais continuer à nous faire découvrir rail, se serait vraiment génial. Merci encore.

  7. Pierre Rigal dit :

    Message reçu ;)

    J’en ai un en préparation sur GoogleMaps et Rails…

  8. Pierre dit :

    C’est vrai, merci beaucoup pour vos articles Pierre !

  9. Tyna dit :

    Man you don’t even know how long I’ve waited for this since disabling my own Movable Type widget (that doesn’t work since Haloscan bypasses that code).

    THANK YOU!

  10. gdufloux dit :

    Merci pour le tuto, c’est trés clair ;)

    Quelques remarques (perfectionnistes) sur la performance, suite à la lecture et l’utilisation de acts_as_taggable_on_steroids :

    1. Ajouter les index à la migration

    Voir le acts_as_taggable_migration généré par le plugin

    add_index :taggings, :tag_id
    add_index :taggings, [:taggable_id, :taggable_type]
    

    2. Optimiser le calcul d’occurence des tags

    On calcule t.count.to_i une seule fois si possible, et un if-elsif évite ici le test de deux conditions exclusives. Un petit 1000.times do sert à comprendre la situation lorsqu’il y a beaucoup de tag ;)

    # 1000.times do 
    tags.each { |t| 
        count = t.count.to_i
        if count &gt; max 
            max = count
        elsif count &lt; min 
            min = count
        end
    }
    # end
    

    2bis. Ou bien utiliser un cache…

    Un petit migrate pour ajouter un champ de cache en base :

    add_column :cds, :cached_tag_list, :string
    

    Puis mettre à jour le cache dans cd_controller.rb lors de create et update :

    @cd.save_cached_tag_list
    

    ps: vivement le tuto sur maps !

  11. Pierre Rigal dit :

    Merci pour ces précisions ! Le tuto GoogleMaps est dans mes brouillons Wordpress… in progress ;)

  12. […] Vous trouverez sur le site de Stoneage un tutoriel pour installer acts_as_taggable_on_steroids. […]

  13. Shoghi dit :

    Super!! comme le premier.

  14. […] Il y a quelques mois j’ai publié un long article présentant la création d’une application Rails depuis la première ligne de code jusqu’à son hébergement sur internet. Quelques temps plus tard je publiai un “addon” à cet article présentant l’ajout d’un système de nuage de tags et nous voilà aujourd’hui avec un nouvel ajout afin d’apprendre à manipuler les fameuses cartes de Google en toute simplicité… […]

Respond

Comments

Comments: