module Taggable
def self.included(base) #:nodoc:
base.extend(ClassMethods)
# base.tag_initialize
end
###################################################################
# Class-level methods
###################################################################
module ClassMethods
attr_accessor :objects_with_tag
# def tag_initialize
# puts "initializing tag object for: #{self.inspect}"
# @objects_with_tag ||= {}
# end
# Returns all instances (of the base class that included this file) that have the given tag.
def with_tag(tag)
# @objects_with_tag ||= {}
return @objects_with_tag[tag] || []
end
# Call this method to add the given object instance to the list of instances for the given tag.
def add_object_with_tag(object, tag)
# @objects_with_tag ||= {}
if @objects_with_tag[tag]
@objects_with_tag[tag] << object unless @objects_with_tag[tag].include?(object)
else
@objects_with_tag[tag] = [object]
puts "tag added #{tag}"
end
end
def remove_object_with_tag(object, tag)
# @objects_with_tag ||= {}
if @objects_with_tag[tag]
@objects_with_tag[tag].delete(object)
if @objects_with_tag[tag].empty?
@objects_with_tag.delete(tag)
# TODO: notify of tag deletion
puts "tag deleted #{tag}"
end
end
end
def tags
# @objects_with_tag ||= {}
return @objects_with_tag.keys
end
end
###################################################################
# Object-level methods
###################################################################
def valid_tag?(tag)
return false if tag.empty? # TODO: also check for pure whitespace ("\t ")
return true
end
# Adding
def add_tag(tag)
tag = clean_tag(tag)
return self unless valid_tag?(tag) # NOTE: silently ignores bad tags
unless has_tag?(tag)
# add to object's list of tags
@tags ||= {}
@tags[tag] = true
# add to class-level list of tags
self.class.add_object_with_tag(self, tag)
end
return self
end
def tags=(tags)
remove_tags
tags.each { |tag| add_tag(tag) }
end
#
# Querying
#
# Returns list of tags on this instance.
def tags
tag_keys
end
# Returns true if this instance is tagged with the given tag, otherwise false.
def has_tag?(tag)
return (@tags and @tags.has_key?(tag))
end
# Yields each tag on this instance, one at a time.
def tag_each
tag_keys.each { |tag| yield tag }
return self
end
#
# Removing
#
def remove_tags
@tags.each { |tag| self.class.remove_object_with_tag(self, tag) } if @tags
@tags = Hash.new
return self
end
def remove_tag(tag)
@tags.delete(tag) if @tags
self.class.remove_object_with_tag(self, tag)
return self
end
private
# Utilities
def clean_tag(tag)
return tag.strip
end
def tag_keys
return (@tags || {}).keys
end
end