Announcing 'acts_as_taggable_on'

For a number of applications, especially our Social Networking Platform I’ve found a need for advanced tagging functionality not offered by the acts_as_taggable_on_steroids plugin. Namely, there have been a number of times when I’ve wished a model could have multiple “sets” of tags that would function both independently and together. For example, a user might have tagged themselves, but they might also have skills, interests or sports that would also function like tags. That’s where acts_as_taggable_on comes in.

Installation

To install the plugin on Edge Rails or Rails 2.1 and greater:

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

On previous versions of Rails:

git clone git://github.com/mbleigh/acts-as-taggable-on.git vendor/plugins/acts-as-taggable-on

Usage

Acts As Taggable On provides the same functionality of acts_as_taggable_on_steroids with the addition of the notion of “contexts,” or scoped areas in which taggings can live. For the user example listed above, I can now simply make a call like so:

class User < ActiveRecord::Base
  acts_as_taggable_on :tags, :skills, :interests, :sports
end

By taking the same one-liner tack as previous implementations, we are now able to set, find, retrieve, and calculate information based on tags within a context as well as as a whole. With the user model we just created:

@user = User.new(:name => "Bobby")
@user.tag_list = "awesome, slick, hefty"      # this should be familiar
@user.skill_list = "joking, clowning, boxing" # but you can do it for any context!
@user.skill_list # => ["joking","clowning","boxing"] as TagList
@user.save

@user.tags # => [<Tag name:"awesome">,<Tag name:"slick">,<Tag name:"hefty">]
@user.skills # => [<Tag name:"joking">,<Tag name:"clowning">,<Tag name:"boxing">]

User.find_tagged_with("awesome") # => [@user]
User.find_tagged_with("joking") # => [@user]
User.find_tagged_with("awesome", :on => :tags) # => [@user]
User.find_tagged_with("awesome", :on => :skills) # => []

@frankie = User.create(:name => "Frankie", :skill_list => "joking, flying, eating")
User.skill_counts # => [<Tag name="joking" count=2>,<Tag name="clowning" count=1>...]

@bobby.skill_counts
@frankie.skill_counts

And that’s all there is to it! Now you can scope your tags to whatever arbitrary name you would like. The inflector is used to create the singular/plural methods, so be careful about making sure that your nomenclature uses plurals in the acts_as_taggable_on call.

As a side note, this is meant to be used in place of acts_as_taggable_on_steroids, and the acts_as_taggable method works in acts_as_taggable_on simply by calling acts_as_taggable_on :tags. Caching has been written into the implementation but is not yet tested or verified to be working. Stay tuned for additional improvements to this plugin, and feel free to submit any bugs or patches to the Lighthouse

UPDATE 5/3/07: Acts As Taggable on is now hosted on GitHub and has a public Lighthouse available for bug tracking. Information updated above.

Share:

