my_annotations 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +6 -0
 - data/.rvmrc +1 -0
 - data/AUTHORS.rdoc +5 -0
 - data/CHANGELOG.rdoc +64 -0
 - data/INDEX.rdoc +17 -0
 - data/generators/annotations_migration/annotations_migration_generator.rb +32 -0
 - data/generators/annotations_migration/templates/migration_v1.rb +60 -0
 - data/generators/annotations_migration/templates/migration_v2.rb +9 -0
 - data/generators/annotations_migration/templates/migration_v3.rb +74 -0
 - data/generators/annotations_migration/templates/migration_v4.rb +13 -0
 - data/install.rb +1 -0
 - data/lib/annotations/acts_as_annotatable.rb +271 -0
 - data/lib/annotations/acts_as_annotation_source.rb +117 -0
 - data/lib/annotations/acts_as_annotation_value.rb +115 -0
 - data/lib/annotations/config.rb +148 -0
 - data/lib/annotations/routing.rb +8 -0
 - data/lib/annotations/util.rb +82 -0
 - data/lib/annotations_version_fu.rb +119 -0
 - data/lib/app/controllers/annotations_controller.rb +162 -0
 - data/lib/app/controllers/application_controller.rb +2 -0
 - data/lib/app/helpers/application_helper.rb +2 -0
 - data/lib/app/models/annotation.rb +413 -0
 - data/lib/app/models/annotation_attribute.rb +37 -0
 - data/lib/app/models/annotation_value_seed.rb +48 -0
 - data/lib/app/models/number_value.rb +23 -0
 - data/lib/app/models/text_value.rb +23 -0
 - data/my_annotations.gemspec +4 -9
 - data/rails/init.rb +8 -0
 - data/test/acts_as_annotatable_test.rb +186 -0
 - data/test/acts_as_annotation_source_test.rb +84 -0
 - data/test/acts_as_annotation_value_test.rb +17 -0
 - data/test/annotation_attribute_test.rb +22 -0
 - data/test/annotation_test.rb +213 -0
 - data/test/annotation_value_seed_test.rb +14 -0
 - data/test/annotation_version_test.rb +39 -0
 - data/test/annotations_controller_test.rb +27 -0
 - data/test/app_root/app/controllers/application_controller.rb +9 -0
 - data/test/app_root/app/models/book.rb +5 -0
 - data/test/app_root/app/models/chapter.rb +5 -0
 - data/test/app_root/app/models/group.rb +3 -0
 - data/test/app_root/app/models/tag.rb +6 -0
 - data/test/app_root/app/models/user.rb +3 -0
 - data/test/app_root/app/views/annotations/edit.html.erb +12 -0
 - data/test/app_root/app/views/annotations/index.html.erb +1 -0
 - data/test/app_root/app/views/annotations/new.html.erb +11 -0
 - data/test/app_root/app/views/annotations/show.html.erb +3 -0
 - data/test/app_root/config/boot.rb +115 -0
 - data/test/app_root/config/environment.rb +16 -0
 - data/test/app_root/config/environments/mysql.rb +0 -0
 - data/test/app_root/config/routes.rb +4 -0
 - data/test/app_root/db/migrate/001_create_test_models.rb +38 -0
 - data/test/app_root/db/migrate/002_annotations_migration_v1.rb +60 -0
 - data/test/app_root/db/migrate/003_annotations_migration_v2.rb +9 -0
 - data/test/app_root/db/migrate/004_annotations_migration_v3.rb +72 -0
 - data/test/config_test.rb +383 -0
 - data/test/fixtures/annotation_attributes.yml +49 -0
 - data/test/fixtures/annotation_value_seeds.csv +16 -0
 - data/test/fixtures/annotation_versions.yml +259 -0
 - data/test/fixtures/annotations.yml +239 -0
 - data/test/fixtures/books.yml +13 -0
 - data/test/fixtures/chapters.yml +27 -0
 - data/test/fixtures/groups.yml +7 -0
 - data/test/fixtures/number_value_versions.csv +2 -0
 - data/test/fixtures/number_values.csv +2 -0
 - data/test/fixtures/text_value_versions.csv +35 -0
 - data/test/fixtures/text_values.csv +35 -0
 - data/test/fixtures/users.yml +8 -0
 - data/test/number_value_version_test.rb +40 -0
 - data/test/routing_test.rb +27 -0
 - data/test/test_helper.rb +41 -0
 - data/test/text_value_version_test.rb +40 -0
 - metadata +77 -7
 
    
        data/.gitignore
    ADDED
    
    
    
        data/.rvmrc
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            rvm --create use ree-1.8.7-2010.02@annotations
         
     | 
    
        data/AUTHORS.rdoc
    ADDED
    
    
    
        data/CHANGELOG.rdoc
    ADDED
    
    | 
         @@ -0,0 +1,64 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            = Changelog
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            == 0.4.1
         
     | 
| 
      
 4 
     | 
    
         
            +
            * Add +Annotation::include_values+ as a named scope that can be used to potentially improve performance.
         
     | 
| 
      
 5 
     | 
    
         
            +
            * Added +include_values+ optional argument (default: false) to as many finder methods. This allows you to specify whether value records must be included in the query results or not, and *may* be used to improve performance.
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            == 0.4.0
         
     | 
| 
      
 8 
     | 
    
         
            +
            * New config option: +Annotations::Config.value_factories+ - support for value object generation factories per attribute name.
         
     | 
| 
      
 9 
     | 
    
         
            +
            Example:    
         
     | 
| 
      
 10 
     | 
    
         
            +
                Annotations::Config.value_factories["tag"] = Proc.new { |v|
         
     | 
| 
      
 11 
     | 
    
         
            +
                  case v
         
     | 
| 
      
 12 
     | 
    
         
            +
                    when String, Symbol
         
     | 
| 
      
 13 
     | 
    
         
            +
                      Tag.find_or_create_by_name(v.to_s)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    else
         
     | 
| 
      
 15 
     | 
    
         
            +
                      v
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                }
         
     | 
| 
      
 18 
     | 
    
         
            +
            IMPORTANT: don't use explicit +return+s in your Proc otherwise it will cause the returning method to exit too!
         
     | 
| 
      
 19 
     | 
    
         
            +
            * The process of generating/setting the annotation's actual value object has been changed to "lazy generate" the actual +value+ object of the annotation. This is now done before validation. NOTE: this still allows +Annotation#value+ (which has been overridden) to be set at any time, but the actual setting of the value association at the ActiveRecord level now happens later.
         
     | 
| 
      
 20 
     | 
    
         
            +
            * The +process_value_adjustments+ code in the +Annotation+ model now happens BEFORE setting the value association. This prevents the value object from being modified after it's been set. NOTE: this does also mean that it will only run when provided with a String or Symbol.
         
     | 
| 
      
 21 
     | 
    
         
            +
            * New config option: +Annotations::Config.valid_value_types+ - support for validation checks based on the class of the value object.
         
     | 
| 
      
 22 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 23 
     | 
    
         
            +
                Annotations::Config::valid_value_types["tag"] = "Tag"
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            == 0.3.1
         
     | 
| 
      
 26 
     | 
    
         
            +
            * Minor bugfixes
         
     | 
| 
      
 27 
     | 
    
         
            +
            * +annotations_version_fu+ nows allows reloading of the versioned columns (needed if doing something during a migration).
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            == 0.3.0
         
     | 
