dm-taggings 0.11.0
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 +9 -0
 - data/Gemfile +142 -0
 - data/History.txt +1 -0
 - data/LICENSE +20 -0
 - data/Manifest.txt +18 -0
 - data/README.rdoc +155 -0
 - data/Rakefile +45 -0
 - data/TODO +5 -0
 - data/VERSION +1 -0
 - data/dm-taggings.gemspec +89 -0
 - data/lib/dm-taggings.rb +19 -0
 - data/lib/dm-taggings/is/tag.rb +42 -0
 - data/lib/dm-taggings/is/taggable.rb +245 -0
 - data/lib/dm-taggings/is/tagger.rb +115 -0
 - data/lib/dm-taggings/is/tagging.rb +13 -0
 - data/lib/dm-taggings/is/version.rb +7 -0
 - data/lib/dm-taggings/spec/taggable_shared_spec.rb +259 -0
 - data/lib/dm-taggings/spec/tagger_shared_spec.rb +47 -0
 - data/spec/fixtures/models.rb +35 -0
 - data/spec/integration/post_spec.rb +14 -0
 - data/spec/integration/tag_spec.rb +24 -0
 - data/spec/integration/taggable_spec.rb +39 -0
 - data/spec/integration/user_spec.rb +18 -0
 - data/spec/spec.opts +2 -0
 - data/spec/spec_helper.rb +21 -0
 - data/tasks/hoe.rb +39 -0
 - data/tasks/yard.rake +9 -0
 - data/tasks/yardstick.rake +19 -0
 - metadata +171 -0
 
    
        data/lib/dm-taggings.rb
    ADDED
    
    | 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'pathname'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Add all external dependencies for the plugin here
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'dm-core'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'dm-constraints'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'dm-is-remixable'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            # Require plugin-files
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            dir = Pathname(__FILE__).dirname.expand_path / 'dm-taggings' / 'is'
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            require dir / 'taggable.rb'
         
     | 
| 
      
 13 
     | 
    
         
            +
            require dir / 'tag.rb'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require dir / 'tagging.rb'
         
     | 
| 
      
 15 
     | 
    
         
            +
            require dir / 'tagger.rb'
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            # Include the plugin in Resource
         
     | 
| 
      
 18 
     | 
    
         
            +
            DataMapper::Model.append_extensions DataMapper::Is::Taggable
         
     | 
| 
      
 19 
     | 
    
         
            +
            DataMapper::Model.append_extensions DataMapper::Is::Tagger
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Tag
         
     | 
| 
      
 2 
     | 
    
         
            +
              include DataMapper::Resource
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              property :id,   Serial
         
     | 
| 
      
 5 
     | 
    
         
            +
              property :name, String, :required => true, :unique => true
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              # Shortcut to build method
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
      
 9 
     | 
    
         
            +
              # @see Tag.build
         
     | 
| 
      
 10 
     | 
    
         
            +
              #
         
     | 
| 
      
 11 
     | 
    
         
            +
              # @api public
         
     | 
| 
      
 12 
     | 
    
         
            +
              def self.[](name)
         
     | 
| 
      
 13 
     | 
    
         
            +
                build(name)
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              # Find or create a tag with the give name
         
     | 
| 
      
 17 
     | 
    
         
            +
              #
         
     | 
| 
      
 18 
     | 
    
         
            +
              # @param [String] name
         
     | 
| 
      
 19 
     | 
    
         
            +
              #   A name of a tag
         
     | 
| 
      
 20 
     | 
    
         
            +
              #
         
     | 
| 
      
 21 
     | 
    
         
            +
              # @return [DataMapper::Resource]
         
     | 
| 
      
 22 
     | 
    
         
            +
              #   A tag resource instance
         
     | 
| 
      
 23 
     | 
    
         
            +
              #
         
     | 
| 
      
 24 
     | 
    
         
            +
              # @api public
         
     | 
| 
      
 25 
     | 
    
         
            +
              def self.build(name)
         
     | 
| 
      
 26 
     | 
    
         
            +
                Tag.first_or_create(:name => name.strip) if name
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              # An overridden name attribute setter that strips the value
         
     | 
| 
      
 30 
     | 
    
         
            +
              #
         
     | 
| 
      
 31 
     | 
    
         
            +
              # @param [String] value
         
     | 
| 
      
 32 
     | 
    
         
            +
              #   A value to be set as the tag name
         
     | 
| 
      
 33 
     | 
    
         
            +
              #
         
     | 
| 
      
 34 
     | 
    
         
            +
              # @return [String]
         
     | 
| 
      
 35 
     | 
    
         
            +
              #  The name
         
     | 
| 
      
 36 
     | 
    
         
            +
              #
         
     | 
| 
      
 37 
     | 
    
         
            +
              # @api public
         
     | 
| 
      
 38 
     | 
    
         
            +
              def name=(value)
         
     | 
| 
      
 39 
     | 
    
         
            +
                super(value.strip) if value
         
     | 
| 
      
 40 
     | 
    
         
            +
                name
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,245 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module DataMapper
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Is
         
     | 
| 
      
 3 
     | 
    
         
            +
                module Taggable
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                  # Make a resource taggable
         
     | 
| 
      
 6 
     | 
    
         
            +
                  #
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 8 
     | 
    
         
            +
                  #
         
     | 