33 Responses to “Announcing 'acts_as_taggable_on'”

  1. Dana J

    Works like a charm, but the migration couldn't be found--I had to go pull up the schema.rb. Not a big deal, just thought I'd let you know. Thanks for putting this out :)
  2. Michael Bleigh

    Dana, thanks for the feedback. I have created a ticket on our public Trac, so feel free to keep track of that and I will close it out when I've had a chance to fix the problem. http://trac.intridea.com/trac/public/ticket/2
  3. Bill

    Thanks for putting this out. Is it good to go for Rails 2.0?
  4. Michael Bleigh

    The better question might be is it pre-2.0 compatible! We are running it on 2.0 and edge, haven't ever tried it on 1.2.6 or prior. Thanks for the interest!
  5. Michael Bleigh

    I have updated the plugin to include the ability to do custom tag contexts and have fixed the missing migration generator problem. Enjoy!
  6. thom

    I really like this plugin. It would be great if acts_as_taggable_on would support user owned tagging (http://monki.geemus.com/2007/6/15/user-owned-tagging).
  7. Michael Bleigh

    thom: That's an interesting suggestion. If I find some time I may try to implement some kind of user owned taggings like you suggested. I can see how that could be helpful in a number of situations.
  8. Chris

    I would second that, I would really like to see a taggable plugin that allows for a multi-user type environment. For me I would see the need that when a user logs in to a system they have their own tags that they use to tag things. They only see their own tags and their tag cloud or counts are scoped to that user. It would be awesome to have a plugin for that!
  9. Nicholas Orr

    Yeah I'm interested in the "User Owned" feature too. Be great if you are able to put that in. Great plugin.
  10. Michael Bleigh

    Thanks for the feedback. I have been working a little bit on user-owned tags, and it's a pretty complex problem (due to dual polymorphism). I will keep you up to date if I make any progress.
  11. Kunal

    I'm trying to use the plugin, but I don't see how to add an individual tag without deleting old tags associated with that model. Help me out?
  12. Walther H Diechmann

    Hi Michael, great plugin - lots of cudo - :) I have one tiny itch, though <:><< Calculation.create p.save (with the calculations being HMP) But - if I change 1 line in acts_as_taggable_on/lib/active_record/acts/taggable_on.rb as per this diff @@ -219,7 +219,7 @@ end def save_tags - (custom_contexts + self.class.tag_types.map(&:to_s)).each do |tag_type| + ( (custom_contexts || []) + self.class.tag_types.map(&:to_s)).each do |tag_type| next unless instance_variable_get("@#{tag_type.singularize}_list") new_tag_names = instance_variable_get("@#{tag_type.singularize}_list") - tags_on(tag_type).map(&:name) everything is just sweet pie ;) I'm totally noob on RoR, so if I'm rambling please excuse my blocking your bandwidth <:) best regards, Walther
  13. Jerome

    Mmmm it doesn't work with STI: the plugin inserts tags with the current model as taggable_type, but gets them with the super model.
  14. Marshall

    Absolutely awesome plugin, Michael. However, it seems there's some kind of problem. I am using acts_as_authenticated, and whenever I login with cookie (calling the remember_me method) and when i logout (calling the forget_me method), I experience an error from the plugin. Here it is: NoMethodError (You have a nil object when you didn't expect it! You might have expected an instance of Array. The error occurred while evaluating nil.+): /vendor/plugins/acts_as_taggable_on/lib/active_record/acts/taggable_on.rb:222:in `save_tags' ... app/models/user.rb:64:in `remember_me' app/controllers/account_controller.rb:14:in `login' Line 64 in user.rb is save(false) The offending line in taggable_on.rb is (custom_contexts + self.class.tag_types.map(&:to_s)).each do |tag_type| so it seems custom_contexts isn't getting set somehow, there. Any ideas? Thanks so much. Marshall
  15. Paul

    I am having similar issues as #14 (Marshall) When I run my tests I get a number of nil.+ errors relating to line 222 of taggable_on. As Marshall said, it seems that custom_contexts is not set. One strange thing is that there is a def custome_contexts method that returns @custom_contexts and there is attr_accessor :custom_contexts Removing either one does not seem to fix the issue though. Any ideas? Thanks!
  16. Paul

    So, it seems the problem is with single table inheritance. I applied the ... (custom_contexts or [] ) ...fix, but had do a lot more tweaking to make this work for a class that has a subclass. Basically, I can only use this on classes that inherit from AR directly. It sill has problems because of the subclass, so I had to replace most instances of self.class with self.class.base_class in the instance methods of taggable_on. I'm not happy with this solution but it seems to work. I tried to actually fix the problem so it works with STI but just ended up hacking the plugin to death with more and more problems.
  17. Michael

    I'm getting the same error on line 222 but I'm not using STI - I'm inheriting from straight from ActiveRecord::Base. Strangely, from the console I have no trouble but get this error from within the controller.
  18. Roy

    I'm getting this same error when I simply update_attribute() on my user model. My user model inherits from ActiveRecord::Base. You have a nil object when you didn't expect it! You might have expected an instance of Array. The error occurred while evaluating nil.+ vendor/plugins/acts_as_taggable_on/lib/active_record/acts/taggable_on.rb:222:in `save_tags' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/callbacks.rb:307:in `send' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/callbacks.rb:307:in `callback' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/callbacks.rb:304:in `each' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/callbacks.rb:304:in `callback' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/callbacks.rb:214:in `create_or_update' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/base.rb:1972:in `save_without_validation' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/validations.rb:934:in `save_without_transactions' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:108:in `save' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/abstract/database_statements.rb:66:in `transaction' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:80:in `transaction' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:100:in `transaction' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:108:in `save' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:120:in `rollback_active_record_state!' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:108:in `save' c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/validations.rb:955:in `update_attribute'
  19. Roy

    Oops..sorry about that spam
  20. Marshall from Wine Q

    Hey guys, I got an easy ghetto-rig fix! Just put this before your save line: object.custom_contexts = Array.new Better yet, add a before_save to your object's class. def Object < ActiveRecord::Base before_save :set_custom_context def set_custom_context self.custom_context = Array.new end end I have no idea why the initialize Instance method isn't running to set custom_context, but until that is figured out, this works!
  21. Lenart

    Very nice work on the plugin! I like the idea. Keep it up!
  22. Matt Van Horn

    I fixed the nil.+ error everyone mentioned by removing the initialize method and replacing it with: def after_initialize @custom_contexts ||= Array.new end I believe it's better not to muck around with initialize on AR objects.
  23. Matt Van Horn

    Actually, on looking at the Trac, I like Dave Chelimsky's fix a lot better.
  24. vince

    Is it possible to search for items with multiple tags? For example, User.find_tagged_with("awesome", "joking") would return users that are tagged as BOTH awesome and joking. thanks.
  25. Michael Bleigh

    vince: absolutely. You can also search with a joined string such as "awesome, joking".
  26. Christopher J. Bottaro

    Thank you very much for this. I was about to write the *exact* same plugin until I stumbled up this... :)
  27. Mr.Prise

    Thank you for your plugin! I like how the tags work in Drupal and I needed that functionality in my Rails app and your plugin just do what I need! :-)
  28. Thomas

    acts_as_taggable_on doesn't work with single table enheritance models. Is there any workarround for this problem or any chance this bug will be fixed in the official repository at github? It's a pitty that this cool plugin is completely useless if you use STI. :-(
  29. Thomas

    If someone has a fix for the STI problem or is able to help Michael with this issue, please visit http://mbleigh.lighthouseapp.com/projects/10116/tickets/5-acts_as_taggable_on-doesn-t-work-with-single-table-enheritance-mo
  30. Russ Jones

    User.tag_counts is pretty cool ... more often then not though i'd like to show a collection of tags for a subset of data, not ALL users ... I'd like to do something akin to... @users = User.find(:all, :condition => etc) ... @users.tag_counts is this possible? currently I use a helper to loop through each result in the collection and push their individual tags into a new array, which i later sort and call uniq! on ... which gives me -n- more queries for each item in the collection.
  31. Russ Jones

    other than looping, is there a better way to show all tag_counts for a collection ... ie, User.tag_counts is nice .... if you want ALL tags for ALL users ... but what if you want tags only for a subset? as in ... User.find(:all, :conditions => etc).tag_count
  32. Russ Jones

    *ping* ... anyone? :)
  33. insub

    my question: i have two model: user / soft like that: class User < ActiveRecord::Base acts_as_tagger end class Soft < ActiveRecord::Base acts_as_taggable_on :tags end So, how can i take like that?: tag_cloud current_user.owned_tags.tag_counts()? thk.


Leave a Reply