| 
      
 30 
     | 
    
         
            +
            * +acts_as_annotatable+ now requires you to specify an option - +:name_field+ - on the model that is becoming an annotatable.
         
     | 
| 
      
 31 
     | 
    
         
            +
            * +acts_as_annotatable+ now exposes an +is_annotatable+ attribute to allow you to check if a model can act as an annotatable. 
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            == 0.2.1
         
     | 
| 
      
 34 
     | 
    
         
            +
            * Updated the routes to allow +requirements+ to be passed in.
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            == 0.2.0
         
     | 
| 
      
 37 
     | 
    
         
            +
            *Main change*: 
         
     | 
| 
      
 38 
     | 
    
         
            +
            Annotation values are now polymorphic rather than just plain strings. 
         
     | 
| 
      
 39 
     | 
    
         
            +
            Some basic +act_as_annotation_value+ models have been introduced for this. 
         
     | 
| 
      
 40 
     | 
    
         
            +
            Note: this has affected all methods that take in or work with annotation values. 
         
     | 
| 
      
 41 
     | 
    
         
            +
            See below for further details.
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            * New mixin module: +acts_as_annotation_value+.
         
     | 
| 
      
 44 
     | 
    
         
            +
            * New basic annotation value models: +TextValue+ and +NumberValue+ (but note that you can use any model as a value by specifying +acts_as_annotation_value+ on it).
         
     | 
| 
      
 45 
     | 
    
         
            +
            * Removed +Annotation::find_annotatables_with_attribute_name_and_value+.
         
     | 
| 
      
 46 
     | 
    
         
            +
            * Removed +Annotation::find_annotatables_with_attribute_names_and_values+.
         
     | 
| 
      
 47 
     | 
    
         
            +
            * Removed +with_annotations_with_attribute_name_and_value+ in the +acts_as_annotatable+ module.
         
     | 
| 
      
 48 
     | 
    
         
            +
            * +Annotations::Config::value_restrictions+ has been renamed to +Annotations::Config::content_restrictions+
         
     | 
| 
      
 49 
     | 
    
         
            +
            * Latest migration version = v3
         
     | 
| 
      
 50 
     | 
    
         
            +
            * NOTE: the new migration script will keep the old +value+ column data in a new +old_value+ column for EXISTING annotations only. This can be used for verification/text purposes.
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            == 0.1.1
         
     | 
| 
      
 53 
     | 
    
         
            +
            * Added +identifier+ to +AnnotationAttribute+. This can be used to specify what ontology term / URI the attribute can be
         
     | 
| 
      
 54 
     | 
    
         
            +
              uniquely identified using. See +AnnotationAttribute#before_validation+ for more information on how this identifier
         
     | 
| 
      
 55 
     | 
    
         
            +
              will be generated if not specified manually.
         
     | 
| 
      
 56 
     | 
    
         
            +
            * Changed the +annotations+ association in +act_as_annotation_source+ to +annotations_by+, to fix cases when a model has both
         
     | 
| 
      
 57 
     | 
    
         
            +
              +acts_as_annotatable+ AND +acts_as_annotation_source+.
         
     | 
| 
      
 58 
     | 
    
         
            +
            * Latest migration version = v2
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            == 0.1.0 (July 23rd 2009)
         
     | 
| 
      
 61 
     | 
    
         
            +
            * Initial import from the BioCatalogue codebase.
         
     | 
| 
      
 62 
     | 
    
         
            +
            * Improved documentation. See README.rdoc for more info on features and usage.
         
     | 
| 
      
 63 
     | 
    
         
            +
            * Latest migration version = v1
         
     | 
| 
      
 64 
     | 
    
         
            +
                          
         
     | 
    
        data/INDEX.rdoc
    ADDED
    
    | 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            = Annotations Plugin (for Ruby on Rails applications)
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Original Author::   	Jiten Bhagat (mailto:mail@jits.co.uk)
         
     | 
| 
      
 4 
     | 
    
         
            +
            Copyright::         	(c) 2008-2011, the University of Manchester and the European Bioinformatics Institute (EMBL-EBI)
         
     | 
| 
      
 5 
     | 
    
         
            +
            License::           	BSD
         
     | 
| 
      
 6 
     | 
    
         
            +
            Version::           	0.4.1
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            For information on the plugin and to get started, see README.rdoc
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            For credits and origins, see AUTHORS.rdoc
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            For license, see LICENSE
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            For the latest updates, see CHANGELOG.rdoc
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            To run the tests in the plugin, see RUNNING_TESTS.rdoc
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class AnnotationsMigrationGenerator < Rails::Generator::Base
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              attr_accessor :version
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              def initialize(*runtime_args)
         
     | 
| 
      
 6 
     | 
    
         
            +
                super(*runtime_args)
         
     | 
| 
      
 7 
     | 
    
         
            +
                if @args[0].nil?
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @version = "all"
         
     | 
| 
      
 9 
     | 
    
         
            +
                else
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @version = @args[0].downcase
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              def manifest
         
     | 
| 
      
 15 
     | 
    
         
            +
                record do |m|
         
     | 
| 
      
 16 
     | 
    
         
            +
                  if @version
         
     | 
| 
      
 17 
     | 
    
         
            +
                    if @version == "all"
         
     | 
| 
      
 18 
     | 
    
         
            +
                      Dir.chdir(File.join(File.dirname(__FILE__), "templates")) do
         
     | 
| 
      
 19 
     | 
    
         
            +
                        Dir.glob("*.rb").each do |f|
         
     | 
| 
      
 20 
     | 
    
         
            +
                          version = f.gsub(/.rb/, '').split('_')[1]
         
     | 
| 
      
 21 
     | 
    
         
            +
                          m.migration_template "migration_#{version}.rb", 'db/migrate', { :migration_file_name => "annotations_migration_#{version}" }
         
     | 
| 
      
 22 
     | 
    
         
            +
                          m.sleep 1   # So that the timestamps on the migration are not the same!
         
     | 
| 
      
 23 
     | 
    
         
            +
                        end
         
     | 
| 
      
 24 
     | 
    
         
            +
                      end
         
     | 
| 
      
 25 
     | 
    
         
            +
                    else
         
     | 
| 
      
 26 
     | 
    
         
            +
                      m.migration_template "migration_#{@version}.rb", 'db/migrate', { :migration_file_name => "annotations_migration_#{@version}" }
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,60 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class AnnotationsMigrationV1 < ActiveRecord::Migration
         
     | 
| 
      
 2 
     | 
    
         
            +
              def self.up
         
     | 
| 
      
 3 
     | 
    
         
            +
                create_table :annotations, :force => true do |t|
         
     | 
| 
      
 4 
     | 
    
         
            +
                  t.string    :source_type,         :null => false
         
     | 
| 
      
 5 
     | 
    
         
            +
                  t.integer   :source_id,           :null => false
         
     | 
| 
      
 6 
     | 
    
         
            +
                  t.string    :annotatable_type,    :limit => 50, :null => false
         
     | 
| 
      
 7 
     | 
    
         
            +
                  t.integer   :annotatable_id,      :null => false
         
     | 
| 
      
 8 
     | 
    
         
            +
                  t.integer   :attribute_id,        :null => false
         
     | 
| 
      
 9 
     | 
    
         
            +
                  t.text      :value,               :limit => 20000, :null => false
         
     | 
| 
      
 10 
     | 
    
         
            +
                  t.string    :value_type,          :limit => 50, :null => false
         
     | 
