require 'spec_helper'

describe "Taggable" do
  supported_by :sqlite, :mysql, :postgres do
    before do
      @taggable = DefaultTaggedModel.new
    end

    it "should return an alphabetically sorted array of the tag names when sent #tag_list" do
      tag_names = %w[ tag1 tag2 tag3 ]

      # @taggable.tags = tag_names.map { |name| { :name => name } }

      tag_names.each do |name|
        @taggable.tag_taggings.new(:tag => Tag.create(:name => name))
      end

      @taggable.save.should be(true)

      @taggable = @taggable.model.get!(@taggable.key)
      @taggable.tag_list.should == tag_names
    end

    it "should set the tag list to a sanitized, stripped, alphabetized, unique array of tag names" do
      @taggable.tag_list = "tags, !$%^&* !@more-stuff' &, tags , me_again9,  et tu   " # Must check for redundancy
      valid_array = ["et tu", "me_again9", "more-stuff", "tags"]
      @taggable.tag_list.should == valid_array
      @taggable.instance_variable_get(:@tag_list).should == valid_array
      @taggable.save
      @taggable = DefaultTaggedModel.first
      @taggable.tag_list.should == valid_array
    end

    it "should set the associated collection of tags to those whose names
        are in the tag list upon saving, creating and deleting as necessary" do
      tag1 = Tag.create(:name => 'tag1')
      tag2 = Tag.create(:name => 'tag2')
      tag3 = Tag.create(:name => 'tag3')
      @taggable = TaggedModel.new
      @taggable.tag_list = 'tag1, tag2, tag3'
      @taggable.save.should be(true)
      @taggable.tags.sort_by{|tag| tag.id}.should == [tag1, tag2, tag3]
      @taggable.tag_list = 'tag1, tag2'
      @taggable.save.should be(true) # Should dirty the model when changed.
      pending do
        @taggable.tags.sort_by{|tag| tag.id}.should == [tag1, tag2]
      end
      @taggable.tag_list = 'tag3, tag4'
      @taggable.save.should be(true)
      @taggable = @taggable.model.get(*@taggable.key)
      pending do
        @taggable.tags.sort_by{|tag| tag.id}.should == [tag3, Tag.first(:name => 'tag4')]
        @taggable.skills.sort_by{|skill| skill.id}.should_not == [tag3, Tag.first(:name => 'tag4')]
      end
    end

    it "should set tags with a string, and return a string (form helpers)" do
      @taggable = TaggedModel.new
      tags_string = "tag-a, tag-b, tag-c"
      @taggable.tag_collection = tags_string
      @taggable.save
      @taggable.tag_collection.should == tags_string
      @taggable.tags.size.should == 3
    end

    it "should be able to add tags and not overwrite old tags" do
      @taggable = TaggedModel.new
      @taggable.add_tag("tag-1")
      @taggable.save
      @taggable.tags.size.should == 1
      @taggable.add_tag("tag-2, tag-3")
      @taggable.save
      pending do
        @taggable.tags.size.should == 3
      end
      @taggable.add_tag("tag-4")
      @taggable.tag_list.include?("tag-4").should be(true)
      @taggable.tag_list.include?("tag-1").should be(true)
      @taggable.save
      @taggable.tags.size.should == 4
    end

    describe ".tagged_with" do
      it "should have a class method .tagged_with" do
        DefaultTaggedModel.should respond_to(:tagged_with)
        UntaggedModel.should_not respond_to(:tagged_with)
      end

      it "should return taggables tagged with the name given in the first argument" do
        @taggable.tag_list = 'tag1, tag2, tag3'
        @taggable.save
        taggable = DefaultTaggedModel.new
        taggable.tag_list = 'tag1, goat, fish'
        taggable.save
        DefaultTaggedModel.tagged_with('tag1').sort_by{|t| t.id}.to_a.should == [@taggable, taggable]
      end

      it "should return taggables of the context specified by the second argument" do
        taggable1 = TaggedModel.new
        taggable2 = TaggedModel.new
        taggable1.tag_list = 'tag1, tag2, tag3'
        taggable2.skill_list = 'tag1, skill4'
        taggable1.save
        taggable2.save
        TaggedModel.tagged_with('tag1').should == [taggable1, taggable2]
        TaggedModel.tagged_with('tag1', :on => 'skills').should == [taggable2]
        TaggedModel.tagged_with('tag1', :on => 'tags').should == [taggable1]
      end
    end

    it "should allow extra conditions for the query" do
      taggable1 = TaggedModel.new
      taggable2 = TaggedModel.new
      taggable1.tag_list = 'tag1, tag2, tag3'
      taggable2.skill_list = 'tag1, skill4'
      taggable1.save
      taggable2.save
      TaggedModel.tagged_with('tag1').should == [taggable1, taggable2]
      TaggedModel.tagged_with('tag1', :id => taggable1.id).should == [taggable1]
    end


    it "should have a class method .taggable? which returns true if tagging is defined, and false otherwise" do
      UntaggedModel.taggable?.should be(false)
      TaggedModel.taggable?.should be(true)
    end

    it 'should return an empty list if tag is not present (should not continue on nil tag)' do
      taggable = DefaultTaggedModel.new
      taggable.tag_list = 'tag1, tag2, tag3'
      taggable.save

      DefaultTaggedModel.tagged_with('tag5').should == []
    end

    it 'should return an empty list nothing if tag is present but not associated with model' do
      taggable1 = DefaultTaggedModel.new
      taggable1.tag_list = 'tag1, tag2, tag3'
      taggable1.save
      taggable2 = TaggedModel.new
      taggable2.tag_list = 'tag1, tag2, tag5'
      taggable2.save

      DefaultTaggedModel.tagged_with('tag5').should == []
    end

    it "should have an instance method #taggable? which returns the same as the instance's class would" do
      UntaggedModel.new.taggable?.should == UntaggedModel.taggable?
      UntaggedModel.new.taggable?.should be(false)
      TaggedModel.new.taggable?.should == TaggedModel.taggable?
      TaggedModel.new.taggable?.should be(true)
    end

    it 'should destroy associated taggings when destroyed' do
      taggable = TaggedModel.new
      taggable.tag_list = 'tag1, tag2, tag3'
      taggable.save
      TaggedModel.tagged_with('tag1').should == [taggable]
      taggable.destroy
      TaggedModel.tagged_with('tag1').should == []
    end
  end
end
