diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/has_tag_string/README.txt | 1 | ||||
-rw-r--r-- | lib/has_tag_string/has_tag_string.rb | 165 |
2 files changed, 166 insertions, 0 deletions
diff --git a/lib/has_tag_string/README.txt b/lib/has_tag_string/README.txt new file mode 100644 index 000000000..0d3a38229 --- /dev/null +++ b/lib/has_tag_string/README.txt @@ -0,0 +1 @@ +Plugin used only in WhatDoTheyKnow right now. diff --git a/lib/has_tag_string/has_tag_string.rb b/lib/has_tag_string/has_tag_string.rb new file mode 100644 index 000000000..4022faaac --- /dev/null +++ b/lib/has_tag_string/has_tag_string.rb @@ -0,0 +1,165 @@ +# lib/has_tag_string.rb: +# Lets a model have tags, represented as space separate strings in a public +# interface, but stored in the database as keys. Each tag can have a value +# followed by a colon - e.g. url:http://www.flourish.org +# +# Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ + +module HasTagString + # Represents one tag of one model. + # The migration to make this is currently only in WDTK code. + class HasTagStringTag < ActiveRecord::Base + # XXX strip_attributes! + + validates_presence_of :name + + # Return instance of the model that this tag tags + def tagged_model + return self.model.constantize.find(self.model_id) + end + + # For display purposes, returns the name and value as a:b, or + # if there is no value just the name a + def name_and_value + ret = self.name + if !self.value.nil? + ret += ":" + self.value + end + return ret + end + + # Parses a text version of one single tag, such as "a:b" and returns + # the name and value, with nil for value if there isn't one. + def HasTagStringTag.split_tag_into_name_value(tag) + sections = tag.split(/:/) + name = sections[0] + if sections[1] + value = sections[1,sections.size].join(":") + else + value = nil + end + return name, value + end + end + + # Methods which are added to the model instances being tagged + module InstanceMethods + # Given an input string of tags, sets all tags to that string. + # XXX This immediately saves the new tags. + def tag_string=(tag_string) + if tag_string.nil? + tag_string = "" + end + + tag_string = tag_string.strip + # split tags apart + tags = tag_string.split(/\s+/).uniq + + ActiveRecord::Base.transaction do + for tag in self.tags + tag.destroy + end + self.tags = [] + for tag in tags + # see if is a machine tags (i.e. a tag which has a value) + name, value = HasTagStringTag.split_tag_into_name_value(tag) + + tag = HasTagStringTag.new( + :model => self.class.base_class.to_s, + :model_id => self.id, + :name => name, :value => value + ) + self.tags << tag + end + end + end + + # Returns the tags the model has, as a space separated string + def tag_string + return self.tags.map { |t| t.name_and_value }.join(' ') + end + + # Returns the tags the model has, as an array of pairs of key/value + # (this can't be a dictionary as you can have multiple instances of a + # key with different values) + def tag_array + return self.tags.map { |t| [t.name, t.value] } + end + + # Returns a list of all the strings someone might want to search for. + # So that is the key by itself, or the key and value. + # e.g. if a request was tagged openlylocal_id:12345, they might + # want to search for "openlylocal_id" or for "openlylocal_id:12345" to find it. + def tag_array_for_search + ret = {} + for tag in self.tags + ret[tag.name] = 1 + ret[tag.name_and_value] = 1 + end + + return ret.keys.sort + end + + # Test to see if class is tagged with the given tag + def has_tag?(tag_as_string) + for tag in self.tags + if tag.name == tag_as_string + return true + end + end + return false + end + + class TagNotFound < StandardError + end + + # If the tag is a machine tag, returns array of its values + def get_tag_values(tag_as_string) + found = false + results = [] + for tag in self.tags + if tag.name == tag_as_string + found = true + if !tag.value.nil? + results << tag.value + end + end + end + if !found + raise TagNotFound + end + return results + end + + # Adds a new tag to the model, if it isn't already there + def add_tag_if_not_already_present(tag_as_string) + self.tag_string = self.tag_string + " " + tag_as_string + end + end + + # Methods which are added to the model class being tagged + module ClassMethods + # Find all public bodies with a particular tag + def find_by_tag(tag_as_string) + return HasTagStringTag.find(:all, :conditions => + ['name = ? and model = ?', tag_as_string, self.to_s ] + ).map { |t| t.tagged_model }.sort { |a,b| a.name <=> b.name }.uniq + end + end + + ###################################################################### + # Main entry point, add has_tag_string to your model. + module HasMethods + def has_tag_string() + has_many :tags, :conditions => "model = '" + self.to_s + "'", :foreign_key => "model_id", :class_name => 'HasTagString::HasTagStringTag' + + include InstanceMethods + self.class.send :include, ClassMethods + end + end + +end + +ActiveRecord::Base.extend HasTagString::HasMethods + |