| 
      
 11 
     | 
    
         
            +
                  t.integer   :version,             :null => false
         
     | 
| 
      
 12 
     | 
    
         
            +
                  t.integer   :version_creator_id,  :null => true
         
     | 
| 
      
 13 
     | 
    
         
            +
                  t.timestamps
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
                
         
     | 
| 
      
 16 
     | 
    
         
            +
                add_index :annotations, [ :source_type, :source_id ]
         
     | 
| 
      
 17 
     | 
    
         
            +
                add_index :annotations, [ :annotatable_type, :annotatable_id ]
         
     | 
| 
      
 18 
     | 
    
         
            +
                add_index :annotations, [ :attribute_id ]
         
     | 
| 
      
 19 
     | 
    
         
            +
                
         
     | 
| 
      
 20 
     | 
    
         
            +
                create_table :annotation_versions, :force => true do |t|
         
     | 
| 
      
 21 
     | 
    
         
            +
                  t.integer   :annotation_id,       :null => false
         
     | 
| 
      
 22 
     | 
    
         
            +
                  t.integer   :version,             :null => false
         
     | 
| 
      
 23 
     | 
    
         
            +
                  t.integer   :version_creator_id,  :null => true
         
     | 
| 
      
 24 
     | 
    
         
            +
                  t.string    :source_type,         :null => false
         
     | 
| 
      
 25 
     | 
    
         
            +
                  t.integer   :source_id,           :null => false
         
     | 
| 
      
 26 
     | 
    
         
            +
                  t.string    :annotatable_type,    :limit => 50, :null => false
         
     | 
| 
      
 27 
     | 
    
         
            +
                  t.integer   :annotatable_id,      :null => false
         
     | 
| 
      
 28 
     | 
    
         
            +
                  t.integer   :attribute_id,        :null => false
         
     | 
| 
      
 29 
     | 
    
         
            +
                  t.text      :value,               :limit => 20000, :null => false
         
     | 
| 
      
 30 
     | 
    
         
            +
                  t.string    :value_type,          :limit => 50, :null => false
         
     | 
| 
      
 31 
     | 
    
         
            +
                  t.timestamps
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
                
         
     | 
| 
      
 34 
     | 
    
         
            +
                add_index :annotation_versions, [ :annotation_id ]
         
     | 
| 
      
 35 
     | 
    
         
            +
                
         
     | 
| 
      
 36 
     | 
    
         
            +
                create_table :annotation_attributes, :force => true do |t|
         
     | 
| 
      
 37 
     | 
    
         
            +
                  t.string :name, :null => false
         
     | 
| 
      
 38 
     | 
    
         
            +
                  
         
     | 
| 
      
 39 
     | 
    
         
            +
                  t.timestamps
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
                
         
     | 
| 
      
 42 
     | 
    
         
            +
                add_index :annotation_attributes, [ :name ]
         
     | 
| 
      
 43 
     | 
    
         
            +
                
         
     | 
| 
      
 44 
     | 
    
         
            +
                create_table :annotation_value_seeds, :force => true do |t|
         
     | 
| 
      
 45 
     | 
    
         
            +
                  t.integer :attribute_id,      :null => false
         
     | 
| 
      
 46 
     | 
    
         
            +
                  t.string  :value,  :null => false
         
     | 
| 
      
 47 
     | 
    
         
            +
                  
         
     | 
| 
      
 48 
     | 
    
         
            +
                  t.timestamps
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
                
         
     | 
| 
      
 51 
     | 
    
         
            +
                add_index :annotation_value_seeds, [ :attribute_id ]
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
              
         
     | 
| 
      
 54 
     | 
    
         
            +
              def self.down
         
     | 
| 
      
 55 
     | 
    
         
            +
                drop_table :annotations
         
     | 
| 
      
 56 
     | 
    
         
            +
                drop_table :annotation_versions
         
     | 
| 
      
 57 
     | 
    
         
            +
                drop_table :annotation_attributes
         
     | 
| 
      
 58 
     | 
    
         
            +
                drop_table :annotation_value_seeds
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,74 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class AnnotationsMigrationV3 < ActiveRecord::Migration
         
     | 
| 
      
 2 
     | 
    
         
            +
              def self.up
         
     | 
| 
      
 3 
     | 
    
         
            +
                
         
     | 
| 
      
 4 
     | 
    
         
            +
                change_table :annotations do |t|
         
     | 
| 
      
 5 
     | 
    
         
            +
                  t.rename :value, :old_value
         
     | 
| 
      
 6 
     | 
    
         
            +
                  t.remove :value_type
         
     | 
| 
      
 7 
     | 
    
         
            +
                  t.string :value_type, :limit => 50, :null => false, :default => "TextValue"
         
     | 
| 
      
 8 
     | 
    
         
            +
                  t.integer :value_id, :null => false, :default => 0
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
                change_column :annotations, :old_value, :string, :null => true
         
     | 
| 
      
 11 
     | 
    
         
            +
                add_index :annotations, [ :value_type, :value_id ]
         
     | 
| 
      
 12 
     | 
    
         
            +
                
         
     | 
| 
      
 13 
     | 
    
         
            +
                change_table :annotation_versions do |t|
         
     | 
| 
      
 14 
     | 
    
         
            +
                  t.rename :value, :old_value
         
     | 
| 
      
 15 
     | 
    
         
            +
                  t.remove :value_type
         
     | 
| 
      
 16 
     | 
    
         
            +
                  t.string :value_type, :limit => 50, :null => false, :default => "TextValue"
         
     | 
| 
      
 17 
     | 
    
         
            +
                  t.integer :value_id, :null => false, :default => 0
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
                change_column :annotation_versions, :old_value, :string, :null => true
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
                create_table :text_values, :force => true do |t|
         
     | 
| 
      
 22 
     | 
    
         
            +
                  t.integer :version, :null => false
         
     | 
| 
      
 23 
     | 
    
         
            +
                  t.integer :version_creator_id, :null => true
         
     | 
| 
      
 24 
     | 
    
         
            +
                  t.text :text, :limit => 16777214, :null => false
         
     | 
| 
      
 25 
     | 
    
         
            +
                  t.timestamps
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
                
         
     | 
| 
      
 28 
     | 
    
         
            +
                create_table :text_value_versions, :force => true do |t|
         
     | 
| 
      
 29 
     | 
    
         
            +
                  t.integer :text_value_id, :null => false
         
     | 
| 
      
 30 
     | 
    
         
            +
                  t.integer :version, :null => false
         
     | 
| 
      
 31 
     | 
    
         
            +
                  t.integer :version_creator_id, :null => true
         
     | 
| 
      
 32 
     | 
    
         
            +
                  t.text :text, :limit => 16777214, :null => false
         
     | 
| 
      
 33 
     | 
    
         
            +
                  t.timestamps
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
                add_index :text_value_versions, [ :text_value_id ]
         
     | 
| 
      
 36 
     | 
    
         
            +
                
         
     | 
| 
      
 37 
     | 
    
         
            +
                create_table :number_values, :force => true do |t|
         
     | 
| 
      
 38 
     | 
    
         
            +
                  t.integer :version, :null => false
         
     | 
| 
      
 39 
     | 
    
         
            +
                  t.integer :version_creator_id, :null => true
         
     | 
| 
      
 40 
     | 
    
         
            +
                  t.integer :number, :null => false
         
     | 