| 
      
 9 
     | 
    
         
            +
                  #   class Post
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #     include DataMapper::Resource
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #     property :id,      Serial
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #     property :title,   String
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #     property :content, Text
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #     is :taggable
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # @param [Hash] options(optional)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #   A hash with options
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # @option options [Array] :by
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #   A list of DataMapper models that should become taggers
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def is_taggable(options={})
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    # Add class-methods
         
     | 
| 
      
 28 
     | 
    
         
            +
                    extend  DataMapper::Is::Taggable::ClassMethods
         
     | 
| 
      
 29 
     | 
    
         
            +
                    # Add instance-methods
         
     | 
| 
      
 30 
     | 
    
         
            +
                    include DataMapper::Is::Taggable::InstanceMethods
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    class << self
         
     | 
| 
      
 33 
     | 
    
         
            +
                      attr_reader :tagging_parent_name, :tagging_relationship_name, :tagging_relationship,
         
     | 
| 
      
 34 
     | 
    
         
            +
                        :tagging_class, :taggable_relationship_name
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    # Make the magic happen
         
     | 
| 
      
 38 
     | 
    
         
            +
                    options[:by] ||= []
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    remix n, :taggings
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    @tagging_parent_name       = DataMapper::Inflector.underscore(name).to_sym
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @tagging_relationship_name = "#{@tagging_parent_name}_tags".to_sym
         
     | 
| 
      
 44 
     | 
    
         
            +
                    @tagging_relationship      = relationships[@tagging_relationship_name]
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @tagging_class             = @tagging_relationship.child_model
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    @taggable_relationship_name = DataMapper::Inflector.underscore(name).pluralize.to_sym
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    @tagging_relationship.add_constraint_option(
         
     | 
| 
      
 50 
     | 
    
         
            +
                      @taggable_relationship_name, @tagging_class, self, :constraint => :destroy!)
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    tagging_parent_name = @tagging_parent_name
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    enhance :taggings do
         
     | 
| 
      
 55 
     | 
    
         
            +
                      belongs_to :tag
         
     | 
| 
      
 56 
     | 
    
         
            +
                      belongs_to tagging_parent_name
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                      options[:by].each do |tagger_class|
         
     | 
| 
      
 59 
     | 
    
         
            +
                        belongs_to DataMapper::Inflector.underscore(tagger_class.name), :required => false
         
     | 
| 
      
 60 
     | 
    
         
            +
                      end
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    has n, :tags, :through => @tagging_relationship_name, :constraint => :destroy!
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    Tag.has n, @tagging_relationship_name,  :constraint => :destroy!
         
     | 
| 
      
 66 
     | 
    
         
            +
                    Tag.has n, @taggable_relationship_name, :through => @tagging_relationship_name
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    options[:by].each do |tagger_class|
         
     | 
| 
      
 69 
     | 
    
         
            +
                      tagger_class.is :tagger, :for => [self]
         
     | 
| 
      
 70 
     | 
    
         
            +
                    end
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  module ClassMethods
         
     | 
| 
      
 74 
     | 
    
         
            +
                    # @attr_reader [String] tagging_parent_name
         
     | 
| 
      
 75 
     | 
    
         
            +
                    # @attr_reader [String] tagging_relationship_name
         
     | 
| 
      
 76 
     | 
    
         
            +
                    # @attr_reader [DataMapper::Associations::OneToMany::Relationship] tagging_relationship
         
     | 
| 
      
 77 
     | 
    
         
            +
                    # @attr_reader [DataMapper::Resource] tagging_class
         
     | 
| 
      
 78 
     | 
    
         
            +
                    # @attr_reader [String] taggable_relationship_name
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 81 
     | 
    
         
            +
                    def taggable?
         
     | 
| 
      
 82 
     | 
    
         
            +
                      true
         
     | 
| 
      
 83 
     | 
    
         
            +
                    end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    # Return all the taggable resources that are tagged with the given list of tags.
         
     | 
| 
      
 86 
     | 
    
         
            +
                    #
         
     | 
| 
      
 87 
     | 
    
         
            +
                    # Can be chained, for instance:
         
     | 
| 
      
 88 
     | 
    
         
            +
                    #
         
     | 
| 
      
 89 
     | 
    
         
            +
                    #     Post.tagged_with(["foo", "bar"]).all(:created_at.lt => 1.day.ago)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    #
         
     | 
| 
      
 91 
     | 
    
         
            +
                    # @param [Array] tags_or_names
         
     | 
| 
      
 92 
     | 
    
         
            +
                    #   A list of either tag resources or tag names
         
     | 
| 
      
 93 
     | 
    
         
            +
                    #
         
     | 
| 
      
 94 
     | 
    
         
            +
                    # @return [DataMapper::Collection]
         
     | 
| 
      
 95 
     | 
    
         
            +
                    #   A collection of taggables
         
     | 
| 
      
 96 
     | 
    
         
            +
                    #
         
     | 
| 
      
 97 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 98 
     | 
    
         
            +
                    def tagged_with(tags_or_names)
         
     | 
| 
      
 99 
     | 
    
         
            +
                      tags_or_names = [tags_or_names] unless tags_or_names.kind_of?(Array)
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                      tag_ids = if tags_or_names.all? { |tag| tag.kind_of?(Tag) }
         
     | 
| 
      
 102 
     | 
    
         
            +
                               tags_or_names
         
     | 
| 
      
 103 
     | 
    
         
            +
                             else
         
     | 
| 
      
 104 
     | 
    
         
            +
                               Tag.all(:name => tags_or_names)
         
     | 
