BeTaggable

From BeTaggable

  1. Install the plugin
    script/plugin install http://railers.rubyforge.org/svn/plugins/trunk/be_taggable
  2. This is what you do for now. Once you've created the models that will be tagged in your application, invoke the following:
    • script/generate be_taggable_tables Article Bookmark ...
    • rake db:migrate

BeTaggable Extensions

Various modifications need to be made to be_taggable, so that it will support:

  • Tagging per user
  • Pagination with will_paginate, such as paginate_tagged_with and paginate_owned_by

Put the following be_taggable.rb file in the /rails_app/lib directory. The file overloads the be_taggable.rb file in the plugin directory.

be_taggable.rb

require_dependency "#{RAILS_ROOT}/vendor/plugins/be_taggable/lib/be_taggable.rb"

# Extends the BeTaggable module defined by the BeTaggable plugin.
module BeTaggable

  module ClassMethods
    # find all rows owned by user
    def find_tagged_by(user_id, options={})
      find(:all, 
           {:select => "DISTINCT m.*",
            :joins => %(as m INNER JOIN #{Tagship.table_name} AS ts ON m.id=ts.model_id
                             INNER JOIN #{Tag.table_name} AS t ON ts.tag_id=t.id),
            :conditions =>["ts.model_type=? and t.user_id=?", name, user_id],
            :limit => 20}.merge(options))
    end

    alias find_all_tagged_by find_tagged_by

    # A hack from http://errtheblog.com/post/4791
    # will_paginate will call find_all_tagged_with, as in the example:
    # @stories = Story.paginate_tagged_with("goo", :total_entries => 2,  :page => params[:page])
    alias find_all_tagged_with find_tagged_with
  end

  module InstanceMethods  
    # create new or overwrite tags, without touching overlapping tags.
    def tag(str, user_id)
      old_tags = tag_names(user_id)
      new_tags = self.class.split_tag_names(str)

      (new_tags - old_tags).each{|tag|
        self.tags << Tag.find_or_create_by_name_and_model_type_and_user_id(tag, self.class.name, user_id)
      }
      (old_tags - new_tags).each{|tag|  
        tag_obj = Tag.find_by_name_and_model_type_and_user_id(tag, self.class.name, user_id)
        self.tags.delete(tag_obj)
        tag_obj.destroy if tag_obj.tagships_count == 1 # cached 1
      }

      update_attribute(:tags_cache, all_tag_names.to_yaml) if respond_to? :tags_cache
    end

    def tag_names(user_id)
      tags = self.tags.find_all_by_user_id(user_id)
      tags.collect{|tag| tag.name}.sort!
    end

    def all_tag_names
      tags.collect { |tag| tag.name }.sort!
    end
  end
end