| 
      
 41 
     | 
    
         
            +
                  t.timestamps
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
                
         
     | 
| 
      
 44 
     | 
    
         
            +
                create_table :number_value_versions, :force => true do |t|
         
     | 
| 
      
 45 
     | 
    
         
            +
                  t.integer :number_value_id, :null => false
         
     | 
| 
      
 46 
     | 
    
         
            +
                  t.integer :version, :null => false
         
     | 
| 
      
 47 
     | 
    
         
            +
                  t.integer :version_creator_id, :null => true
         
     | 
| 
      
 48 
     | 
    
         
            +
                  t.integer :number, :null => false
         
     | 
| 
      
 49 
     | 
    
         
            +
                  t.timestamps
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
                add_index :number_value_versions, [ :number_value_id ]
         
     | 
| 
      
 52 
     | 
    
         
            +
                
         
     | 
| 
      
 53 
     | 
    
         
            +
                # Migrate existing annotations to the v3 db schema
         
     | 
| 
      
 54 
     | 
    
         
            +
                # 
         
     | 
| 
      
 55 
     | 
    
         
            +
                # TODO: IMPORTANT: please check the comments and logic in
         
     | 
| 
      
 56 
     | 
    
         
            +
                # this util method to see if it is what you want.
         
     | 
| 
      
 57 
     | 
    
         
            +
                # If you need to change the behaviour, redefine it in your app.
         
     | 
| 
      
 58 
     | 
    
         
            +
                Annotation::reset_column_information
         
     | 
| 
      
 59 
     | 
    
         
            +
                Annotation::reload_versioned_columns_info
         
     | 
| 
      
 60 
     | 
    
         
            +
                Annotations::Util::migrate_annotations_to_v3
         
     | 
| 
      
 61 
     | 
    
         
            +
                
         
     | 
| 
      
 62 
     | 
    
         
            +
                change_table :annotation_value_seeds do |t|
         
     | 
| 
      
 63 
     | 
    
         
            +
                  t.rename :value, :old_value
         
     | 
| 
      
 64 
     | 
    
         
            +
                  t.string :value_type, :limit => 50, :null => false, :default => "FIXME"
         
     | 
| 
      
 65 
     | 
    
         
            +
                  t.integer :value_id, :null => false, :default => 0
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
                change_column :annotation_value_seeds, :old_value, :string, :null => true
         
     | 
| 
      
 68 
     | 
    
         
            +
                
         
     | 
| 
      
 69 
     | 
    
         
            +
              end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
              def self.down
         
     | 
| 
      
 72 
     | 
    
         
            +
                raise ActiveRecord::IrreversibleMigration.new
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class AnnotationsMigrationV4 < ActiveRecord::Migration
         
     | 
| 
      
 2 
     | 
    
         
            +
              def self.up
         
     | 
| 
      
 3 
     | 
    
         
            +
                change_column :annotations,:version,:integer,:null=>true
         
     | 
| 
      
 4 
     | 
    
         
            +
                change_column :text_values,:version,:integer,:null=>true
         
     | 
| 
      
 5 
     | 
    
         
            +
                change_column :number_values,:version,:integer,:null=>true
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              def self.down
         
     | 
| 
      
 9 
     | 
    
         
            +
                change_column :annotations,:version,:integer,:null=>false
         
     | 
| 
      
 10 
     | 
    
         
            +
                change_column :text_values,:version,:integer,:null=>false
         
     | 
| 
      
 11 
     | 
    
         
            +
                change_column :number_values,:version,:integer,:null=>false
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
    
        data/install.rb
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Install hook code here
         
     | 
| 
         @@ -0,0 +1,271 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # ActsAsAnnotatable
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Annotations
         
     | 
| 
      
 3 
     | 
    
         
            +
              module Acts #:nodoc:
         
     | 
| 
      
 4 
     | 
    
         
            +
                module Annotatable #:nodoc:
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def self.included(base)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    base.send :extend, ClassMethods 
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  module ClassMethods
         
     | 
| 
      
 11 
     | 
    
         
            +
                    def acts_as_annotatable(options)
         
     | 
| 
      
 12 
     | 
    
         
            +
                      cattr_accessor :annotatable_name_field, :is_annotatable
         
     | 
| 
      
 13 
     | 
    
         
            +
                      
         
     | 
| 
      
 14 
     | 
    
         
            +
                      if options[:name_field].blank?
         
     | 
| 
      
 15 
     | 
    
         
            +
                        raise ArgumentError.new("Must specify the :name_field option that will be used as the field for the name")
         
     | 
| 
      
 16 
     | 
    
         
            +
                      end
         
     | 
| 
      
 17 
     | 
    
         
            +
                      
         
     | 
| 
      
 18 
     | 
    
         
            +
                      self.annotatable_name_field = options[:name_field] 
         
     | 
| 
      
 19 
     | 
    
         
            +
                      
         
     | 
| 
      
 20 
     | 
    
         
            +
                      has_many :annotations, 
         
     | 
| 
      
 21 
     | 
    
         
            +
                               :as => :annotatable, 
         
     | 
| 
      
 22 
     | 
    
         
            +
                               :dependent => :destroy, 
         
     | 
| 
      
 23 
     | 
    
         
            +
                               :order => 'updated_at ASC'
         
     | 
| 
      
 24 
     | 
    
         
            +
                               
         
     | 
| 
      
 25 
     | 
    
         
            +
                      __send__ :extend, SingletonMethods
         
     | 
| 
      
 26 
     | 
    
         
            +
                      __send__ :include, InstanceMethods
         
     | 
| 
      
 27 
     | 
    
         
            +
                      
         
     | 
| 
      
 28 
     | 
    
         
            +
                      self.is_annotatable = true
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # Class methods added to the model that has been made acts_as_annotatable (ie: the mixin target class).
         
     | 
| 
      
 33 
     | 
    
         
            +
                  module SingletonMethods
         
     | 
| 
      
 34 
     | 
    
         
            +
                    # Helper finder to get all annotations for an object of the mixin annotatable type with the ID provided.
         
     | 
| 
      
 35 
     | 
    
         
            +
                    # This is the same as object.annotations with the added benefit that the object doesnt have to be loaded.
         
     | 
| 
      
 36 
     | 
    
         
            +
                    # E.g: Book.find_annotations_for(34) will give all annotations for the Book with ID 34.
         
     | 
| 
      
 37 
     | 
    
         
            +
                    def find_annotations_for(id, include_values=false)
         
     | 
| 
      
 38 
     | 
    
         
            +
                      obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s
         
     | 
| 
      
 39 
     | 
    
         
            +
                      
         
     | 
| 
      
 40 
     | 
    
         
            +
                      options = { 
         
     | 
| 
      
 41 
     | 
    
         
            +
                        :conditions => { :annotatable_type =>  obj_type, 
         
     | 
| 
      
 42 
     | 
    
         
            +
                                         :annotatable_id => id },
         
     | 
| 
      
 43 
     | 
    
         
            +
                        :order => "updated_at DESC"
         
     | 
| 
      
 44 
     | 
    
         
            +
                      }
         
     | 
| 
      
 45 
     | 
    
         
            +
                      
         
     | 
| 
      
 46 
     | 
    
         
            +
                      options[:include] = [ :value ] if include_values
         
     | 
| 
      
 47 
     | 
    
         
            +
                      
         
     | 
| 
      
 48 
     | 
    
         
            +
                      Annotation.find(:all, options)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                    
         
     | 
