def acts_as_taggable(options = {})
options = { :collection => :tags, :tag_class_name => 'Tag', :tag_class_column_name => 'name', :normalizer=> Proc.new {|name| name}}.merge(options)
collection_name = options[:collection]
tag_model = options[:tag_class_name].constantize
tag_model_name = options[:tag_class_column_name]
normalizer = options[:normalizer]
if tag_model.table_name < self.table_name
default_join_table = "#{tag_model.table_name}_#{self.table_name}"
else
default_join_table = "#{self.table_name}_#{tag_model.table_name}"
end
options[:join_table] ||= default_join_table
options[:foreign_key] ||= self.name.to_s.foreign_key
options[:association_foreign_key] ||= tag_model.to_s.foreign_key
if join_class_name = options[:join_class_name]
Object.class_eval "class #{join_class_name} < ActiveRecord::Base; set_table_name '#{options[:join_table]}' end" unless Object.const_defined?(join_class_name)
join_model = join_class_name.constantize
tagged = self
join_model.class_eval do
belongs_to :tag, :class_name => tag_model.to_s
belongs_to :tagged, :class_name => tagged.name.to_s
define_method(:normalizer, normalizer)
define_method(tag_model_name.to_sym) { self[tag_model_name] ||= normalizer(tag.send(tag_model_name.to_sym)) }
end
options[:class_name] ||= join_model.to_s
tag_pk, tag_fk = tag_model.primary_key, options[:association_foreign_key]
t, tn, jt = tag_model.table_name, tag_model_name, join_model.table_name
options[:finder_sql] ||= "SELECT #{jt}.*, #{t}.#{tn} AS #{tn} FROM #{jt}, #{t} WHERE #{jt}.#{tag_fk} = #{t}.#{tag_pk} AND #{jt}.#{options[:foreign_key]} = \#{quoted_id}"
else
join_model = nil
end
write_inheritable_attribute(:tag_foreign_key, options[:association_foreign_key])
write_inheritable_attribute(:taggable_foreign_key, options[:foreign_key])
write_inheritable_attribute(:normalizer, normalizer)
write_inheritable_attribute(:tag_collection_name, collection_name)
write_inheritable_attribute(:tag_model, tag_model)
write_inheritable_attribute(:tag_model_name, tag_model_name)
write_inheritable_attribute(:tags_join_model, join_model)
write_inheritable_attribute(:tags_join_table, options[:join_table])
write_inheritable_attribute(:tag_options, options)
[ :collection, :tag_class_name, :tag_class_column_name, :join_class_name,:normalizer].each { |key| options.delete(key) }
[ :join_table, :association_foreign_key ].each { |key| options.delete(key) } if join_model
class_eval do
include ActiveRecord::Acts::Taggable::InstanceMethods
extend ActiveRecord::Acts::Taggable::SingletonMethods
class_inheritable_reader :tag_collection_name, :tag_model, :tag_model_name, :tags_join_model,
:tags_options, :tags_join_table,
:tag_foreign_key, :taggable_foreign_key,:normalizer
if join_model
has_many collection_name, options
else
has_and_belongs_to_many collection_name, options
end
end
end