| 
      
 105 
     | 
    
         
            +
                             end.map { |tag| tag.id }
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                      all("#{tagging_relationship_name}.tag_id" => tag_ids)
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end # ClassMethods
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                  module InstanceMethods
         
     | 
| 
      
 112 
     | 
    
         
            +
                    # Add tags to a resource but do not persist them.
         
     | 
| 
      
 113 
     | 
    
         
            +
                    #
         
     | 
| 
      
 114 
     | 
    
         
            +
                    # @param [Array] tags_or_names
         
     | 
| 
      
 115 
     | 
    
         
            +
                    #   A list of either tag resources or tag names
         
     | 
| 
      
 116 
     | 
    
         
            +
                    #
         
     | 
| 
      
 117 
     | 
    
         
            +
                    # @return [DataMapper::Associations::OneToMany::Collection]
         
     | 
| 
      
 118 
     | 
    
         
            +
                    #   A DataMapper collection of resource's tags
         
     | 
| 
      
 119 
     | 
    
         
            +
                    #
         
     | 
| 
      
 120 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 121 
     | 
    
         
            +
                    def tag(tags_or_names)
         
     | 
| 
      
 122 
     | 
    
         
            +
                      tags = extract_tags_from_names(tags_or_names)
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                      tags.each do |tag|
         
     | 
| 
      
 125 
     | 
    
         
            +
                        next if self.tags.include?(tag)
         
     | 
| 
      
 126 
     | 
    
         
            +
                        taggings.new(:tag => tag)
         
     | 
| 
      
 127 
     | 
    
         
            +
                      end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                      taggings
         
     | 
| 
      
 130 
     | 
    
         
            +
                    end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                    # Add tags to a resource and persists them.
         
     | 
| 
      
 133 
     | 
    
         
            +
                    #
         
     | 
| 
      
 134 
     | 
    
         
            +
                    # @param [Array] tags_or_names
         
     | 
| 
      
 135 
     | 
    
         
            +
                    #   A list of either tag resources or tag names
         
     | 
| 
      
 136 
     | 
    
         
            +
                    #
         
     | 
| 
      
 137 
     | 
    
         
            +
                    # @return [DataMapper::Associations::OneToMany::Collection]
         
     | 
| 
      
 138 
     | 
    
         
            +
                    #   A DataMapper collection of resource's tags
         
     | 
| 
      
 139 
     | 
    
         
            +
                    #
         
     | 
| 
      
 140 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 141 
     | 
    
         
            +
                    def tag!(tags_or_names)
         
     | 
| 
      
 142 
     | 
    
         
            +
                      taggings = tag(tags_or_names)
         
     | 
| 
      
 143 
     | 
    
         
            +
                      taggings.save! unless new?
         
     | 
| 
      
 144 
     | 
    
         
            +
                      taggings
         
     | 
| 
      
 145 
     | 
    
         
            +
                    end
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                    # Delete given tags from a resource collection without actually deleting
         
     | 
| 
      
 148 
     | 
    
         
            +
                    # them from the datastore. Everything will be deleted if no tags are given.
         
     | 
| 
      
 149 
     | 
    
         
            +
                    #
         
     | 
| 
      
 150 
     | 
    
         
            +
                    # @param [Array] tags_or_names (optional)
         
     | 
| 
      
 151 
     | 
    
         
            +
                    #   A list of either tag resources or tag names
         
     | 
| 
      
 152 
     | 
    
         
            +
                    #
         
     | 
| 
      
 153 
     | 
    
         
            +
                    # @return [DataMapper::Associations::OneToMany::Collection]
         
     | 
| 
      
 154 
     | 
    
         
            +
                    #   A DataMapper collection of resource's tags
         
     | 
| 
      
 155 
     | 
    
         
            +
                    #
         
     | 
| 
      
 156 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 157 
     | 
    
         
            +
                    def untag(tags_or_names=nil)
         
     | 
| 
      
 158 
     | 
    
         
            +
                      tags = extract_tags_from_names(tags_or_names) if tags_or_names
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                      taggings_to_destroy = if tags.blank?
         
     | 
| 
      
 161 
     | 
    
         
            +
                                         taggings.all
         
     | 
| 
      
 162 
     | 
    
         
            +
                                       else
         
     | 
| 
      
 163 
     | 
    
         
            +
                                         taggings.all(:tag => tags)
         
     | 
| 
      
 164 
     | 
    
         
            +
                                       end
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                      self.taggings = taggings - taggings_to_destroy
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                      taggings_to_destroy
         
     | 
| 
      
 169 
     | 
    
         
            +
                    end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                    # Same as untag but actually delete the tags from the datastore.
         
     | 
| 
      
 172 
     | 
    
         
            +
                    #
         
     | 
| 
      
 173 
     | 
    
         
            +
                    # @param [Array] tags_or_names (optional)
         
     | 
| 
      
 174 
     | 
    
         
            +
                    #   A list of either tag resources or tag names
         
     | 
| 
      
 175 
     | 
    
         
            +
                    #
         
     | 
| 
      
 176 
     | 
    
         
            +
                    # @return [DataMapper::Associations::OneToMany::Collection]
         
     | 
| 
      
 177 
     | 
    
         
            +
                    #   A DataMapper collection of resource's tags
         
     | 
| 
      
 178 
     | 
    
         
            +
                    #
         
     | 
