aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/has_tag_string/README.txt1
-rw-r--r--lib/has_tag_string/has_tag_string.rb165
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
+