There are several Ruby on Rails plugins for tagging. The first one was acts_as_taggable by DHH. It was simple and limited in functionality, so it was forked several times. The most popular version of these forks is acts_as_taggable_on_steroids, which is pretty performant, has tag cloud calculations and offers extras such as tests.

However, it's not possible to have several sets of tags on the same object. For example, it can be useful to separate skills and interests for users. acts_as_taggable_on implements this lacking functionality.

Cool, but how do we use it ?

First, install it like any rails plugin :

script/plugin install http://svn.intridea.com/svn/public/acts_as_taggable_on/

Then, generate the migration for creating the new SQL tables for the tags (and play this migration) :

script/generate acts_as_taggable_on_migration
rake db:migrate

The last step of the installation is to declare the User class as taggable :

RUBY:
  1. class User < ActiveRecord::Base
  2.   acts_as_taggable_on :skills, :interest
  3.   # ...
  4. end

Done ? OK, let's test it in script/console:

RUBY:
  1. >> joe = User.new(:login => 'Joe')
  2. # => #<User id: nil, login: "joe">
  3.  
  4. >> joe.skill_list
  5. # => []
  6. >> joe.skill_list = "ruby, rails, optimization"
  7. # => "ruby, rails, optimization"
  8. >> joe.skill_list
  9. # => ["ruby", "rails", "optimization"]
  10.  
  11. >> joe.interest_list = "procrastinate, humour"
  12. # => "procrastinate, humour"
  13. >> joe.interest_list
  14. # => ["procrastinate", "humour"]
  15.  
  16. >> joe.save
  17. # => true
  18.  
  19. >> User.find_tagged_with("rails")
  20. # => [#<User id: 1, login: "joe">]
  21. >> User.find_tagged_with("rails", :on => :interests)
  22. # => []

With these methods, you should be able to have tags on many models on your app, with several sets if you want. The next big step is tag clouds, because of the lacking documentation. I think an example is welcomed.

So, the first thing is finding tags with their counts :

RUBY:
  1. # app/controllers/clouds_controller.rb
  2. class CloudsController < ApplicationController
  3.   def skills
  4.     @tags = User.skill_counts
  5.     @levels = (1 .. 5).map { |i| "level-#{i}" }
  6.   end
  7. end

Then, we can show them with the tag_cloud helper:

HTML:
  1. # app/views/clouds/skills.html.erb
  2. <% if @tags.empty? -%>
  3.   <p>No tags :/</p>
  4. <% else -%>
  5.   <ul id="tag-cloud">
  6.     <% tag_cloud(@tags, @levels) do |tag,level| -%>
  7.       <li class="<%= level %>"><%=h tag %></span>
  8.     <% end -%>
  9.   </ul>
  10. <% end -%>

Go on http://my-web-site/clouds/skills and have a glance on the tag clouds.

The last caveat is tag clouds with tags from all the models and sets. For this, we need to dive into acts_as_taggable_on and play with ActiveRecord:

RUBY:
  1. class CloudsController < ApplicationController
  2.   def index
  3.     @tags = Tag.find(:all,
  4.       :select => "#{Tag.table_name}.id, #{Tag.table_name}.name, COUNT(*) AS count",
  5.       :joins  => "LEFT OUTER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id",
  6.       :group  => "#{Tag.table_name}.id, #{Tag.table_name}.name HAVING COUNT(*)> 0",
  7.       :order  => "count DESC",
  8.       :limit  => 30
  9.     ).sort_by(&:name)
  10.     @levels = (1 .. 5).map { |i| "level-#{i}" }
  11.   end
  12. end

The app/views/clouds/all.html.erb view is the same as skills.html.erb. Enjoy it on http://my-web-site/clouds/ :-)

4 Responses to “Playing with acts as taggable on”

  1. Michael Bleigh Says:

    Thanks for the great writeup on my plugin! I just wanted to drop a note to say that I have moved the project over to GitHub, which is where future updates will be coming in.

    GitHub Project URL: http://github.com/mbleigh/acts-as-taggable-on

    New Installation Instructions: script/plugin install git://github.com/mbleigh/acts-as-taggable-on.git

  2. bmichel Says:

    Thanks for your reply. I will follow your updates on github.

  3. Jerome Says:

    Doesn’t work with single table inheritance :(

  4. Juanjo Says:

    You must include TagsHelper on ApplicationHelper if you want to use tag_cloud.

    module ApplicationHelper
    include TagsHelper
    end

    and I prefer this…

    No tags :/

    :tag, :id => tag.name }, :class => level %>

    .level-1 { font-size: 1.0em; }
    .level-2 { font-size: 1.2em; }
    .level-3 { font-size: 1.4em; }
    .level-4 { font-size: 1.6em; }
    .level-5 { font-size: 1.8em; }

Leave a Reply

Creative Commons License
This work is licensed under a Creative Commons Attribution 2.0 License.