| 
      
 179 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 180 
     | 
    
         
            +
                    def untag!(tags_or_names=nil)
         
     | 
| 
      
 181 
     | 
    
         
            +
                      taggings_to_destroy = untag(tags_or_names)
         
     | 
| 
      
 182 
     | 
    
         
            +
                      taggings_to_destroy.destroy! unless new?
         
     | 
| 
      
 183 
     | 
    
         
            +
                      taggings_to_destroy
         
     | 
| 
      
 184 
     | 
    
         
            +
                    end
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                    # Return a string representation of tags collection
         
     | 
| 
      
 187 
     | 
    
         
            +
                    #
         
     | 
| 
      
 188 
     | 
    
         
            +
                    # @return [String]
         
     | 
| 
      
 189 
     | 
    
         
            +
                    #   A tag list separated by commas
         
     | 
| 
      
 190 
     | 
    
         
            +
                    #
         
     | 
| 
      
 191 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 192 
     | 
    
         
            +
                    def tag_list
         
     | 
| 
      
 193 
     | 
    
         
            +
                      @tag_list ||= tags.collect { |tag| tag.name }.join(", ")
         
     | 
| 
      
 194 
     | 
    
         
            +
                    end
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                    # Tag a resource using tag names from the give list separated by commas.
         
     | 
| 
      
 197 
     | 
    
         
            +
                    #
         
     | 
| 
      
 198 
     | 
    
         
            +
                    # @param [String]
         
     | 
| 
      
 199 
     | 
    
         
            +
                    #   A tag list separated by commas
         
     | 
| 
      
 200 
     | 
    
         
            +
                    #
         
     | 
| 
      
 201 
     | 
    
         
            +
                    # @return [DataMapper::Associations::OneToMany::Collection]
         
     | 
| 
      
 202 
     | 
    
         
            +
                    #   A DataMapper collection of resource's tags
         
     | 
| 
      
 203 
     | 
    
         
            +
                    def tag_list=(list)
         
     | 
| 
      
 204 
     | 
    
         
            +
                      @tag_list = list
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                      tag_names = list.split(",").map { |name| name.blank? ? nil : name.strip }.compact
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
                      old_tag_names = taggings.map { |tagging| tagging.tag.name } - tag_names
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                      untag!(old_tag_names)
         
     | 
| 
      
 211 
     | 
    
         
            +
                      tag(tag_names)
         
     | 
| 
      
 212 
     | 
    
         
            +
                    end
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
      
 214 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 215 
     | 
    
         
            +
                    def reload
         
     | 
| 
      
 216 
     | 
    
         
            +
                      @tag_list = nil
         
     | 
| 
      
 217 
     | 
    
         
            +
                      super
         
     | 
| 
      
 218 
     | 
    
         
            +
                    end
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 221 
     | 
    
         
            +
                    def taggings
         
     | 
| 
      
 222 
     | 
    
         
            +
                      send(self.class.tagging_relationship_name)
         
     | 
| 
      
 223 
     | 
    
         
            +
                    end
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 226 
     | 
    
         
            +
                    def taggings=(taggings)
         
     | 
| 
      
 227 
     | 
    
         
            +
                      send("#{self.class.tagging_relationship_name}=", taggings)
         
     | 
| 
      
 228 
     | 
    
         
            +
                    end
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
                    protected
         
     | 
| 
      
 231 
     | 
    
         
            +
             
     | 
| 
      
 232 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 233 
     | 
    
         
            +
                    def extract_tags_from_names(tags_or_names)
         
     | 
| 
      
 234 
     | 
    
         
            +
                      tags_or_names = [tags_or_names] unless tags_or_names.kind_of?(Array)
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                      tags_or_names.map do |tag_or_name|
         
     | 
| 
      
 237 
     | 
    
         
            +
                        tag_or_name.kind_of?(Tag) ? tag_or_name : Tag[tag_or_name]
         
     | 
| 
      
 238 
     | 
    
         
            +
                      end
         
     | 
| 
      
 239 
     | 
    
         
            +
                    end
         
     | 
| 
      
 240 
     | 
    
         
            +
                  end # InstanceMethods
         
     | 
| 
      
 241 
     | 
    
         
            +
             
     | 
| 
      
 242 
     | 
    
         
            +
                end # Taggable
         
     | 
| 
      
 243 
     | 
    
         
            +
              end # Is
         
     | 
| 
      
 244 
     | 
    
         
            +
            end # DataMapper
         
     | 
| 
      
 245 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,115 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module DataMapper
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Is
         
     | 
| 
      
 3 
     | 
    
         
            +
                module Tagger
         
     | 
| 
      
 4 
     | 
    
         
            +
                  # Set up a resource as tagger
         
     | 
| 
      
 5 
     | 
    
         
            +
                  #
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 7 
     | 
    
         
            +
                  #
         
     | 
| 
      
 8 
     | 
    
         
            +
                  #   class Song
         
     | 
| 
      
 9 
     | 
    
         
            +
                  #     include DataMapper::Resource
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #     property :id,    Serial
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #     property :title, String
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #     is :taggable, :by => [ User ]
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #   class User
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #     include DataMapper::Resource
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #     property :id,   Serial
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #     property :name, String
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #     is :tagger, :for => [ Song ]
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # @param [Hash] options
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #   A hash of options
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # @option options [Array] :for
         
     | 
| 
      
 29 
     | 
    
         
            +
                  #   A list of DataMapper taggable models
         
     | 