| 
      
 51 
     | 
    
         
            +
                    # Helper finder to get all annotations for all objects of the mixin annotatable type, by the source specified.
         
     | 
| 
      
 52 
     | 
    
         
            +
                    # E.g: Book.find_annotations_by('User', 10) will give all annotations for all Books by User with ID 10. 
         
     | 
| 
      
 53 
     | 
    
         
            +
                    def find_annotations_by(source_type, source_id, include_values=false)
         
     | 
| 
      
 54 
     | 
    
         
            +
                      obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s
         
     | 
| 
      
 55 
     | 
    
         
            +
                      
         
     | 
| 
      
 56 
     | 
    
         
            +
                      options = {
         
     | 
| 
      
 57 
     | 
    
         
            +
                        :conditions => { :annotatable_type =>  obj_type, 
         
     | 
| 
      
 58 
     | 
    
         
            +
                                         :source_type => source_type,
         
     | 
| 
      
 59 
     | 
    
         
            +
                                         :source_id => source_id },
         
     | 
| 
      
 60 
     | 
    
         
            +
                        :order => "updated_at DESC"
         
     | 
| 
      
 61 
     | 
    
         
            +
                      }
         
     | 
| 
      
 62 
     | 
    
         
            +
                      
         
     | 
| 
      
 63 
     | 
    
         
            +
                      options[:include] = [ :value ] if include_values
         
     | 
| 
      
 64 
     | 
    
         
            +
                      
         
     | 
| 
      
 65 
     | 
    
         
            +
                      Annotation.find(:all, options)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
                  
         
     | 
| 
      
 69 
     | 
    
         
            +
                  # This module contains instance methods
         
     | 
| 
      
 70 
     | 
    
         
            +
                  module InstanceMethods
         
     | 
| 
      
 71 
     | 
    
         
            +
                    
         
     | 
| 
      
 72 
     | 
    
         
            +
                    # Gets the name of the annotatable object
         
     | 
| 
      
 73 
     | 
    
         
            +
                    def annotatable_name
         
     | 
| 
      
 74 
     | 
    
         
            +
                      self.send(self.class.annotatable_name_field)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    end
         
     | 
| 
      
 76 
     | 
    
         
            +
                    
         
     | 
| 
      
 77 
     | 
    
         
            +
                    # Helper method to get latest annotations
         
     | 
| 
      
 78 
     | 
    
         
            +
                    def latest_annotations(limit=nil, include_values=false)
         
     | 
| 
      
 79 
     | 
    
         
            +
                      obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self.class).to_s
         
     | 
| 
      
 80 
     | 
    
         
            +
                      
         
     | 
| 
      
 81 
     | 
    
         
            +
                      options = {
         
     | 
| 
      
 82 
     | 
    
         
            +
                        :conditions => { :annotatable_type =>  obj_type, 
         
     | 
| 
      
 83 
     | 
    
         
            +
                                         :annotatable_id => self.id },
         
     | 
| 
      
 84 
     | 
    
         
            +
                        :order => "updated_at DESC",
         
     | 
| 
      
 85 
     | 
    
         
            +
                        :limit => limit
         
     | 
| 
      
 86 
     | 
    
         
            +
                      }
         
     | 
| 
      
 87 
     | 
    
         
            +
                      
         
     | 
| 
      
 88 
     | 
    
         
            +
                      options[:include] = [ :value ] if include_values
         
     | 
| 
      
 89 
     | 
    
         
            +
                      
         
     | 
| 
      
 90 
     | 
    
         
            +
                      Annotation.find(:all, options)
         
     | 
| 
      
 91 
     | 
    
         
            +
                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
                    
         
     | 
| 
      
 93 
     | 
    
         
            +
                    # Finder to get annotations with a specific attribute.
         
     | 
| 
      
 94 
     | 
    
         
            +
                    # The input parameter is the attribute name 
         
     | 
| 
      
 95 
     | 
    
         
            +
                    # (MUST be a String representing the attribute's name).
         
     | 
| 
      
 96 
     | 
    
         
            +
                    def annotations_with_attribute(attrib, include_values=false)
         
     | 
| 
      
 97 
     | 
    
         
            +
                      return [] if attrib.blank?
         
     | 
| 
      
 98 
     | 
    
         
            +
                      
         
     | 
| 
      
 99 
     | 
    
         
            +
                      obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self.class).to_s
         
     | 
| 
      
 100 
     | 
    
         
            +
                      
         
     | 
| 
      
 101 
     | 
    
         
            +
                      options = {
         
     | 
| 
      
 102 
     | 
    
         
            +
                        :joins => :attribute,
         
     | 
| 
      
 103 
     | 
    
         
            +
                        :conditions => { :annotatable_type => obj_type,
         
     | 
| 
      
 104 
     | 
    
         
            +
                        :annotatable_id => self.id,
         
     | 
| 
      
 105 
     | 
    
         
            +
                        :annotation_attributes =>  { :name => attrib.strip.downcase } },
         
     | 
| 
      
 106 
     | 
    
         
            +
                        :order => "updated_at DESC"
         
     | 
| 
      
 107 
     | 
    
         
            +
                      }
         
     | 
| 
      
 108 
     | 
    
         
            +
                      
         
     | 
| 
      
 109 
     | 
    
         
            +
                      options[:include] = [ :value ] if include_values
         
     | 
| 
      
 110 
     | 
    
         
            +
                      
         
     | 
| 
      
 111 
     | 
    
         
            +
                      Annotation.find(:all, options)
         
     | 
| 
      
 112 
     | 
    
         
            +
                    end
         
     | 
| 
      
 113 
     | 
    
         
            +
                    
         
     | 
| 
      
 114 
     | 
    
         
            +
                    # Same as the {obj}.annotations_with_attribute method (above) but 
         
     | 
| 
      
 115 
     | 
    
         
            +
                    # takes in an array for attribute names to look for.
         
     | 
| 
      
 116 
     | 
    
         
            +
                    #
         
     | 
| 
      
 117 
     | 
    
         
            +
                    # NOTE (1): the argument to this method MUST be an Array of Strings.
         
     | 
| 
      
 118 
     | 
    
         
            +
                    def annotations_with_attributes(attribs, include_values=false)
         
     | 
| 
      
 119 
     | 
    
         
            +
                      return [] if attribs.blank?
         
     | 
| 
      
 120 
     | 
    
         
            +
                      
         
     | 
| 
      
 121 
     | 
    
         
            +
                      obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self.class).to_s
         
     | 
| 
      
 122 
     | 
    
         
            +
                      
         
     | 
| 
      
 123 
     | 
    
         
            +
                      options = {
         
     | 
| 
      
 124 
     | 
    
         
            +
                        :joins => :attribute,
         
     | 
| 
      
 125 
     | 
    
         
            +
                        :conditions => { :annotatable_type => obj_type,
         
     | 
| 
      
 126 
     | 
    
         
            +
                                         :annotatable_id => self.id,
         
     | 
| 
      
 127 
     | 
    
         
            +
                                         :annotation_attributes =>  { :name => attribs } },
         
     | 
| 
      
 128 
     | 
    
         
            +
                        :order => "updated_at DESC"
         
     | 
| 
      
 129 
     | 
    
         
            +
                      }
         
     | 
| 
      
 130 
     | 
    
         
            +
                      
         
     | 
| 
      
 131 
     | 
    
         
            +
                      options[:include] = [ :value ] if include_values
         
     | 
| 
      
 132 
     | 
    
         
            +
                      
         
     | 
| 
      
 133 
     | 
    
         
            +
                      Annotation.find(:all, options)
         
     | 
| 
      
 134 
     | 
    
         
            +
                    end
         
     | 
| 
      
 135 
     | 
    
         
            +
                    
         
     | 
| 
      
 136 
     | 
    
         
            +
                    # Finder to get annotations with a specific attribute by a specific source.
         
     | 
| 
      
 137 
     | 
    
         
            +
                    #
         
     | 
| 
      
 138 
     | 
    
         
            +
                    # The first input parameter is the attribute name (MUST be a String representing the attribute's name).
         
     | 
| 
      
 139 
     | 
    
         
            +
                    # The second input is the source object.
         
     | 
| 
      
 140 
     | 
    
         
            +
                    def annotations_with_attribute_and_by_source(attrib, source, include_values=false)
         
     | 
| 
      
 141 
     | 
    
         
            +
                      return [] if attrib.blank? or source.nil?
         
     | 
| 
      
 142 
     | 
    
         
            +
                      
         
     | 
| 
      
 143 
     | 
    
         
            +
                      obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self.class).to_s
         
     | 
| 
      
 144 
     | 
    
         
            +
                      
         
     | 
| 
      
 145 
     | 
    
         
            +
                      options = {
         
     | 
| 
      
 146 
     | 
    
         
            +
                        :joins => :attribute,
         
     | 
| 
      
 147 
     | 
    
         
            +
                        :conditions => { :annotatable_type => obj_type,
         
     | 
| 
      
 148 
     | 
    
         
            +
                                         :annotatable_id => self.id,
         
     | 
| 
      
 149 
     | 
    
         
            +
                                         :source_type => source.class.name,
         
     | 
| 
      
 150 
     | 
    
         
            +
                                         :source_id => source.id,
         
     | 
| 
      
 151 
     | 
    
         
            +
                                         :annotation_attributes =>  { :name => attrib.strip.downcase } },
         
     | 
| 
      
 152 
     | 
    
         
            +
                        :order => "updated_at DESC"
         
     | 
| 
      
 153 
     | 
    
         
            +
                      }
         
     | 
| 
      
 154 
     | 
    
         
            +
                      
         
     | 
| 
      
 155 
     | 
    
         
            +
                      options[:include] = [ :value ] if include_values
         
     | 
| 
      
 156 
     | 
    
         
            +
                      
         
     | 
| 
      
 157 
     | 
    
         
            +
                      Annotation.find(:all, options)
         
     | 
| 
      
 158 
     | 
    
         
            +
                    end
         
     | 
| 
      
 159 
     | 
    
         
            +
                    
         
     | 
| 
      
 160 
     | 
    
         
            +
                    # Finder to get all annotations on this object excluding those that
         
     | 
| 
      
 161 
     | 
    
         
            +
                    # have the attribute names specified.
         
     | 
| 
      
 162 
     | 
    
         
            +
                    #
         
     | 
| 
      
 163 
     | 
    
         
            +
                    # NOTE (1): the argument to this method MUST be an Array of Strings.
         
     | 
| 
      
 164 
     | 
    
         
            +
                    # NOTE (2): the returned records will be Read Only.
         
     | 
| 
      
 165 
     | 
    
         
            +
                    def all_annotations_excluding_attributes(attribs, include_values=false)
         
     | 
| 
      
 166 
     | 
    
         
            +
                      return [] if attribs.blank?
         
     | 
| 
      
 167 
     | 
    
         
            +
                      
         
     | 
| 
      
 168 
     | 
    
         
            +
                      obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self.class).to_s
         
     | 
| 
      
 169 
     | 
    
         
            +
                      
         
     | 
| 
      
 170 
     | 
    
         
            +
                      options = {
         
     | 
| 
      
 171 
     | 
    
         
            +
                        :joins => :attribute,
         
     | 
| 
      
 172 
     | 
    
         
            +
                        :conditions => [ "`annotations`.`annotatable_type` = ? AND `annotations`.`annotatable_id` = ? AND `annotation_attributes`.`name` NOT IN (?)",
         
     | 
| 
      
 173 
     | 
    
         
            +
                                         obj_type,
         
     | 
| 
      
 174 
     | 
    
         
            +
                                         self.id,
         
     | 
| 
      
 175 
     | 
    
         
            +
                                         attribs ],
         
     | 
| 
      
 176 
     | 
    
         
            +
                        :order => "`annotations`.`updated_at` DESC"
         
     | 
| 
      
 177 
     | 
    
         
            +
                      }
         
     | 
| 
      
 178 
     | 
    
         
            +
                      
         
     | 
| 
      
 179 
     | 
    
         
            +
                      options[:include] = [ :value ] if include_values
         
     | 
| 
      
 180 
     | 
    
         
            +
                      
         
     | 
| 
      
 181 
     | 
    
         
            +
                      Annotation.find(:all, options)
         
     | 
| 
      
 182 
     | 
    
         
            +
                    end
         
     | 
| 
      
 183 
     | 
    
         
            +
                    
         
     | 
| 
      
 184 
     | 
    
         
            +
                    # Returns the number of annotations on this annotatable object by the source type specified.
         
     | 
| 
      
 185 
     | 
    
         
            +
                    # "all" (case insensitive) can be provided to get all annotations regardless of source type.
         
     | 
| 
      
 186 
     | 
    
         
            +
                    # E.g.: book.count_annotations_by("User") or book.count_annotations_by("All")
         
     | 
| 
      
 187 
     | 
    
         
            +
                    def count_annotations_by(source_type_in)
         
     | 
| 
      
 188 
     | 
    
         
            +
                      if source_type_in == nil || source_type_in.downcase == "all"
         
     | 
| 
      
 189 
     | 
    
         
            +
                        return self.annotations.count
         
     | 
| 
      
 190 
     | 
    
         
            +
                      else
         
     | 
| 
      
 191 
     | 
    
         
            +
                        return self.annotations.count(:conditions => { :source_type => source_type_in })  
         
     | 
| 
      
 192 
     | 
    
         
            +
                      end
         
     | 
| 
      
 193 
     | 
    
         
            +
                    end
         
     | 
| 
      
 194 
     | 
    
         
            +
                    
         
     | 
| 
      
 195 
     | 
    
         
            +
                    # Use this method to create many annotations from a Hash of data.
         
     | 
| 
      
 196 
     | 
    
         
            +
                    # Arrays for Hash values will be converted to multiple annotations.
         
     | 
| 
      
 197 
     | 
    
         
            +
                    # Blank values (nil or empty string) will be ignored and thus annotations
         
     | 
| 
      
 198 
     | 
    
         
            +
                    # will not be created for them.
         
     | 
| 
      
 199 
     | 
    
         
            +
                    #
         
     | 
| 
      
 200 
     | 
    
         
            +
                    # Returns an array of Annotation objects of the annotations that were
         
     | 
| 
      
 201 
     | 
    
         
            +
                    # successfully created.
         
     | 
| 
      
 202 
     | 
    
         
            +
                    #
         
     | 
| 
      
 203 
     | 
    
         
            +
                    # Code example:
         
     | 
| 
      
 204 
     | 
    
         
            +
                    # -------------
         
     | 
| 
      
 205 
     | 
    
         
            +
                    # data = { "tag" => [ "tag1", "tag2", "tag3" ], "description" => "This is a book" }
         
     | 
