Permalink
Browse files

Initial commit - imported the annotations plugin from the BioCatalogu…

…e codebase
  • Loading branch information...
jits committed Jul 23, 2009
0 parents commit 5b50d473d65447630f8a9f4f6d7500f72f1f6593
Showing with 2,854 additions and 0 deletions.
  1. +2 −0 CHANGELOG
  2. +20 −0 MIT-LICENSE
  3. +9 −0 README
  4. +4 −0 RUNNING_UNIT_TESTS
  5. +22 −0 RakeFile
  6. +11 −0 generators/annotations_migration/annotations_migration_generator.rb
  7. +60 −0 generators/annotations_migration/templates/migration.rb
  8. +1 −0 init.rb
  9. +1 −0 install.rb
  10. +18 −0 lib/annotations.rb
  11. +220 −0 lib/annotations/acts_as_annotatable.rb
  12. +69 −0 lib/annotations/acts_as_annotation_source.rb
  13. +95 −0 lib/annotations/config.rb
  14. +7 −0 lib/annotations/routing.rb
  15. +110 −0 lib/annotations_version_fu.rb
  16. +162 −0 lib/app/controllers/annotations_controller.rb
  17. +2 −0 lib/app/controllers/application_controller.rb
  18. +2 −0 lib/app/helpers/application_helper.rb
  19. +329 −0 lib/app/models/annotation.rb
  20. +9 −0 lib/app/models/annotation_attribute.rb
  21. +14 −0 lib/app/models/annotation_value_seed.rb
  22. +7 −0 script/console
  23. +4 −0 tasks/annotations_tasks.rake
  24. +119 −0 test/acts_as_annotatable_test.rb
  25. +56 −0 test/acts_as_annotation_source_test.rb
  26. +22 −0 test/annotation_attribute_test.rb
  27. +168 −0 test/annotation_test.rb
  28. +14 −0 test/annotation_value_seed_test.rb
  29. +33 −0 test/annotation_version_test.rb
  30. +27 −0 test/annotations_controller_test.rb
  31. +9 −0 test/app_root/app/controllers/application_controller.rb
  32. +5 −0 test/app_root/app/models/book.rb
  33. +5 −0 test/app_root/app/models/chapter.rb
  34. +3 −0 test/app_root/app/models/group.rb
  35. +3 −0 test/app_root/app/models/user.rb
  36. +12 −0 test/app_root/app/views/annotations/edit.html.erb
  37. +1 −0 test/app_root/app/views/annotations/index.html.erb
  38. +11 −0 test/app_root/app/views/annotations/new.html.erb
  39. +3 −0 test/app_root/app/views/annotations/show.html.erb
  40. +115 −0 test/app_root/config/boot.rb
  41. +6 −0 test/app_root/config/database.yml
  42. +16 −0 test/app_root/config/environment.rb
  43. 0 test/app_root/config/environments/mysql.rb
  44. +4 −0 test/app_root/config/routes.rb
  45. +33 −0 test/app_root/db/migrate/001_create_test_models.rb
  46. +60 −0 test/app_root/db/migrate/002_annotations_migration.rb
  47. +279 −0 test/config_test.rb
  48. +39 −0 test/fixtures/annotation_attributes.yml
  49. +16 −0 test/fixtures/annotation_value_seeds.csv
  50. +259 −0 test/fixtures/annotation_versions.yml
  51. +239 −0 test/fixtures/annotations.yml
  52. +13 −0 test/fixtures/books.yml
  53. +27 −0 test/fixtures/chapters.yml
  54. +7 −0 test/fixtures/groups.yml
  55. +8 −0 test/fixtures/users.yml
  56. +27 −0 test/routing_test.rb
  57. +37 −0 test/test_helper.rb
@@ -0,0 +1,2 @@
+
+
@@ -0,0 +1,20 @@
+Copyright (c) 2008 BioCatalogue
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9 README
@@ -0,0 +1,9 @@
+Annotations
+===========
+
+Allows for annotations to be added to multiple and different models.
+
+KNOWN ISSUES:
+=============
+
+- Will not work with sqlite and sqlite3 databases. Furthermore, not tested with Postgres.
@@ -0,0 +1,4 @@
+To run the tests for this plugin:
+- ensure the gem 'plugin_test_helper' is installed (only tested with v0.3.0).
+- ensure database 'annotations_plugin_test' exists on a local mysql server installation (with default ports) and username 'root' with blank password.
+- run: rake test:plugins PLUGIN=annotations
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the annotations plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the annotations plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'Annotations plugin'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
@@ -0,0 +1,11 @@
+class AnnotationsMigrationGenerator < Rails::Generator::Base
+ def manifest
+ record do |m|
+ m.migration_template 'migration.rb', 'db/migrate'
+ end
+ end
+
+ def file_name
+ "annotations_migration"
+ end
+end
@@ -0,0 +1,60 @@
+class AnnotationsMigration < ActiveRecord::Migration
+ def self.up
+ create_table :annotations, :force => true do |t|
+ t.string :source_type, :null => false
+ t.integer :source_id, :null => false
+ t.string :annotatable_type, :limit => 50, :null => false
+ t.integer :annotatable_id, :null => false
+ t.integer :attribute_id, :null => false
+ t.text :value, :limit => 20000, :null => false
+ t.string :value_type, :limit => 50, :null => false
+ t.integer :version, :null => false
+ t.integer :version_creator_id, :null => true
+ t.timestamps
+ end
+
+ add_index :annotations, [ :source_type, :source_id ]
+ add_index :annotations, [ :annotatable_type, :annotatable_id ]
+ add_index :annotations, [ :attribute_id ]
+
+ create_table :annotation_versions, :force => true do |t|
+ t.integer :annotation_id, :null => false
+ t.integer :version, :null => false
+ t.integer :version_creator_id, :null => true
+ t.string :source_type, :null => false
+ t.integer :source_id, :null => false
+ t.string :annotatable_type, :limit => 50, :null => false
+ t.integer :annotatable_id, :null => false
+ t.integer :attribute_id, :null => false
+ t.text :value, :limit => 20000, :null => false
+ t.string :value_type, :limit => 50, :null => false
+ t.timestamps
+ end
+
+ add_index :annotation_versions, [ :annotation_id ]
+
+ create_table :annotation_attributes, :force => true do |t|
+ t.string :name, :null => false
+
+ t.timestamps
+ end
+
+ add_index :annotation_attributes, [ :name ]
+
+ create_table :annotation_value_seeds, :force => true do |t|
+ t.integer :attribute_id, :null => false
+ t.string :value, :null => false
+
+ t.timestamps
+ end
+
+ add_index :annotation_value_seeds, [ :attribute_id ]
+ end
+
+ def self.down
+ drop_table :annotations
+ drop_table :annotation_versions
+ drop_table :annotation_attributes
+ drop_table :annotation_value_seeds
+ end
+end
@@ -0,0 +1 @@
+require File.join(File.dirname(__FILE__), "lib", "annotations")
@@ -0,0 +1 @@
+# Install hook code here
@@ -0,0 +1,18 @@
+require File.join(File.dirname(__FILE__), "annotations", "config")
+
+require File.join(File.dirname(__FILE__), "annotations_version_fu")
+
+%w{ models controllers helpers }.each do |dir|
+ path = File.join(File.dirname(__FILE__), 'app', dir)
+ $LOAD_PATH << path
+ ActiveSupport::Dependencies.load_paths << path
+ ActiveSupport::Dependencies.load_once_paths.delete(path)
+end
+
+require File.join(File.dirname(__FILE__), "annotations", "acts_as_annotatable")
+ActiveRecord::Base.send(:include, Annotations::Acts::Annotatable)
+
+require File.join(File.dirname(__FILE__), "annotations", "acts_as_annotation_source")
+ActiveRecord::Base.send(:include, Annotations::Acts::AnnotationSource)
+
+require File.join(File.dirname(__FILE__), "annotations", "routing")
@@ -0,0 +1,220 @@
+# ActsAsAnnotatable
+module Annotations
+ module Acts #:nodoc:
+ module Annotatable #:nodoc:
+
+ def self.included(base)
+ base.send :extend, ClassMethods
+ end
+
+ module ClassMethods
+ def acts_as_annotatable
+ has_many :annotations,
+ :as => :annotatable,
+ :dependent => :destroy,
+ :order => 'created_at ASC'
+
+ send :extend, SingletonMethods
+ send :include, InstanceMethods
+ end
+ end
+
+ # Class methods added to the model that has been made acts_as_annotatable (ie: the mixin annotatable type).
+ module SingletonMethods
+ # Helper finder to get all objects of the mixin annotatable type that have the specified attribute name and value.
+ #
+ # NOTE (1): both the attribute name and the value will be treated case insensitively.
+ def with_annotations_with_attribute_name_and_value(attribute_name, value)
+ return [ ] if attribute_name.blank? or value.nil?
+
+ obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s
+
+ anns = Annotation.find(:all,
+ :joins => :attribute,
+ :conditions => { :annotatable_type => obj_type,
+ :annotation_attributes => { :name => attribute_name.strip.downcase },
+ :value => value.strip.downcase })
+
+ return anns.map{|a| a.annotatable}
+ end
+
+ # Helper finder to get all annotations for an object of the mixin annotatable type with the ID provided.
+ # This is the same as object.annotations with the added benefit that the object doesnt have to be loaded.
+ # E.g: Book.find_annotations_for(34) will give all annotations for the Book with ID 34.
+ def find_annotations_for(id)
+ obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s
+
+ Annotation.find(:all,
+ :conditions => { :annotatable_type => obj_type,
+ :annotatable_id => id },
+ :order => "created_at DESC")
+ end
+
+ # Helper finder to get all annotations for all objects of the mixin annotatable type, by the source specified.
+ # E.g: Book.find_annotations_by('User', 10) will give all annotations for all Books by User with ID 10.
+ def find_annotations_by(source_type, source_id)
+ obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s
+
+ Annotation.find(:all,
+ :conditions => { :annotatable_type => obj_type,
+ :source_type => source_type,
+ :source_id => source_id },
+ :order => "created_at DESC")
+ end
+ end
+
+ # This module contains instance methods
+ module InstanceMethods
+
+ # Provides a default implementation to get the display name for
+ # an annotatable object, that can be overrided.
+ def annotatable_name
+ %w{ display_name title name }.each do |w|
+ return eval("self.#{w}") if self.respond_to?(w)
+ end
+ return "#{self.class.name}_#{id}"
+ end
+
+ # Helper method to get latest annotations
+ def latest_annotations(limit=nil)
+ obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self.class).to_s
+
+ Annotation.find(:all,
+ :conditions => { :annotatable_type => obj_type,
+ :annotatable_id => self.id },
+ :order => "created_at DESC",
+ :limit => limit)
+ end
+
+ # Finder to get annotations with a specific attribute.
+ # The input parameter is the attribute name
+ # (MUST be a String representing the attribute's name).
+ def annotations_with_attribute(attrib)
+ return [] if attrib.blank?
+
+ obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self.class).to_s
+
+ Annotation.find(:all,
+ :joins => :attribute,
+ :conditions => { :annotatable_type => obj_type,
+ :annotatable_id => self.id,
+ :annotation_attributes => { :name => attrib.strip.downcase } },
+ :order => "created_at DESC")
+ end
+
+ # Same as the {obj}.annotations_with_attribute method (above) but
+ # takes in an array for attribute names to look for.
+ #
+ # NOTE (1): the argument to this method MUST be an Array of Strings.
+ def annotations_with_attributes(attribs)
+ return [] if attribs.blank?
+
+ obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self.class).to_s
+
+ Annotation.find(:all,
+ :joins => :attribute,
+ :conditions => { :annotatable_type => obj_type,
+ :annotatable_id => self.id,
+ :annotation_attributes => { :name => attribs } },
+ :order => "created_at DESC")
+ end
+
+ # Finder to get annotations with a specific attribute by a specific source.
+ #
+ # The first input parameter is the attribute name (MUST be a String representing the attribute's name).
+ # The second input is the source object.
+ def annotations_with_attribute_and_by_source(attrib, source)
+ return [] if attrib.blank? or source.nil?
+
+ obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self.class).to_s
+
+ Annotation.find(:all,
+ :joins => :attribute,
+ :conditions => { :annotatable_type => obj_type,
+ :annotatable_id => self.id,
+ :source_type => source.class.name,
+ :source_id => source.id,
+ :annotation_attributes => { :name => attrib.strip.downcase } },
+ :order => "created_at DESC")
+ end
+
+ # Finder to get all annotations on this object excluding those that
+ # have the attribute names specified.
+ #
+ # NOTE (1): the argument to this method MUST be an Array of Strings.
+ # NOTE (2): the returned records will be Read Only.
+ def all_annotations_excluding_attributes(attribs)
+ return [] if attribs.blank?
+
+ obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self.class).to_s
+
+ Annotation.find(:all,
+ :joins => :attribute,
+ :conditions => [ "`annotations`.`annotatable_type` = ? AND `annotations`.`annotatable_id` = ? AND `annotation_attributes`.`name` NOT IN (?)",
+ obj_type,
+ self.id,
+ attribs ],
+ :order => "`annotations`.`created_at` DESC")
+ end
+
+ # Returns the number of annotations on this annotatable object by the source type specified.
+ # "all" (case insensitive) can be provided to get all annotations regardless of source type.
+ # E.g.: book.count_annotations_by("User") or book.count_annotations_by("All")
+ def count_annotations_by(source_type_in)
+ if source_type_in == nil || source_type_in.downcase == "all"
+ return self.annotations.count
+ else
+ return self.annotations.count(:conditions => { :source_type => source_type_in })
+ end
+ end
+
+ # Use this method to create many annotations from a Hash of data.
+ # Arrays for Hash values will be converted to multiple annotations.
+ # Blank values (nil or empty string) will be ignored and thus annotations
+ # will not be created for them.
+ #
+ # Returns an array of Annotation objects of the annotations that were
+ # successfully created.
+ #
+ # Code example:
+ # -------------
+ # data = { "tag" => [ "tag1", "tag2", "tag3" ], "description" => "This is a book" }
+ # book.create_annotations(data, current_user)
+ def create_annotations(annotations_data, source)
+ anns = [ ]
+
+ annotations_data.each do |attrib, val|
+ unless val.blank?
+ if val.is_a? Array
+ val.each do |val_inner|
+ unless val_inner.blank?
+ ann = self.annotations << Annotation.new(:attribute_name => attrib,
+ :value => val_inner,
+ :source_type => source.class.name,
+ :source_id => source.id)
+
+ unless ann.nil? || ann == false
+ anns << ann
+ end
+ end
+ end
+ else
+ ann = self.annotations << Annotation.new(:attribute_name => attrib,
+ :value => val,
+ :source_type => source.class.name,
+ :source_id => source.id)
+
+ unless ann.nil? || ann == false
+ anns << ann
+ end
+ end
+ end
+ end
+
+ return anns
+ end
+ end
+
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 5b50d47

Please sign in to comment.