Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
127 lines (109 sloc) 5.25 KB
module ActiveRecord
module Acts #:nodoc:
module Taggable #:nodoc:
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def acts_as_taggable(options = {})
has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag
has_many :tags, :through => :taggings, :order => 'name'
after_save :update_tags
extend ActiveRecord::Acts::Taggable::SingletonMethods
include ActiveRecord::Acts::Taggable::InstanceMethods
end
end
module SingletonMethods
# Pass a tag string, returns taggables that match the tag string.
#
# Options:
# :match - Match taggables matching :all or :any of the tags, defaults to :any
# :user - Limits results to those owned by a particular user
def find_tagged_with(tags, options = {})
options.assert_valid_keys([:match, :order, :user])
tags = Tag.parse(tags).collect {|t| Tag.select('id').where(:name => t).first}
group = "#{table_name}_taggings.taggable_id HAVING COUNT(#{table_name}_taggings.taggable_id) >= #{tags.size}" if options[:match] == :all
conditions = sanitize_sql(["taggings.tag_id IN (?)", tags])
conditions += sanitize_sql([" AND #{table_name}_taggings.user_id = ?", options[:user]]) if options[:user]
joins("LEFT OUTER JOIN taggings ON taggings.taggable_id = #{table_name}.#{primary_key} AND taggings.taggable_type = '#{name}'").where(conditions).group(group).order(options[:order])
end
#
# Pass a tag string, returns taggables that match the tag string for a particular user.
#
# Options:
# :match - Match taggables matching :all or :any of the tags, defaults to :any
def find_tagged_with_by_user(tags, user, options = {})
options.assert_valid_keys([:match, :order])
find_tagged_with(tags, {:match => options[:match], :order => options[:order], :user => user})
end
# Returns an array of related tags.
# Related tags are all the other tags that are found on the models tagged with the provided tags.
#
# Pass either a tag, string, or an array of strings or tags.
#
# Options:
# :order - SQL Order how to order the tags. Defaults to "count DESC, tags.name".
# :match - Match taggables matching :all or :any of the tags, defaults to :any
def find_related_tags(tags, options = {})
#duplicated work, the tags are parsed twice. I need to elimidate this by making find_tagged_with
#accept an array of tags and not just a string
parsed_tags = Tag.parse(tags)
related_models = find_tagged_with(tags, :match => options.delete(:match))
return [] if related_models.blank?
related_ids = related_models.to_s(:db)
Tag.find(:all, options.merge({
:select => "#{Tag.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
:joins => "JOIN #{Tagging.table_name} ON #{Tagging.table_name}.taggable_type = '#{base_class.name}'
AND #{Tagging.table_name}.taggable_id IN (#{related_ids})
AND #{Tagging.table_name}.tag_id = #{Tag.table_name}.id",
:order => options[:order] || "count DESC, #{Tag.table_name}.name",
:group => "#{Tag.table_name}.id, #{Tag.table_name}.name HAVING #{Tag.table_name}.name NOT IN (#{parsed_tags.map { |n| quote_value(n) }.join(",")})"
}))
end
end
module InstanceMethods
# Warning: Not updated until save is called!
# hmm, want to update cache before the item is saved so we don't have to do another query
def tag_list=(new_tag_list)
unless tag_list == new_tag_list
@new_tag_list = Tag.parse(new_tag_list)
self.tagnames = @new_tag_list.join(', ') if has_attribute?("tagnames")
end
end
def user_id=(new_user_id)
@new_user_id = new_user_id
super(new_user_id)
end
def tag_list(user = nil)
unless user
result = tags.collect { |tag| tag.name.include?(" ") ? %("#{tag.name}") : tag.name }.join(" ")
else
#TODO: make it work if I pass in an int instead of a user object
tags.find(:all, :conditions => ["#{Tagging.table_name}.user_id = ?", user.id]).collect { |tag| tag.name.include?(" ") ? %("#{tag.name}") : tag.name }.uniq.join(" ")
end
end
def update_tags
if @new_tag_list
Tag.transaction do
# this needs commenting. why only remove tags for the user?
unless @new_user_id
taggings.destroy_all
else
taggings.find(:all, :conditions => "user_id = #{@new_user_id}").each do |tagging|
tagging.destroy
end
end
#moved parse into tag_list = so we can assign to tagnames if present for caching
@new_tag_list.each do |name|
Tag.find_or_create_by_name(name).tag(self, @new_user_id)
end
tags.reset
taggings.reset
@new_tag_list = nil
end
end
end
end
end
end
end
Jump to Line
Something went wrong with that request. Please try again.