| 
      
 206 
     | 
    
         
            +
                    # book.create_annotations(data, current_user)
         
     | 
| 
      
 207 
     | 
    
         
            +
                    def create_annotations(annotations_data, source)
         
     | 
| 
      
 208 
     | 
    
         
            +
                      anns = [ ]
         
     | 
| 
      
 209 
     | 
    
         
            +
                      
         
     | 
| 
      
 210 
     | 
    
         
            +
                      annotations_data.each do |attrib, val|
         
     | 
| 
      
 211 
     | 
    
         
            +
                        unless val.blank?
         
     | 
| 
      
 212 
     | 
    
         
            +
                          val = [ val ].flatten
         
     | 
| 
      
 213 
     | 
    
         
            +
                          val.each do |val_inner|
         
     | 
| 
      
 214 
     | 
    
         
            +
                            unless val_inner.blank?
         
     | 
| 
      
 215 
     | 
    
         
            +
                              ann = self.annotations.new(:attribute_name => attrib,
         
     | 
| 
      
 216 
     | 
    
         
            +
                                                         :source_type => source.class.name,
         
     | 
| 
      
 217 
     | 
    
         
            +
                                                         :source_id => source.id)
         
     | 
| 
      
 218 
     | 
    
         
            +
                              
         
     | 
| 
      
 219 
     | 
    
         
            +
                              ann.value = val_inner
         
     | 
| 
      
 220 
     | 
    
         
            +
                              ann.save
         
     | 
| 
      
 221 
     | 
    
         
            +
                              
         
     | 
| 
      
 222 
     | 
    
         
            +
                              if ann && ann.valid?
         
     | 
| 
      
 223 
     | 
    
         
            +
                                anns << ann
         
     | 
| 
      
 224 
     | 
    
         
            +
                              end
         
     | 
| 
      
 225 
     | 
    
         
            +
                            end
         
     | 
| 
      
 226 
     | 
    
         
            +
                          end
         
     | 
| 
      
 227 
     | 
    
         
            +
                        end
         
     | 
| 
      
 228 
     | 
    
         
            +
                      end
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
                      # Reload annotations collection
         
     | 
| 
      
 231 
     | 
    
         
            +
                      self.annotations(true)
         
     | 
| 
      
 232 
     | 
    
         
            +
                      
         
     | 
| 
      
 233 
     | 
    
         
            +
                      return anns
         
     | 
| 
      
 234 
     | 
    
         
            +
                    end
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                    # When used with the default style (:simple), returns a Hash of the +annotations+ values
         
     | 
| 
      
 237 
     | 
    
         
            +
                    # grouped by attribute name.
         
     | 
| 
      
 238 
     | 
    
         
            +
                    #
         
     | 
| 
      
 239 
     | 
    
         
            +
                    # Example output:
         
     | 
| 
      
 240 
     | 
    
         
            +
                    # {
         
     | 
| 
      
 241 
     | 
    
         
            +
                    #   "Summary" => "Something interesting happens",
         
     | 
| 
      
 242 
     | 
    
         
            +
                    #   "length" => 345,
         
     | 
| 
      
 243 
     | 
    
         
            +
                    #   "Title" => "Harry Potter and the Exploding Men's Locker Room",
         
     | 
| 
      
 244 
     | 
    
         
            +
                    #   "Tag" => [ "amusing rhetoric", "wizadry" ],
         
     | 
| 
      
 245 
     | 
    
         
            +
                    #   "rating" => "4/5"
         
     | 
| 
      
 246 
     | 
    
         
            +
                    # }
         
     | 
| 
      
 247 
     | 
    
         
            +
                    def annotations_hash(style=:simple)
         
     | 
| 
      
 248 
     | 
    
         
            +
                      h = { }
         
     | 
| 
      
 249 
     | 
    
         
            +
             
     | 
| 
      
 250 
     | 
    
         
            +
                      unless self.annotations.blank?
         
     | 
| 
      
 251 
     | 
    
         
            +
                        self.annotations.each do |a|
         
     | 
| 
      
 252 
     | 
    
         
            +
                          if h.has_key?(a.attribute_name)
         
     | 
| 
      
 253 
     | 
    
         
            +
                            case h[a.attribute_name]
         
     | 
| 
      
 254 
     | 
    
         
            +
                              when Array
         
     | 
| 
      
 255 
     | 
    
         
            +
                                h[a.attribute_name] << a.value_content
         
     | 
| 
      
 256 
     | 
    
         
            +
                              else
         
     | 
| 
      
 257 
     | 
    
         
            +
                                h[a.attribute_name] = [ h[a.attribute_name], a.value_content ]
         
     | 
| 
      
 258 
     | 
    
         
            +
                            end
         
     | 
| 
      
 259 
     | 
    
         
            +
                          else
         
     | 
| 
      
 260 
     | 
    
         
            +
                            h[a.attribute_name] = a.value_content
         
     | 
| 
      
 261 
     | 
    
         
            +
                          end
         
     | 
| 
      
 262 
     | 
    
         
            +
                        end
         
     | 
| 
      
 263 
     | 
    
         
            +
                      end
         
     | 
| 
      
 264 
     | 
    
         
            +
             
     | 
| 
      
 265 
     | 
    
         
            +
                      return h
         
     | 
| 
      
 266 
     | 
    
         
            +
                    end
         
     | 
| 
      
 267 
     | 
    
         
            +
                  end
         
     | 
| 
      
 268 
     | 
    
         
            +
                  
         
     | 
| 
      
 269 
     | 
    
         
            +
                end
         
     | 
| 
      
 270 
     | 
    
         
            +
              end
         
     | 
| 
      
 271 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,117 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # ActsAsAnnotationSource
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Annotations
         
     | 
| 
      
 3 
     | 
    
         
            +
              module Acts #:nodoc:
         
     | 
| 
      
 4 
     | 
    
         
            +
                module AnnotationSource #:nodoc:
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def self.included(base)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    base.send :extend, ClassMethods  
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  module ClassMethods
         
     | 
| 
      
 11 
     | 
    
         
            +
                    def acts_as_annotation_source
         
     | 
| 
      
 12 
     | 
    
         
            +
                      has_many :annotations_by,
         
     | 
| 
      
 13 
     | 
    
         
            +
                               :class_name => "Annotation",
         
     | 
| 
      
 14 
     | 
    
         
            +
                               :as => :source, 
         
     | 
| 
      
 15 
     | 
    
         
            +
                               :order => 'updated_at ASC'
         
     | 
| 
      
 16 
     | 
    
         
            +
                               
         
     | 
| 
      
 17 
     | 
    
         
            +
                      __send__ :extend, SingletonMethods
         
     | 
| 
      
 18 
     | 
    
         
            +
                      __send__ :include, InstanceMethods
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # Class methods added to the model that has been made acts_as_annotation_source (the mixin target class).
         
     | 
| 
      
 23 
     | 
    
         
            +
                  module SingletonMethods
         
     | 
| 
      
 24 
     | 
    
         
            +
                    # Helper finder to get all annotations for an object of the mixin source type with the ID provided.
         
     | 
| 
      
 25 
     | 
    
         
            +
                    # This is the same as +#annotations+ on the object, with the added benefit that the object doesnt have to be loaded.
         
     | 
| 
      
 26 
     | 
    
         
            +
                    # E.g: +User.find_annotations_by(10)+ will give all annotations by User with ID 34.
         
     | 
