Skip to content

Instantly share code, notes, and snippets.

@royw
Created September 21, 2009 20:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save royw/190546 to your computer and use it in GitHub Desktop.
Save royw/190546 to your computer and use it in GitHub Desktop.
##
# Container object for a set boolean Tag objects
class Tagset
include DataMapper::Resource
include DataMapper::Timestamp
include DataMapper::Serialize
property :id, Serial
property :name, String, :index => true, :nullable => false
property :select_mode, Boolean, :index => true, :default => true
property :asserted_tags, Text, :default => '', :lazy => true
property :created_at, DateTime
property :updated_at, DateTime
has n, :tags, :through => Resource
SELECT_TAGSET_NAME = 'Select'
EXCLUDE_TAGSET_NAME = 'Exclude'
@@logger = Log4r::Logger[APP]
##
# Get the Tagset used by the filters for selecting tags
def self.select_tags
Tagset.find_or_create(:name => SELECT_TAGSET_NAME)
end
##
# Get the Tagset used by the filters for excluding tags
def self.exclude_tags
Tagset.find_or_create(:name => EXCLUDE_TAGSET_NAME)
end
##
# find or create the record specified by the given options Hash
#
# @param [Hash] options find options
def self.find_or_create(options)
obj = self.first(options)
if obj.nil?
obj = self.create(options)
obj.save
end
obj
end
##
# destroy orphaned Tagsets. Orphan is defined as any Tagset
# without a provider except for the two global Tagsets
# (select_tags and exclude_tags).
def self.clean
tagsets = Tagset.all.select{|ts| ts.provider.nil? && ts.view.nil? } - [Tagset.select_tags, Tagset.exclude_tags]
tagsets.each do |ts|
ts.destroy
end
end
##
# set the select mode
#
# @param [Boolean] mode asserted means 'OR' where deasserted means 'AND'
def set_select_mode(mode)
self.select_mode = mode
self.save
end
##
# get the selected (asserted) Tags in this Tagset
#
# @return [Array] set of asserted Tags
def selected
self.tags.reload
self.tags
end
##
# get the set of deselected (deasserted) Tags in this Tagset
#
# @return [Array] set of deasserted Tags
def deselected
Tag.all - selected()
end
##
# is the named Tag asserted?
#
# @param [String] name the name of a Tag object
# @return [Boolean] asserted if the named Tag is asserted in this Tagset
def asserted?(name)
result = false
tag = Tag.first(:name => name)
unless tag.nil?
result = !TagTagset.first(:tagset_id => self.id, :tag_id => tag.id).nil?
end
result
end
##
# set the state of the named Tag
#
# @param [String] name the name of a Tag object
# @param [Boolean] state the state to set the named Tag to
def set_tag(name, state)
if state
assert_tag(name)
else
deassert_tag(name)
end
end
##
# replace the current set of states with the given set of states
# note, assumes same set of tags
#
# @param [Hash] tag_states where the hash key is the Tag name and the hash value is the tag state.
# @return [Hash] the new set of states where the hash key is the Tag name and the hash value is the tag state.
def set_states(tag_states)
tag_states.each do |name, state|
set_tag(name, state)
end
states()
end
##
# get the current set of Tag states
#
# @return [Hash] where the hash key is the Tag name and the hash value is the tag state.
def states
states = {}
selected().each{|tag| states[tag.name.to_s] = true}
deselected().each{|tag| states[tag.name.to_s] = false}
states
end
##
# get the set of Tag names regardless of state
#
# @return [Array] an Array of String tag names
def names
tag_names = Tag.all.collect{ |tag| tag.name }.sort
tag_names
end
##
# get a String that represents the Tagset
#
# @return [String] "ALL" | "NONE" | "name1 | name2 |..." | "name1 & name2 &..."
def to_s
buf = []
buf << (self.select_mode ? 'OR' : 'AND') + ' mode'
sel = selected()
if sel.empty?
buf << 'NONE'
else
if deselected().empty?
buf << 'ALL'
else
buf << sel.collect{|tag| tag.name}.sort.join(select_mode ? ' | ' : ' & ')
end
end
buf.join(' ')
end
def from_s(str)
self.select_mode = (str =~ /^\s*OR\s+mode/)
if str =~ /\sALL\s/
assert_all_tags
else
deassert_all_tags
unless str =~ /\sNONE\s/
names = str.split(' ')
if names[1] == 'mode'
names.shift
names.shift
end
names.select{|name| name !~ /^\&|\|$/}.each {|name| assert_tag(name)}
end
end
end
protected
def deassert_all_tags
deselected().each { |tag| deassert_tag(tag.name) }
end
##
# assert the Tag with the given Tag name
#
# @param [String] name the name of a Tag object
def assert_tag(name)
unless name =~ /^ALL|NONE$/
tag = Tag.first(:name => name)
tag = self.tags.first(:name => name)
if tag.nil?
@@logger.debug{"asserting tag name => #{name}"}
tag = Tag.find_or_create(name)
@@logger.debug{"tag => #{tag.inspect}"}
@@logger.debug{"tagset => #{self.inspect}"}
@@logger.debug{"tags => #{self.tags.inspect}"}
@@logger.error{"self.tags.save failed"} unless self.tags.save
@@logger.error{"self.save failed"} unless self.save
@@logger.error{"tag.save failed"} unless tag.save
self.tags << tag
@@logger.error{"self.tags.save failed"} unless self.tags.save
@@logger.error{"self.save failed"} unless self.save
self.tags.reload
end
end
end
##
# deassert the Tag with the given Tag name
#
# @param [String] name the name of a Tag object
def deassert_tag(name)
tag = self.tags.first(:name => name)
unless tag.nil?
self.tag_tagsets(:tag_id => tag.id).each do |intermediate|
@@logger.warn{"error return from destroy"} unless intermediate.destroy
end
self.tags.reload
self.save
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment