Original Author: | Jiten Bhagat (mail@jits.co.uk) |
Copyright: | © 2008-2009, the University of Manchester and the European Bioinformatics Institute (EMBL-EBI) |
License: | BSD |
Version: | 0.1.0 |
This plugin allows arbitrary metadata to be stored and retrieved for any model object in your Ruby on Rails (v2.2+) application. This metadata is in the form of attribute/value data pairs that, together with provenance information, make up ‘annotations’. These annotations are “attached” to a model object in your application.
The idea is to abstract out and decouple annotations from your application’s core data model and allow layering in of metadata easily.
Examples of things you can use the annotations plugin for:
The main benefit is that annotations are stored and retrieved in a uniform manner with a common API. Parts of the plugin can also be extended and overridden in your application (see the Usage section for more info).
The annotations plugin is currently being used successfully in the BioCatalogue codebase to allow easy annotation of web services and their deployments/versions/operations/inputs/outputs/etc. Check out www.biocatalogue.org.
To generate full documentation (including this readme and full API docs) do the following:
Install RDoc (v2.3.0 or above):
% [sudo] gem install rdoc
Install the darkfish-rdoc gem:
% [sudo] gem install darkfish-rdoc
Run the following command in the plugin’s root directory (vendor/plugins/annotations):
% rdoc -SHNU -w 2 --title='Annotations Plugin' -m 'INDEX.rdoc' -x '(/doc|/tasks|/script|/test)'
The generated documentation will be in the doc folder of the annotations plugin.
At a conceptual level, an Annotation consists of:
This can be represented as:
Annotation = Annotatable + Source + Attribute + Value + Value Type + Version Info + Timestamps
The database model for an Annotation is made up of the following fields:
| id | annotatable_type | annotatable_id | source_type | source_id | attribute_id | value | value_type | version | version_creator_id | created_at | updated_at |
This makes use of ActiveRecord polymorphic relationships to allow any ActiveRecord model to be an Annotatable or Source.
Note: The version_creator_id field is only relevant when there are more than one version of an Annotation. I.e.: for the first version of the annotation the version_creator_id will be nil.
Annotation Attributes currently only consist of a name. This is the attribute or property you are describing for an Annotatable. Examples: ‘tag’, ‘description’, etc.
Whenever an Annotation is updated, a new AnnotationVersion entry is created in the db (corresponding model name = Annotation::Version).
This uses a library from the version_fu plugin. A customised version of this is embedded within the Annotations plugin (which won’t conflict if version_fu is installed in the main codebase).
To install the plugin as a git cloned repo (this allows you to easily update the plugin in the future):
ruby script/plugin install git://github.com/jits/annotations.git
Note: this requires git to be installed.
To install the plugin the regular way:
ruby script/plugin install http://github.com/jits/annotations.git/
ruby script/generate annotations_migration all
require_dependency File.join(Rails.root, 'vendor', 'plugins', 'annotations', 'lib', 'app', 'controllers', 'application_controller')
require_dependency File.join(Rails.root, 'vendor', 'plugins', 'annotations', 'lib', 'app', 'helpers', 'application_helper')
Note: the require_dependency line is crucial in making your app work with the files in the plugin and allows for easy extension and overriding of the plugin parts. *However, this does mean that any file with the require_dependency line will not be automagically loaded by Rails in development mode when changes have been made - a server restart is required whenever any changes are made to these files.*
# app/models/annotation.rb # # This extends the Annotation model defined in the Annotations plugin. require_dependency File.join(Rails.root, 'vendor', 'plugins', 'annotations', 'lib', 'app', 'models', 'annotation') class Annotation < ActiveRecord::Base end
# app/models/annotation_attribute.rb # # This extends the AnnotationAttribute model defined in the Annotations plugin. require_dependency File.join(Rails.root, 'vendor', 'plugins', 'annotations', 'lib', 'app', 'models', 'annotation_attribute') class AnnotationAttribute < ActiveRecord::Base end
# app/controllers/annotations_controller.rb # # This extends the AnnotationsController controller defined in the Annotations plugin. require_dependency File.join(Rails.root, 'vendor', 'plugins', 'annotations', 'lib', 'app', 'controllers', 'annotations_controller') class AnnotationsController < ApplicationController end
These are used to extend/override the models and controller from the plugin with extra actions, filters, validations, processing and so on (see Usage section for more details and examples).
class Service < ActiveRecord::Base ... acts_as_annotatable ... end
class User < ActiveRecord::Base ... acts_as_annotation_source ... end
The Annotations plugin has now been installed and is ready for use. See the Usage section for further details.
book = Book.find(1) data = { :description => "My bookie wookie", :rating => 5, :tag => [ "horror", "romance", "indiluted", "true story" ] } new_annotations = book.create_annotations(data, current_user)
book = Book.find(10) ann1 = Annotation.new(:attribute_name => "tag", :value => "hot", :source_type => "User", :source_id => 100, :annotatable_type => book.class.name, :annotatable_id => book.id)
<% form_tag annotations_url do %> <%= hidden_field_tag "annotation[annotatable_type]", "Book" -%> <%= hidden_field_tag "annotation[annotatable_id]", 100 -%> <%= hidden_field_tag "annotation[attribute_name]", "description" -%> <%= text_area_tag "annotation[value]" -%> <%= submit_tag "Submit", :disable_with => "Submitting..." -%> <% end %>
<% form_tag create_multiple_annotations_url do %> <%= hidden_field_tag "separator", "," -%> <%= hidden_field_tag "annotation[annotatable_type]", "Book" -%> <%= hidden_field_tag "annotation[annotatable_id]", 100 -%> <%= hidden_field_tag "annotation[attribute_name]", "description" -%> <%= text_area_tag "annotation[value]" -%> <%= submit_tag "Submit", :disable_with => "Submitting..." -%> <% end %>
book = Book.find(6806) annotations = book.annotations
book = Book.find(23) tag_annotations = book.annotations_with_attribute("tag")
book = Book.find(22124) rating_annotations = book.annotations_with_attributes([ "rating-performance", "rating-usefulness", "rating-documentation" ])
See _More Examples_ below.
All the config options can be found in lib/annotations/config.rb, listed below:
TODO: explain and document these.
params = { :annotatable_type => "Book", :annotatable_id => 78, :attribute_name => "tag", :value => "workflow, taverna, brilliant", :source_type => "Group", :source_id => 4 } Annotation.create_multiple(params, ',')
book = Book.find(67) user = User.find_by_name("jane") annotations = book.annotations_with_attribute_and_by_source("comment", user)
book = Book.find(56) annotations = book.all_annotations_excluding_attributes([ "tag", "note", "example" ])
Annotation.find_annotatables_with_attribute_name_and_value("complexity", "O(x^2)")
Book.with_annotations_with_attribute_name_and_value("Tag", "Amusing rhetoric")
Annotation.find_annotatables_with_attribute_names_and_values([ "tag" ], [ "fiction", "sci-fi", "fantasy" ]) Annotation.find_annotatables_with_attribute_names_and_values([ "tag", "keyword", "category" ], [ "fiction", "fantasy" ])
book = Book.find(90) count = book.count_annotations_by("all")
book = Book.find(90) count = book.count_annotations_by("User")
TODO: add more advanced examples!
Most of the aspects of this plugin can be extended/overridden in your codebase.
Examples:
require_dependency RAILS_ROOT + '/vendor/plugins/annotations/lib/app/models/annotation' class Annotation < ActiveRecord::Base ... validates_inclusion_of :value, :in => [ "fruit", "nut", "fibre" ], :message => "Please select a valid category.", :if => Proc.new { |ann| ann.attribute_name.downcase == "category" } ... end
TODO: add more!
The BioCatalogue codebase contains many examples of using the Annotations plugin successfully. Some pointers:
The prototype Shims Library also makes use of the Annotations plugin successfully and may provide some useful examples.
Update the plugin code using the appropriate mechanism. Then:
ruby script/generate annotations_migration vX
… where X is the version of the migration you want to generate
TODO: list files and what the purpose of each is.
A copy of ApplicationController has been removed from the module tree but is still active!
… then restart your server. This is due to the way Rails autoloads files in development mode, which affects the plugin and any extensions in the main app.
In no particular order:
Table name: annotations
Fields:
Table name: annotation_versions
Fields:
Table name: annotation_attributes
Fields:
Table name: annotation_value_seeds
Fields:
Generated with the Darkfish Rdoc Generator 1.1.6.