| 
      
 27 
     | 
    
         
            +
                    def annotations_by(id, include_values=false)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s
         
     | 
| 
      
 29 
     | 
    
         
            +
                      
         
     | 
| 
      
 30 
     | 
    
         
            +
                      options = {
         
     | 
| 
      
 31 
     | 
    
         
            +
                        :conditions => { :source_type =>  obj_type, 
         
     | 
| 
      
 32 
     | 
    
         
            +
                                         :source_id => id },
         
     | 
| 
      
 33 
     | 
    
         
            +
                        :order => "updated_at DESC"
         
     | 
| 
      
 34 
     | 
    
         
            +
                      }
         
     | 
| 
      
 35 
     | 
    
         
            +
                      
         
     | 
| 
      
 36 
     | 
    
         
            +
                      options[:include] = [ :value ] if include_values
         
     | 
| 
      
 37 
     | 
    
         
            +
                      
         
     | 
| 
      
 38 
     | 
    
         
            +
                      Annotation.find(:all, options)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
                    
         
     | 
| 
      
 41 
     | 
    
         
            +
                    # Helper finder to get all annotations for all objects of the mixin source type, for the annotatable object provided.
         
     | 
| 
      
 42 
     | 
    
         
            +
                    # E.g: +User.find_annotations_for('Book', 28)+ will give all annotations made by all Users for Book with ID 28. 
         
     | 
| 
      
 43 
     | 
    
         
            +
                    def annotations_for(annotatable_type, annotatable_id, include_values=false)
         
     | 
| 
      
 44 
     | 
    
         
            +
                      obj_type = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s
         
     | 
| 
      
 45 
     | 
    
         
            +
                      
         
     | 
| 
      
 46 
     | 
    
         
            +
                      options = {
         
     | 
| 
      
 47 
     | 
    
         
            +
                        :conditions => { :source_type => obj_type,
         
     | 
| 
      
 48 
     | 
    
         
            +
                                         :annotatable_type =>  annotatable_type, 
         
     | 
| 
      
 49 
     | 
    
         
            +
                                         :annotatable_id => annotatable_id },
         
     | 
| 
      
 50 
     | 
    
         
            +
                        :order => "updated_at DESC"
         
     | 
| 
      
 51 
     | 
    
         
            +
                      }
         
     | 
| 
      
 52 
     | 
    
         
            +
                      
         
     | 
| 
      
 53 
     | 
    
         
            +
                      options[:include] = [ :value ] if include_values
         
     | 
| 
      
 54 
     | 
    
         
            +
                      
         
     | 
| 
      
 55 
     | 
    
         
            +
                      Annotation.find(:all, options)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # This module contains instance methods
         
     | 
| 
      
 60 
     | 
    
         
            +
                  module InstanceMethods
         
     | 
| 
      
 61 
     | 
    
         
            +
                    # Helper method to get latest annotations
         
     | 
| 
      
 62 
     | 
    
         
            +
                    def latest_annotations(limit=nil, include_values=false)
         
     | 
| 
      
 63 
     | 
    
         
            +
                      options = {
         
     | 
| 
      
 64 
     | 
    
         
            +
                        :conditions => { :source_type =>  self.class.name, 
         
     | 
| 
      
 65 
     | 
    
         
            +
                                         :source_id => id },
         
     | 
| 
      
 66 
     | 
    
         
            +
                        :order => "updated_at DESC",
         
     | 
| 
      
 67 
     | 
    
         
            +
                        :limit => limit
         
     | 
| 
      
 68 
     | 
    
         
            +
                      }
         
     | 
| 
      
 69 
     | 
    
         
            +
                      
         
     | 
| 
      
 70 
     | 
    
         
            +
                      options[:include] = [ :value ] if include_values
         
     | 
| 
      
 71 
     | 
    
         
            +
                      
         
     | 
| 
      
 72 
     | 
    
         
            +
                      Annotation.find(:all, options)
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
                    
         
     | 
| 
      
 75 
     | 
    
         
            +
                    def annotation_source_name
         
     | 
| 
      
 76 
     | 
    
         
            +
                      %w{ preferred_name display_name title name }.each do |w|
         
     | 
| 
      
 77 
     | 
    
         
            +
                        return eval("self.#{w}") if self.respond_to?(w)
         
     | 
| 
      
 78 
     | 
    
         
            +
                      end
         
     | 
| 
      
 79 
     | 
    
         
            +
                      return "#{self.class.name}_#{self.id}"
         
     | 
| 
      
 80 
     | 
    
         
            +
                    end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                    # When used with the default style (:simple), returns a Hash of the +annotations_by+ values
         
     | 
| 
      
 83 
     | 
    
         
            +
                    # grouped by attribute name.
         
     | 
| 
      
 84 
     | 
    
         
            +
                    #
         
     | 
| 
      
 85 
     | 
    
         
            +
                    # Example output:
         
     | 
| 
      
 86 
     | 
    
         
            +
                    # {
         
     | 
| 
      
 87 
     | 
    
         
            +
                    #   "Summary" => "Something interesting happens",
         
     | 
| 
      
 88 
     | 
    
         
            +
                    #   "length" => 345,
         
     | 
| 
      
 89 
     | 
    
         
            +
                    #   "Title" => "Harry Potter and the Exploding Men's Locker Room",
         
     | 
| 
      
 90 
     | 
    
         
            +
                    #   "Tag" => [ "amusing rhetoric", "wizadry" ],
         
     | 
| 
      
 91 
     | 
    
         
            +
                    #   "rating" => "4/5"
         
     | 
| 
      
 92 
     | 
    
         
            +
                    # }
         
     | 
| 
      
 93 
     | 
    
         
            +
                    def annotations_by_hash(style=:simple)
         
     | 
| 
      
 94 
     | 
    
         
            +
                      h = { }
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                      unless self.annotations_by.blank?
         
     | 
| 
      
 97 
     | 
    
         
            +
                        self.annotations_by.each do |a|
         
     | 
| 
      
 98 
     | 
    
         
            +
                          if h.has_key?(a.attribute_name)
         
     | 
| 
      
 99 
     | 
    
         
            +
                            case h[a.attribute_name]
         
     | 
| 
      
 100 
     | 
    
         
            +
                              when Array
         
     | 
| 
      
 101 
     | 
    
         
            +
                                h[a.attribute_name] << a.value_content
         
     | 
| 
      
 102 
     | 
    
         
            +
                              else
         
     | 
| 
      
 103 
     | 
    
         
            +
                                h[a.attribute_name] = [ h[a.attribute_name], a.value_content ]
         
     | 
| 
      
 104 
     | 
    
         
            +
                            end
         
     | 
| 
      
 105 
     | 
    
         
            +
                          else
         
     | 
| 
      
 106 
     | 
    
         
            +
                            h[a.attribute_name] = a.value_content
         
     | 
| 
      
 107 
     | 
    
         
            +
                          end
         
     | 
| 
      
 108 
     | 
    
         
            +
                        end
         
     | 
| 
      
 109 
     | 
    
         
            +
                      end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                      return h
         
     | 
| 
      
 112 
     | 
    
         
            +
                    end
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
                  
         
     | 
| 
      
 115 
     | 
    
         
            +
                end
         
     | 
| 
      
 116 
     | 
    
         
            +
              end
         
     | 
| 
      
 117 
     | 
    
         
            +
            end
         
     |