| 
      
 30 
     | 
    
         
            +
                  #
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # @return [Array]
         
     | 
| 
      
 32 
     | 
    
         
            +
                  #   A list of DataMapper taggable models
         
     | 
| 
      
 33 
     | 
    
         
            +
                  #
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 35 
     | 
    
         
            +
                  def is_tagger(options={})
         
     | 
| 
      
 36 
     | 
    
         
            +
                    unless self.respond_to?(:tagger?)
         
     | 
| 
      
 37 
     | 
    
         
            +
                      # Add class-methods
         
     | 
| 
      
 38 
     | 
    
         
            +
                      extend  DataMapper::Is::Tagger::ClassMethods
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                      # Add instance-methods
         
     | 
| 
      
 41 
     | 
    
         
            +
                      include DataMapper::Is::Tagger::InstanceMethods
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                      cattr_accessor(:taggable_object_classes)
         
     | 
| 
      
 44 
     | 
    
         
            +
                      self.taggable_object_classes = []
         
     | 
| 
      
 45 
     | 
    
         
            +
                    end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    raise "options[:for] is missing" unless options[:for]
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    add_taggable_object_classes(options[:for])
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  module ClassMethods
         
     | 
| 
      
 53 
     | 
    
         
            +
                    # Return if a model is tagger
         
     | 
| 
      
 54 
     | 
    
         
            +
                    #
         
     | 
| 
      
 55 
     | 
    
         
            +
                    # @return [TrueClass]
         
     | 
| 
      
 56 
     | 
    
         
            +
                    #   true
         
     | 
| 
      
 57 
     | 
    
         
            +
                    #
         
     | 
| 
      
 58 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 59 
     | 
    
         
            +
                    def tagger?
         
     | 
| 
      
 60 
     | 
    
         
            +
                      true
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    # Register new taggables and set up relationships
         
     | 
| 
      
 64 
     | 
    
         
            +
                    #
         
     | 
| 
      
 65 
     | 
    
         
            +
                    # @param [Array] taggable_object_classes
         
     | 
| 
      
 66 
     | 
    
         
            +
                    #   An array of taggable DataMapper models
         
     | 
| 
      
 67 
     | 
    
         
            +
                    #
         
     | 
| 
      
 68 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 69 
     | 
    
         
            +
                    def add_taggable_object_classes(taggable_object_classes)
         
     | 
| 
      
 70 
     | 
    
         
            +
                      taggable_object_classes.each do |taggable_object_class|
         
     | 
| 
      
 71 
     | 
    
         
            +
                        self.taggable_object_classes << taggable_object_class
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                        has n, taggable_object_class.tagging_relationship_name,
         
     | 
| 
      
 74 
     | 
    
         
            +
                          :constraint => :destroy
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                        has n, taggable_object_class.taggable_relationship_name,
         
     | 
| 
      
 77 
     | 
    
         
            +
                          :through => taggable_object_class.tagging_relationship_name,
         
     | 
| 
      
 78 
     | 
    
         
            +
                          :constraint => :destroy
         
     | 
| 
      
 79 
     | 
    
         
            +
                      end
         
     | 
| 
      
 80 
     | 
    
         
            +
                    end
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end # ClassMethods
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  module InstanceMethods
         
     | 
| 
      
 84 
     | 
    
         
            +
                    # Tag a resource
         
     | 
| 
      
 85 
     | 
    
         
            +
                    #
         
     | 
| 
      
 86 
     | 
    
         
            +
                    # @param [DataMapper::Resource]
         
     | 
| 
      
 87 
     | 
    
         
            +
                    #   An instance of a taggable resource
         
     | 
| 
      
 88 
     | 
    
         
            +
                    #
         
     | 
| 
      
 89 
     | 
    
         
            +
                    # @param [Hash] options (optional)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    #   A hash with options
         
     | 
| 
      
 91 
     | 
    
         
            +
                    #
         
     | 
| 
      
 92 
     | 
    
         
            +
                    # @return [DataMapper::Collection]
         
     | 
| 
      
 93 
     | 
    
         
            +
                    #  A collection of tags that were associated with the resource
         
     | 
| 
      
 94 
     | 
    
         
            +
                    #
         
     | 
| 
      
 95 
     | 
    
         
            +
                    # @api public
         
     | 
| 
      
 96 
     | 
    
         
            +
                    def tag!(taggable, options={})
         
     | 
| 
      
 97 
     | 
    
         
            +
                      unless self.taggable_object_classes.include?(taggable.class)
         
     | 
| 
      
 98 
     | 
    
         
            +
                        raise "Object of type #{taggable.class} isn't taggable!"
         
     | 
| 
      
 99 
     | 
    
         
            +
                      end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                      tags = options[:with]
         
     | 
| 
      
 102 
     | 
    
         
            +
                      tags = [tags] unless tags.kind_of?(Array)
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                      tags.each do |tag|
         
     | 
| 
      
 105 
     | 
    
         
            +
                        taggable.taggings.create(:tag => tag, :tagger => self)
         
     | 
| 
      
 106 
     | 
    
         
            +
                      end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                      tags
         
     | 
| 
      
 109 
     | 
    
         
            +
                    end
         
     | 
| 
      
 110 
     | 
    
         
            +
                  end # InstanceMethods
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                end # Tagger
         
     | 
| 
      
 113 
     | 
    
         
            +
              end # Is
         
     | 
