DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world
Extending Acts_as_taggable To Take Scope Into Account
The acts_as_taggable plugin is great and so useful. But it discards scope, and it doesn't work like any other model attribute in your controller views [using normal @model.update(params[:model]) calls].
I have changed it to:
* add an alias tag_list= for tag_with (found in the "rails wiki":http://wiki.rubyonrails.com/rails/pages/ActsAsTaggablePluginHowto),
* updated find_tagged_with and count_tagged_with to use scope (after reading an article from Jamis on "ActiveRecord::Base.find":http://weblog.jamisbuck.org/2006/11/20/under-the-hood-activerecord-base-find-part-2)
Scope is used when doing this for instance:
Message.with_scope :find => {:conditions => ["project_id = ?", @project]} do
@messages = Message.find_tagged_with(tag.name)
end
Then only the messages tagged with tag.name that belong to project @project. *acts_as_taggable.rb*
module ActiveRecord
module Acts #:nodoc:
module Taggable #:nodoc:
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def acts_as_taggable(options = {})
write_inheritable_attribute(:acts_as_taggable_options, {
:taggable_type => ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s,
:from => options[:from]
})
class_inheritable_reader :acts_as_taggable_options
has_many :taggings, :as => :taggable, :dependent => true
has_many :tags, :through => :taggings
include ActiveRecord::Acts::Taggable::InstanceMethods
extend ActiveRecord::Acts::Taggable::SingletonMethods
end
end
module SingletonMethods
def find_tagged_with(list)
tagged_with list
end
def count_tagged_with(list)
tagged_with list, :count
end
# DRY sql query and handle scope
protected
def tagged_with(list, type = :find)
if type == :count
sql = "SELECT COUNT(DISTINCT #{table_name}.#{primary_key}) AS cnt "
else
sql = "SELECT DISTINCT #{table_name}.* "
end
sql << "FROM #{table_name}, tags, taggings "
conditions = [
"#{table_name}.#{primary_key} = taggings.taggable_id " +
"AND taggings.taggable_type = ? " +
"AND taggings.tag_id = tags.id AND tags.name IN (?) ",
acts_as_taggable_options[:taggable_type], list
]
add_conditions!(sql, conditions)
result = find_by_sql(sql)
(type == :count) ? result.first.cnt.to_i : result
end
end
module InstanceMethods
def tag_with(list)
Tag.transaction do
taggings.destroy_all
Tag.parse(list).each do |name|
if acts_as_taggable_options[:from]
send(acts_as_taggable_options[:from]).tags.find_or_create_by_name(name).on(self)
else
Tag.find_or_create_by_name(name).on(self)
end
end
end
end
# allow using active record update_attributes() and others by using tag_list to set
# and read the tag list
alias tag_list= tag_with
def tag_list
tags.collect { |tag| tag.name.include?(" ") ? "'#{tag.name}'" : tag.name }.join(" ")
end
end
end
end
end