| 
      
 114 
     | 
    
         
            +
            end # DataMapper
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Tagging
         
     | 
| 
      
 2 
     | 
    
         
            +
              include DataMapper::Resource
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              property :id, Serial
         
     | 
| 
      
 5 
     | 
    
         
            +
              property :tag_id, Integer, :min => 1, :required => true
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              is :remixable, :suffix => "tag"
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def tagger=(tagger)
         
     | 
| 
      
 10 
     | 
    
         
            +
                send("#{DataMapper::Inflector.underscore(tagger.class.name).to_sym}=", tagger)
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,259 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            share_examples_for 'A taggable resource' do
         
     | 
| 
      
 2 
     | 
    
         
            +
              def create_taggable(attrs={})
         
     | 
| 
      
 3 
     | 
    
         
            +
                @taggable.create(@taggable_attributes.merge(attrs))
         
     | 
| 
      
 4 
     | 
    
         
            +
              end
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              before :all do
         
     | 
| 
      
 7 
     | 
    
         
            +
                %w[ @taggable ].each do |ivar|
         
     | 
| 
      
 8 
     | 
    
         
            +
                  raise "+#{ivar}+ should be defined in before block" unless instance_variable_defined?(ivar)
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                @taggable_attributes ||= {}
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                @foo_tag  = Tag["foo"]
         
     | 
| 
      
 14 
     | 
    
         
            +
                @bar_tag  = Tag["bar"]
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                @tags = [@foo_tag, @bar_tag]
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              describe "public class methods" do
         
     | 
| 
      
 20 
     | 
    
         
            +
                subject { @taggable }
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                it { should respond_to(:is_taggable) }
         
     | 
| 
      
 23 
     | 
    
         
            +
                it { should respond_to(:taggable?) }
         
     | 
| 
      
 24 
     | 
    
         
            +
                it { should respond_to(:tagged_with) }
         
     | 
| 
      
 25 
     | 
    
         
            +
                it { should respond_to(:tagging_relationship_name) }
         
     | 
| 
      
 26 
     | 
    
         
            +
                it { should respond_to(:tagging_relationship) }
         
     | 
| 
      
 27 
     | 
    
         
            +
                it { should respond_to(:tagging_class) }
         
     | 
| 
      
 28 
     | 
    
         
            +
                it { should respond_to(:tagging_parent_name) }
         
     | 
| 
      
 29 
     | 
    
         
            +
                it { should respond_to(:taggable_relationship_name) }
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                describe ".taggable?" do
         
     | 
| 
      
 32 
     | 
    
         
            +
                  it "should return true" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                    @taggable.taggable?.should be(true)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                describe "relationships" do
         
     | 
| 
      
 38 
     | 
    
         
            +
                  subject { @taggable.relationships }
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  it { should have_key(@taggable.tagging_relationship_name) }
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  describe "tagging constraint" do
         
     | 
| 
      
 43 
     | 
    
         
            +
                    subject { @taggable.tagging_relationship.constraint }
         
     | 
| 
      
 44 
     | 
    
         
            +
                    it { subject.should eql(:destroy!) }
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                describe ".tagged_with" do
         
     | 
| 
      
 49 
     | 
    
         
            +
                  before :all do
         
     | 
| 
      
 50 
     | 
    
         
            +
                    @resource_one = create_taggable(:tag_list => "red, green, blue")
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @resource_two = create_taggable(:tag_list => "orange, yellow")
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  it "should return correct resources" do
         
     | 
| 
      
 55 
     | 
    
         
            +
                    result = @taggable.tagged_with(["red", "yellow", "purple"])
         
     | 
| 
      
 56 
     | 
    
         
            +
                    result.size.should eql(2)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    result.should include(@resource_one, @resource_two)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              describe "public instance methods" do
         
     | 
| 
      
 63 
     | 
    
         
            +
                subject { @taggable.new }
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                it { should respond_to(:tag) }
         
     | 
| 
      
 66 
     | 
    
         
            +
                it { should respond_to(:tag!) }
         
     | 
| 
      
 67 
     | 
    
         
            +
                it { should respond_to(:untag) }
         
     | 
| 
      
 68 
     | 
    
         
            +
                it { should respond_to(:untag!) }
         
     | 
| 
      
 69 
     | 
    
         
            +
                it { should respond_to(:tag_list) }
         
     | 
| 
      
 70 
     | 
    
         
            +
                it { should respond_to(:taggings) }
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                describe ".tag" do
         
     | 
| 
      
 73 
     | 
    
         
            +
                  before :all do
         
     | 
| 
      
 74 
     | 
    
         
            +
                    @resource = create_taggable
         
     | 
| 
      
 75 
     | 
    
         
            +
                    @taggings = @resource.tag([@foo_tag, @bar_tag])
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  it "should set new taggings" do
         
     | 
| 
      
 79 
     | 
    
         
            +
                    @taggings.should eql(@resource.taggings)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  it "should not create new taggings" do
         
     | 
| 
      
 83 
     | 
    
         
            +
                    @resource.tags.should be_empty
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                describe ".tag!" do
         
     | 
| 
      
 88 
     | 
    
         
            +
                  before :all do
         
     | 
| 
      
 89 
     | 
    
         
            +
                    @resource = create_taggable
         
     | 
| 
      
 90 
     | 
    
         
            +
                    @taggings = @resource.tag!([@foo_tag, @bar_tag])
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  it "should create new taggings" do
         
     | 
| 
      
 94 
     | 
    
         
            +
                    @resource.reload.tags.should include(@foo_tag, @bar_tag)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                describe ".untag" do
         
     | 
| 
      
 99 
     | 
    
         
            +
                  describe "all" do
         
     | 
| 
      
 100 
     | 
    
         
            +
                    before :all do
         
     | 
| 
      
 101 
     | 
    
         
            +
                      @resource = create_taggable
         
     | 
| 
      
 102 
     | 
    
         
            +
                      @taggings = @resource.tag!([@foo_tag, @bar_tag])
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                      @resource.untag
         
     | 
| 
      
 105 
     | 
    
         
            +
                    end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                    it "should remove the taggings from the collection" do
         
     | 
| 
      
 108 
     | 
    
         
            +
                      @resource.taggings.should be_empty
         
     | 
| 
      
 109 
     | 
    
         
            +
                    end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                    it "should not destroy the taggings" do
         
     | 
| 
      
 112 
     | 
    
         
            +
                      @resource.reload.tags.should_not be_empty
         
     | 
| 
      
 113 
     | 
    
         
            +
                    end
         
     | 
| 
      
 114 
     | 
    
         
            +
                  end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  describe "specific names" do
         
     | 
| 
      
 117 
     | 
    
         
            +
                    before :all do
         
     | 
| 
      
 118 
     | 
    
         
            +
                      @resource = create_taggable
         
     | 
| 
      
 119 
     | 
    
         
            +
                      @taggings = @resource.tag!([@foo_tag, @bar_tag])
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                      @resource.untag([@foo_tag])
         
     | 
| 
      
 122 
     | 
    
         
            +
                    end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                    it "should remove the related tagging from the collection" do
         
     | 
| 
      
 125 
     | 
    
         
            +
                      @resource.taggings.size.should eql(1)
         
     | 
| 
      
 126 
     | 
    
         
            +
                    end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                    it "should remove the related tag" do
         
     | 
| 
      
 129 
     | 
    
         
            +
                      @resource.tags.should_not include(@foo_tag)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    end
         
     | 
| 
      
 131 
     | 
    
         
            +
                  end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                  describe "when save is called" do
         
     | 
| 
      
 134 
     | 
    
         
            +
                    before :all do
         
     | 
| 
      
 135 
     | 
    
         
            +
                      @resource = create_taggable
         
     | 
| 
      
 136 
     | 
    
         
            +
                      @taggings = @resource.tag!([@foo_tag, @bar_tag])
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                      @resource.untag
         
     | 
| 
      
 139 
     | 
    
         
            +
                    end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                    it "should return true" do
         
     | 
| 
      
 142 
     | 
    
         
            +
                      pending "Currently DataMapper doesn't support saving an empty collection" do
         
     | 
| 
      
 143 
     | 
    
         
            +
                        @resource.save.should be(true)
         
     | 
| 
      
 144 
     | 
    
         
            +
                      end
         
     | 
| 
      
 145 
     | 
    
         
            +
                    end
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                    it "should destroy taggings" do
         
     | 
| 
      
 148 
     | 
    
         
            +
                      pending "Currently DataMapper doesn't support saving an empty collection" do
         
     | 
| 
      
 149 
     | 
    
         
            +
                        @resource.reload.taggings.should be_empty
         
     | 
| 
      
 150 
     | 
    
         
            +
                      end
         
     | 
| 
      
 151 
     | 
    
         
            +
                    end
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                    it "should destroy tags" do
         
     | 
| 
      
 154 
     | 
    
         
            +
                      pending "Currently DataMapper doesn't support saving an empty collection" do
         
     | 
| 
      
 155 
     | 
    
         
            +
                        @resource.reload.tags.should be_empty
         
     | 
| 
      
 156 
     | 
    
         
            +
                      end
         
     | 
| 
      
 157 
     | 
    
         
            +
                    end
         
     | 
| 
      
 158 
     | 
    
         
            +
                  end
         
     | 
| 
      
 159 
     | 
    
         
            +
                end
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                describe ".untag!" do
         
     | 
| 
      
 162 
     | 
    
         
            +
                  describe "all" do
         
     | 
| 
      
 163 
     | 
    
         
            +
                    before :all do
         
     | 
| 
      
 164 
     | 
    
         
            +
                      @resource = create_taggable
         
     | 
| 
      
 165 
     | 
    
         
            +
                      @taggings = @resource.tag!([@foo_tag, @bar_tag])
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                      @resource.untag!
         
     | 
| 
      
 168 
     | 
    
         
            +
                    end
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
                    it "should destroy the taggings" do
         
     | 
| 
      
 171 
     | 
    
         
            +
                      @resource.reload.taggings.should be_empty
         
     | 
| 
      
 172 
     | 
    
         
            +
                    end
         
     | 
| 
      
 173 
     | 
    
         
            +
                  end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                  describe "specific names" do
         
     | 
| 
      
 176 
     | 
    
         
            +
                    before :all do
         
     | 
| 
      
 177 
     | 
    
         
            +
                      @resource = create_taggable
         
     | 
| 
      
 178 
     | 
    
         
            +
                      @taggings = @resource.tag!([@foo_tag, @bar_tag])
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
                      @resource.untag!([@foo_tag])
         
     | 
| 
      
 181 
     | 
    
         
            +
                      @resource.reload
         
     | 
| 
      
 182 
     | 
    
         
            +
                    end
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                    subject { @resource.tags }
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                    it { should_not include(@foo_tag) }
         
     | 
| 
      
 187 
     | 
    
         
            +
                    it { should include(@bar_tag) }
         
     | 
| 
      
 188 
     | 
    
         
            +
                  end
         
     | 
| 
      
 189 
     | 
    
         
            +
                end
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                describe ".tag_list=" do
         
     | 
| 
      
 192 
     | 
    
         
            +
                  describe "with a list of tag names" do
         
     | 
| 
      
 193 
     | 
    
         
            +
                    describe "with blank values" do
         
     | 
| 
      
 194 
     | 
    
         
            +
                      before :all do
         
     | 
| 
      
 195 
     | 
    
         
            +
                        @resource = create_taggable(:tag_list => "foo, , ,bar, , ")
         
     | 
| 
      
 196 
     | 
    
         
            +
                      end
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                      it "should add new tags and reject blank names" do
         
     | 
| 
      
 199 
     | 
    
         
            +
                        @resource.reload.tags.should include(Tag["foo"], Tag["bar"])
         
     | 
| 
      
 200 
     | 
    
         
            +
                      end
         
     | 
| 
      
 201 
     | 
    
         
            +
                    end
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
                    describe "when tags are removed and added" do
         
     | 
| 
      
 204 
     | 
    
         
            +
                      before :all do
         
     | 
| 
      
 205 
     | 
    
         
            +
                        @resource = create_taggable(:tag_list => "foo, bar")
         
     | 
| 
      
 206 
     | 
    
         
            +
                        @resource.update(:tag_list => "foo, bar, pub")
         
     | 
| 
      
 207 
     | 
    
         
            +
                      end
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
                      it "should add new tags" do
         
     | 
| 
      
 210 
     | 
    
         
            +
                        @resource.reload.tags.should include(Tag["bar"], Tag["bar"], Tag["pub"])
         
     | 
| 
      
 211 
     | 
    
         
            +
                      end
         
     | 
| 
      
 212 
     | 
    
         
            +
                    end
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
      
 214 
     | 
    
         
            +
                    describe "when tags are added" do
         
     | 
| 
      
 215 
     | 
    
         
            +
                      before :all do
         
     | 
| 
      
 216 
     | 
    
         
            +
                        @resource = create_taggable(:tag_list => "foo, bar")
         
     | 
| 
      
 217 
     | 
    
         
            +
                        @resource.update(:tag_list => "bar, pub")
         
     | 
| 
      
 218 
     | 
    
         
            +
                      end
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                      it "should add new tags" do
         
     | 
| 
      
 221 
     | 
    
         
            +
                        @resource.reload.tags.should include(Tag["bar"], Tag["pub"])
         
     | 
| 
      
 222 
     | 
    
         
            +
                      end
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
                      it "should remove tags" do
         
     | 
| 
      
 225 
     | 
    
         
            +
                        @resource.reload.tags.should_not include(Tag["foo"])
         
     | 
| 
      
 226 
     | 
    
         
            +
                      end
         
     | 
| 
      
 227 
     | 
    
         
            +
                    end
         
     | 
| 
      
 228 
     | 
    
         
            +
                  end
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
                  describe "when no list of tag names is given" do
         
     | 
| 
      
 231 
     | 
    
         
            +
                    before :all do
         
     | 
| 
      
 232 
     | 
    
         
            +
                      @resource = create_taggable(:tag_list => "foo, bar")
         
     | 
| 
      
 233 
     | 
    
         
            +
                      @resource.update(:tag_list => "")
         
     | 
| 
      
 234 
     | 
    
         
            +
                    end
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                    it "should destroy taggings" do
         
     | 
| 
      
 237 
     | 
    
         
            +
                      @resource.reload.taggings.should be_blank
         
     | 
| 
      
 238 
     | 
    
         
            +
                    end
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
                    it "should remove the tags" do
         
     | 
| 
      
 241 
     | 
    
         
            +
                      @resource.reload.tags.should be_blank
         
     | 
| 
      
 242 
     | 
    
         
            +
                    end
         
     | 
| 
      
 243 
     | 
    
         
            +
                  end
         
     | 
| 
      
 244 
     | 
    
         
            +
                end
         
     | 
| 
      
 245 
     | 
    
         
            +
             
     | 
| 
      
 246 
     | 
    
         
            +
                describe ".tag_list" do
         
     | 
| 
      
 247 
     | 
    
         
            +
                  before :all do
         
     | 
| 
      
 248 
     | 
    
         
            +
                    @tag_names = %w(red green blue)
         
     | 
| 
      
 249 
     | 
    
         
            +
                    @expected  = @tag_names.join(', ')
         
     | 
| 
      
 250 
     | 
    
         
            +
                    @resource  = create_taggable(:tag_list => @expected)
         
     | 
| 
      
 251 
     | 
    
         
            +
                  end
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
                  it "should return the list of tag names" do
         
     | 
| 
      
 254 
     | 
    
         
            +
                    @resource.tag_list.should eql(@expected)
         
     | 
| 
      
 255 
     | 
    
         
            +
                  end
         
     | 
| 
      
 256 
     | 
    
         
            +
                end
         
     | 
| 
      
 257 
     | 
    
         
            +
              end
         
     | 
| 
      
 258 
     | 
    
         
            +
            end
         
     | 
| 
      
 259 
     | 
    
         
            +
             
     |