acts-as-taggable-on 2.0.0 → 2.0.6

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.
Files changed (32) hide show
  1. data/Gemfile +7 -3
  2. data/README.rdoc +14 -14
  3. data/VERSION +1 -1
  4. data/lib/acts-as-taggable-on.rb +1 -1
  5. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +3 -3
  6. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +56 -22
  7. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +26 -22
  8. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +12 -12
  9. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +7 -6
  10. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +2 -2
  11. data/lib/acts_as_taggable_on/acts_as_tagger.rb +2 -2
  12. data/lib/acts_as_taggable_on/compatibility/Gemfile +3 -1
  13. data/lib/acts_as_taggable_on/compatibility/postgresql.rb +44 -0
  14. data/lib/acts_as_taggable_on/tag.rb +53 -44
  15. data/lib/acts_as_taggable_on/tag_list.rb +79 -78
  16. data/lib/acts_as_taggable_on/tagging.rb +19 -18
  17. data/lib/acts_as_taggable_on/tags_helper.rb +12 -12
  18. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +2 -1
  19. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +4 -2
  20. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +3 -3
  21. data/spec/acts_as_taggable_on/tag_list_spec.rb +3 -3
  22. data/spec/acts_as_taggable_on/tag_spec.rb +21 -21
  23. data/spec/acts_as_taggable_on/taggable_spec.rb +37 -12
  24. data/spec/acts_as_taggable_on/tagger_spec.rb +5 -5
  25. data/spec/acts_as_taggable_on/tagging_spec.rb +7 -7
  26. data/spec/acts_as_taggable_on/tags_helper_spec.rb +3 -3
  27. data/spec/database.yml +17 -0
  28. data/spec/database.yml.sample +17 -0
  29. data/spec/models.rb +1 -0
  30. data/spec/spec_helper.rb +40 -33
  31. metadata +6 -4
  32. data/spec/spec.opts +0 -2
@@ -1,65 +1,74 @@
1
- class Tag < ActiveRecord::Base
2
- include ActsAsTaggableOn::ActiveRecord::Backports if ActiveRecord::VERSION::MAJOR < 3
1
+ module ActsAsTaggableOn
2
+ class Tag < ::ActiveRecord::Base
3
+ include ActsAsTaggableOn::ActiveRecord::Backports if ::ActiveRecord::VERSION::MAJOR < 3
3
4
 
4
- attr_accessible :name
5
+ attr_accessible :name
5
6
 
6
- ### ASSOCIATIONS:
7
+ ### ASSOCIATIONS:
7
8
 
8
- has_many :taggings, :dependent => :destroy
9
+ has_many :taggings, :dependent => :destroy, :class_name => 'ActsAsTaggableOn::Tagging'
9
10
 
10
- ### VALIDATIONS:
11
+ ### VALIDATIONS:
11
12
 
12
- validates_presence_of :name
13
- validates_uniqueness_of :name
13
+ validates_presence_of :name
14
+ validates_uniqueness_of :name
14
15
 
15
- ### SCOPES:
16
+ ### SCOPES:
16
17
 
17
- def self.named(name)
18
- where(["name LIKE ?", name])
19
- end
18
+ def self.named(name)
19
+ where(["name #{like_operator} ?", name])
20
+ end
20
21
 
21
- def self.named_any(list)
22
- where(list.map { |tag| sanitize_sql(["name LIKE ?", tag.to_s]) }.join(" OR "))
23
- end
22
+ def self.named_any(list)
23
+ where(list.map { |tag| sanitize_sql(["name #{like_operator} ?", tag.to_s]) }.join(" OR "))
24
+ end
24
25
 
25
- def self.named_like(name)
26
- where(["name LIKE ?", "%#{name}%"])
27
- end
26
+ def self.named_like(name)
27
+ where(["name #{like_operator} ?", "%#{name}%"])
28
+ end
28
29
 
29
- def self.named_like_any(list)
30
- where(list.map { |tag| sanitize_sql(["name LIKE ?", "%#{tag.to_s}%"]) }.join(" OR "))
31
- end
30
+ def self.named_like_any(list)
31
+ where(list.map { |tag| sanitize_sql(["name #{like_operator} ?", "%#{tag.to_s}%"]) }.join(" OR "))
32
+ end
32
33
 
33
- ### CLASS METHODS:
34
+ ### CLASS METHODS:
34
35
 
35
- def self.find_or_create_with_like_by_name(name)
36
- named_like(name).first || create(:name => name)
37
- end
36
+ def self.find_or_create_with_like_by_name(name)
37
+ named_like(name).first || create(:name => name)
38
+ end
38
39
 
39
- def self.find_or_create_all_with_like_by_name(*list)
40
- list = [list].flatten
40
+ def self.find_or_create_all_with_like_by_name(*list)
41
+ list = [list].flatten
41
42
 
42
- return [] if list.empty?
43
+ return [] if list.empty?
43
44
 
44
- existing_tags = Tag.named_any(list).all
45
- new_tag_names = list.reject { |name| existing_tags.any? { |tag| tag.name.mb_chars.downcase == name.mb_chars.downcase } }
46
- created_tags = new_tag_names.map { |name| Tag.create(:name => name) }
45
+ existing_tags = Tag.named_any(list).all
46
+ new_tag_names = list.reject { |name| existing_tags.any? { |tag| tag.name.mb_chars.downcase == name.mb_chars.downcase } }
47
+ created_tags = new_tag_names.map { |name| Tag.create(:name => name) }
47
48
 
48
- existing_tags + created_tags
49
- end
49
+ existing_tags + created_tags
50
+ end
50
51
 
51
- ### INSTANCE METHODS:
52
+ ### INSTANCE METHODS:
52
53
 
53
- def ==(object)
54
- super || (object.is_a?(Tag) && name == object.name)
55
- end
54
+ def ==(object)
55
+ super || (object.is_a?(Tag) && name == object.name)
56
+ end
56
57
 
57
- def to_s
58
- name
59
- end
58
+ def to_s
59
+ name
60
+ end
60
61
 
61
- def count
62
- read_attribute(:count).to_i
63
- end
62
+ def count
63
+ read_attribute(:count).to_i
64
+ end
64
65
 
65
- end
66
+ class << self
67
+ private
68
+ def like_operator
69
+ connection.adapter_name == 'PostgreSQL' ? 'ILIKE' : 'LIKE'
70
+ end
71
+ end
72
+
73
+ end
74
+ end
@@ -1,95 +1,96 @@
1
- class TagList < Array
1
+ module ActsAsTaggableOn
2
+ class TagList < Array
3
+ cattr_accessor :delimiter
4
+ self.delimiter = ','
2
5
 
3
- cattr_accessor :delimiter
4
- self.delimiter = ','
6
+ attr_accessor :owner
5
7
 
6
- attr_accessor :owner
7
-
8
- def initialize(*args)
9
- add(*args)
10
- end
8
+ def initialize(*args)
9
+ add(*args)
10
+ end
11
11
 
12
- ##
13
- # Returns a new TagList using the given tag string.
14
- #
15
- # Example:
16
- # tag_list = TagList.from("One , Two, Three")
17
- # tag_list # ["One", "Two", "Three"]
18
- def self.from(string)
19
- string = string.join(", ") if string.respond_to?(:join)
12
+ ##
13
+ # Returns a new TagList using the given tag string.
14
+ #
15
+ # Example:
16
+ # tag_list = TagList.from("One , Two, Three")
17
+ # tag_list # ["One", "Two", "Three"]
18
+ def self.from(string)
19
+ glue = delimiter.ends_with?(" ") ? delimiter : "#{delimiter} "
20
+ string = string.join(glue) if string.respond_to?(:join)
20
21
 
21
- new.tap do |tag_list|
22
- string = string.to_s.dup
22
+ new.tap do |tag_list|
23
+ string = string.to_s.dup
23
24
 
24
- # Parse the quoted tags
25
- string.gsub!(/(\A|#{delimiter})\s*"(.*?)"\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
26
- string.gsub!(/(\A|#{delimiter})\s*'(.*?)'\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
25
+ # Parse the quoted tags
26
+ string.gsub!(/(\A|#{delimiter})\s*"(.*?)"\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
27
+ string.gsub!(/(\A|#{delimiter})\s*'(.*?)'\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
27
28
 
28
- tag_list.add(string.split(delimiter))
29
+ tag_list.add(string.split(delimiter))
30
+ end
29
31
  end
30
- end
31
32
 
32
- ##
33
- # Add tags to the tag_list. Duplicate or blank tags will be ignored.
34
- # Use the <tt>:parse</tt> option to add an unparsed tag string.
35
- #
36
- # Example:
37
- # tag_list.add("Fun", "Happy")
38
- # tag_list.add("Fun, Happy", :parse => true)
39
- def add(*names)
40
- extract_and_apply_options!(names)
41
- concat(names)
42
- clean!
43
- self
44
- end
33
+ ##
34
+ # Add tags to the tag_list. Duplicate or blank tags will be ignored.
35
+ # Use the <tt>:parse</tt> option to add an unparsed tag string.
36
+ #
37
+ # Example:
38
+ # tag_list.add("Fun", "Happy")
39
+ # tag_list.add("Fun, Happy", :parse => true)
40
+ def add(*names)
41
+ extract_and_apply_options!(names)
42
+ concat(names)
43
+ clean!
44
+ self
45
+ end
45
46
 
46
- ##
47
- # Remove specific tags from the tag_list.
48
- # Use the <tt>:parse</tt> option to add an unparsed tag string.
49
- #
50
- # Example:
51
- # tag_list.remove("Sad", "Lonely")
52
- # tag_list.remove("Sad, Lonely", :parse => true)
53
- def remove(*names)
54
- extract_and_apply_options!(names)
55
- delete_if { |name| names.include?(name) }
56
- self
57
- end
47
+ ##
48
+ # Remove specific tags from the tag_list.
49
+ # Use the <tt>:parse</tt> option to add an unparsed tag string.
50
+ #
51
+ # Example:
52
+ # tag_list.remove("Sad", "Lonely")
53
+ # tag_list.remove("Sad, Lonely", :parse => true)
54
+ def remove(*names)
55
+ extract_and_apply_options!(names)
56
+ delete_if { |name| names.include?(name) }
57
+ self
58
+ end
58
59
 
59
- ##
60
- # Transform the tag_list into a tag string suitable for edting in a form.
61
- # The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
62
- #
63
- # Example:
64
- # tag_list = TagList.new("Round", "Square,Cube")
65
- # tag_list.to_s # 'Round, "Square,Cube"'
66
- def to_s
67
- tags = frozen? ? self.dup : self
68
- tags.send(:clean!)
60
+ ##
61
+ # Transform the tag_list into a tag string suitable for edting in a form.
62
+ # The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
63
+ #
64
+ # Example:
65
+ # tag_list = TagList.new("Round", "Square,Cube")
66
+ # tag_list.to_s # 'Round, "Square,Cube"'
67
+ def to_s
68
+ tags = frozen? ? self.dup : self
69
+ tags.send(:clean!)
69
70
 
70
- tags.map do |name|
71
- name.include?(delimiter) ? "\"#{name}\"" : name
72
- end.join(delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ")
73
- end
71
+ tags.map do |name|
72
+ name.include?(delimiter) ? "\"#{name}\"" : name
73
+ end.join(delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ")
74
+ end
74
75
 
75
- private
76
+ private
76
77
 
77
- # Remove whitespace, duplicates, and blanks.
78
- def clean!
79
- reject!(&:blank?)
80
- map!(&:strip)
81
- uniq!
82
- end
78
+ # Remove whitespace, duplicates, and blanks.
79
+ def clean!
80
+ reject!(&:blank?)
81
+ map!(&:strip)
82
+ uniq!
83
+ end
83
84
 
84
- def extract_and_apply_options!(args)
85
- options = args.last.is_a?(Hash) ? args.pop : {}
86
- options.assert_valid_keys :parse
85
+ def extract_and_apply_options!(args)
86
+ options = args.last.is_a?(Hash) ? args.pop : {}
87
+ options.assert_valid_keys :parse
87
88
 
88
- if options[:parse]
89
- args.map! { |a| self.class.from(a) }
90
- end
89
+ if options[:parse]
90
+ args.map! { |a| self.class.from(a) }
91
+ end
91
92
 
92
- args.flatten!
93
+ args.flatten!
94
+ end
93
95
  end
94
-
95
- end
96
+ end
@@ -1,23 +1,24 @@
1
- class Tagging < ActiveRecord::Base #:nodoc:
2
- include ActsAsTaggableOn::ActiveRecord::Backports if ActiveRecord::VERSION::MAJOR < 3
1
+ module ActsAsTaggableOn
2
+ class Tagging < ::ActiveRecord::Base #:nodoc:
3
+ include ActsAsTaggableOn::ActiveRecord::Backports if ::ActiveRecord::VERSION::MAJOR < 3
3
4
 
4
- attr_accessible :tag,
5
- :tag_id,
6
- :context,
7
- :taggable,
8
- :taggable_type,
9
- :taggable_id,
10
- :tagger,
11
- :tagger_type,
12
- :tagger_id
5
+ attr_accessible :tag,
6
+ :tag_id,
7
+ :context,
8
+ :taggable,
9
+ :taggable_type,
10
+ :taggable_id,
11
+ :tagger,
12
+ :tagger_type,
13
+ :tagger_id
13
14
 
14
- belongs_to :tag
15
- belongs_to :taggable, :polymorphic => true
16
- belongs_to :tagger, :polymorphic => true
15
+ belongs_to :tag, :class_name => 'ActsAsTaggableOn::Tag'
16
+ belongs_to :taggable, :polymorphic => true
17
+ belongs_to :tagger, :polymorphic => true
17
18
 
18
- validates_presence_of :context
19
- validates_presence_of :tag_id
20
-
21
- validates_uniqueness_of :tag_id, :scope => [ :taggable_type, :taggable_id, :context, :tagger_id, :tagger_type ]
19
+ validates_presence_of :context
20
+ validates_presence_of :tag_id
22
21
 
22
+ validates_uniqueness_of :tag_id, :scope => [ :taggable_type, :taggable_id, :context, :tagger_id, :tagger_type ]
23
+ end
23
24
  end
@@ -1,17 +1,17 @@
1
- module TagsHelper
1
+ module ActsAsTaggableOn
2
+ module TagsHelper
3
+ # See the README for an example using tag_cloud.
4
+ def tag_cloud(tags, classes)
5
+ tags = tags.all if tags.respond_to?(:all)
2
6
 
3
- # See the README for an example using tag_cloud.
4
- def tag_cloud(tags, classes)
5
- tags = tags.all if tags.respond_to?(:all)
7
+ return [] if tags.empty?
6
8
 
7
- return [] if tags.empty?
9
+ max_count = tags.sort_by(&:count).last.count.to_f
8
10
 
9
- max_count = tags.sort_by(&:count).last.count.to_f
10
-
11
- tags.each do |tag|
12
- index = ((tag.count / max_count) * (classes.size - 1)).round
13
- yield tag, classes[index]
11
+ tags.each do |tag|
12
+ index = ((tag.count / max_count) * (classes.size - 1)).round
13
+ yield tag, classes[index]
14
+ end
14
15
  end
15
16
  end
16
-
17
- end
17
+ end
@@ -11,7 +11,7 @@ module ActsAsTaggableOn
11
11
  end
12
12
 
13
13
  def self.source_root
14
- File.join(File.dirname(__FILE__), 'templates', orm)
14
+ File.join(File.dirname(__FILE__), 'templates', (orm.to_s unless orm.class.eql?(String)) )
15
15
  end
16
16
 
17
17
  def self.orm_has_migration?
@@ -29,3 +29,4 @@ module ActsAsTaggableOn
29
29
  end
30
30
  end
31
31
  end
32
+
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
2
 
3
3
  describe "Acts As Taggable On" do
4
4
  before(:each) do
@@ -11,7 +11,9 @@ describe "Acts As Taggable On" do
11
11
 
12
12
  describe "Taggable Method Generation" do
13
13
  before(:each) do
14
- [TaggableModel, Tag, Tagging, TaggableUser].each(&:delete_all)
14
+ clean_database!
15
+ TaggableModel.write_inheritable_attribute(:tag_types, [])
16
+ TaggableModel.acts_as_taggable_on(:tags, :languages, :skills, :needs, :offerings)
15
17
  @taggable = TaggableModel.new(:name => "Bob Jones")
16
18
  end
17
19
 
@@ -1,11 +1,11 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
2
 
3
3
  describe "acts_as_tagger" do
4
4
  before(:each) do
5
5
  clean_database!
6
6
  end
7
7
 
8
- context "Tagger Method Generation" do
8
+ describe "Tagger Method Generation" do
9
9
  before(:each) do
10
10
  @tagger = TaggableUser.new()
11
11
  end
@@ -84,7 +84,7 @@ describe "acts_as_tagger" do
84
84
  end
85
85
  end
86
86
 
87
- context "when called by multiple tagger's" do
87
+ describe "when called by multiple tagger's" do
88
88
  before(:each) do
89
89
  @user_x = TaggableUser.create(:name => "User X")
90
90
  @user_y = TaggableUser.create(:name => "User Y")
@@ -1,8 +1,8 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
2
 
3
- describe TagList do
3
+ describe ActsAsTaggableOn::TagList do
4
4
  before(:each) do
5
- @tag_list = TagList.new("awesome","radical")
5
+ @tag_list = ActsAsTaggableOn::TagList.new("awesome","radical")
6
6
  end
7
7
 
8
8
  it "should be an array" do
@@ -1,20 +1,20 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
2
 
3
- describe Tag do
3
+ describe ActsAsTaggableOn::Tag do
4
4
  before(:each) do
5
5
  clean_database!
6
- @tag = Tag.new
6
+ @tag = ActsAsTaggableOn::Tag.new
7
7
  @user = TaggableModel.create(:name => "Pablo")
8
8
  end
9
9
 
10
10
  describe "named like any" do
11
11
  before(:each) do
12
- Tag.create(:name => "awesome")
13
- Tag.create(:name => "epic")
12
+ ActsAsTaggableOn::Tag.create(:name => "awesome")
13
+ ActsAsTaggableOn::Tag.create(:name => "epic")
14
14
  end
15
15
 
16
16
  it "should find both tags" do
17
- Tag.named_like_any(["awesome", "epic"]).should have(2).items
17
+ ActsAsTaggableOn::Tag.named_like_any(["awesome", "epic"]).should have(2).items
18
18
  end
19
19
  end
20
20
 
@@ -25,17 +25,17 @@ describe Tag do
25
25
  end
26
26
 
27
27
  it "should find by name" do
28
- Tag.find_or_create_with_like_by_name("awesome").should == @tag
28
+ ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("awesome").should == @tag
29
29
  end
30
30
 
31
31
  it "should find by name case insensitive" do
32
- Tag.find_or_create_with_like_by_name("AWESOME").should == @tag
32
+ ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("AWESOME").should == @tag
33
33
  end
34
34
 
35
35
  it "should create by name" do
36
36
  lambda {
37
- Tag.find_or_create_with_like_by_name("epic")
38
- }.should change(Tag, :count).by(1)
37
+ ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("epic")
38
+ }.should change(ActsAsTaggableOn::Tag, :count).by(1)
39
39
  end
40
40
  end
41
41
 
@@ -46,27 +46,27 @@ describe Tag do
46
46
  end
47
47
 
48
48
  it "should find by name" do
49
- Tag.find_or_create_all_with_like_by_name("awesome").should == [@tag]
49
+ ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name("awesome").should == [@tag]
50
50
  end
51
51
 
52
52
  it "should find by name case insensitive" do
53
- Tag.find_or_create_all_with_like_by_name("AWESOME").should == [@tag]
53
+ ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name("AWESOME").should == [@tag]
54
54
  end
55
55
 
56
56
  it "should create by name" do
57
57
  lambda {
58
- Tag.find_or_create_all_with_like_by_name("epic")
59
- }.should change(Tag, :count).by(1)
58
+ ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name("epic")
59
+ }.should change(ActsAsTaggableOn::Tag, :count).by(1)
60
60
  end
61
61
 
62
62
  it "should find or create by name" do
63
63
  lambda {
64
- Tag.find_or_create_all_with_like_by_name("awesome", "epic").map(&:name).should == ["awesome", "epic"]
65
- }.should change(Tag, :count).by(1)
64
+ ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name("awesome", "epic").map(&:name).should == ["awesome", "epic"]
65
+ }.should change(ActsAsTaggableOn::Tag, :count).by(1)
66
66
  end
67
67
 
68
68
  it "should return an empty array if no tags are specified" do
69
- Tag.find_or_create_all_with_like_by_name([]).should == []
69
+ ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name([]).should == []
70
70
  end
71
71
  end
72
72
 
@@ -91,7 +91,7 @@ describe Tag do
91
91
 
92
92
  it "should equal a tag with the same name" do
93
93
  @tag.name = "awesome"
94
- new_tag = Tag.new(:name => "awesome")
94
+ new_tag = ActsAsTaggableOn::Tag.new(:name => "awesome")
95
95
  new_tag.should == @tag
96
96
  end
97
97
 
@@ -103,13 +103,13 @@ describe Tag do
103
103
  it "have named_scope named(something)" do
104
104
  @tag.name = "cool"
105
105
  @tag.save!
106
- Tag.named('cool').should include(@tag)
106
+ ActsAsTaggableOn::Tag.named('cool').should include(@tag)
107
107
  end
108
108
 
109
109
  it "have named_scope named_like(something)" do
110
110
  @tag.name = "cool"
111
111
  @tag.save!
112
- @another_tag = Tag.create!(:name => "coolip")
113
- Tag.named_like('cool').should include(@tag, @another_tag)
112
+ @another_tag = ActsAsTaggableOn::Tag.create!(:name => "coolip")
113
+ ActsAsTaggableOn::Tag.named_like('cool').should include(@tag, @another_tag)
114
114
  end
115
115
  end
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
2
 
3
3
  describe "Taggable" do
4
4
  before(:each) do
@@ -26,11 +26,11 @@ describe "Taggable" do
26
26
 
27
27
  it "should be able to create tags" do
28
28
  @taggable.skill_list = "ruby, rails, css"
29
- @taggable.instance_variable_get("@skill_list").instance_of?(TagList).should be_true
29
+ @taggable.instance_variable_get("@skill_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
30
30
 
31
31
  lambda {
32
32
  @taggable.save
33
- }.should change(Tag, :count).by(3)
33
+ }.should change(ActsAsTaggableOn::Tag, :count).by(3)
34
34
 
35
35
  @taggable.reload
36
36
  @taggable.skill_list.sort.should == %w(ruby rails css).sort
@@ -90,7 +90,7 @@ describe "Taggable" do
90
90
  bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby")
91
91
  frank = TaggableModel.create(:name => "Frank", :tag_list => "Ruby")
92
92
 
93
- Tag.find(:all).size.should == 1
93
+ ActsAsTaggableOn::Tag.find(:all).size.should == 1
94
94
  TaggableModel.tagged_with("ruby").to_a.should == TaggableModel.tagged_with("Ruby").to_a
95
95
  end
96
96
 
@@ -108,7 +108,7 @@ describe "Taggable" do
108
108
  charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
109
109
 
110
110
  TaggableModel.all_tag_counts.all.should_not be_empty
111
- TaggableModel.all_tag_counts.first.count.should == 3 # ruby
111
+ TaggableModel.all_tag_counts(:order => 'tags.id').first.count.should == 3 # ruby
112
112
  end
113
113
 
114
114
  if ActiveRecord::VERSION::MAJOR >= 3
@@ -132,7 +132,7 @@ describe "Taggable" do
132
132
  frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
133
133
  charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
134
134
 
135
- TaggableModel.tagged_with("ruby").tag_counts.first.count.should == 2 # ruby
135
+ TaggableModel.tagged_with("ruby").tag_counts(:order => 'tags.id').first.count.should == 2 # ruby
136
136
  TaggableModel.tagged_with("ruby").skill_counts.first.count.should == 1 # ruby
137
137
  end
138
138
 
@@ -141,7 +141,22 @@ describe "Taggable" do
141
141
  frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
142
142
  charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
143
143
 
144
- TaggableModel.tagged_with("ruby").all_tag_counts.first.count.should == 3 # ruby
144
+ TaggableModel.tagged_with("ruby").all_tag_counts(:order => 'tags.id').first.count.should == 3 # ruby
145
+ end
146
+
147
+ it 'should only return tag counts for the available scope' do
148
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
149
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
150
+ charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby, java")
151
+
152
+ TaggableModel.tagged_with('rails').all_tag_counts.should have(3).items
153
+ TaggableModel.tagged_with('rails').all_tag_counts.any? { |tag| tag.name == 'java' }.should be_false
154
+
155
+ # Test specific join syntaxes:
156
+ frank.untaggable_models.create!
157
+ TaggableModel.tagged_with('rails').scoped(:joins => :untaggable_models).all_tag_counts.should have(2).items
158
+ TaggableModel.tagged_with('rails').scoped(:joins => { :untaggable_models => :taggable_model }).all_tag_counts.should have(2).items
159
+ TaggableModel.tagged_with('rails').scoped(:joins => [:untaggable_models]).all_tag_counts.should have(2).items
145
160
  end
146
161
 
147
162
  it "should be able to set a custom tag context list" do
@@ -162,6 +177,16 @@ describe "Taggable" do
162
177
  TaggableModel.tagged_with("ruby, rails", :order => 'taggable_models.name').to_a.should == [bob, frank]
163
178
  TaggableModel.tagged_with(["ruby", "rails"], :order => 'taggable_models.name').to_a.should == [bob, frank]
164
179
  end
180
+
181
+ it "should be able to find tagged with quotation marks" do
182
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive, 'I love the ,comma,'")
183
+ TaggableModel.tagged_with("'I love the ,comma,'").should include(bob)
184
+ end
185
+
186
+ it "should be able to find tagged with invalid tags" do
187
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive")
188
+ TaggableModel.tagged_with("sad, happier").should_not include(bob)
189
+ end
165
190
 
166
191
  it "should be able to find tagged with any tag" do
167
192
  bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive", :skill_list => "ruby, rails, css")
@@ -216,7 +241,7 @@ describe "Taggable" do
216
241
  bob.tag_list << "happier"
217
242
  bob.tag_list << "happier"
218
243
  bob.save
219
- }.should change(Tagging, :count).by(1)
244
+ }.should change(ActsAsTaggableOn::Tagging, :count).by(1)
220
245
  end
221
246
 
222
247
  describe "Associations" do
@@ -233,7 +258,7 @@ describe "Taggable" do
233
258
 
234
259
  describe "grouped_column_names_for method" do
235
260
  it "should return all column names joined for Tag GROUP clause" do
236
- @taggable.grouped_column_names_for(Tag).should == "tags.id, tags.name"
261
+ @taggable.grouped_column_names_for(ActsAsTaggableOn::Tag).should == "tags.id, tags.name"
237
262
  end
238
263
 
239
264
  it "should return all column names joined for TaggableModel GROUP clause" do
@@ -273,9 +298,9 @@ describe "Taggable" do
273
298
  @inherited_different.tag_list = "fork, spoon"
274
299
  @inherited_different.save!
275
300
 
276
- InheritingTaggableModel.tag_counts_on(:tags).map(&:name).should == %w(bob kelso)
277
- AlteredInheritingTaggableModel.tag_counts_on(:tags).map(&:name).should == %w(fork spoon)
278
- TaggableModel.tag_counts_on(:tags).map(&:name).should == %w(bob kelso fork spoon)
301
+ InheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso)
302
+ AlteredInheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(fork spoon)
303
+ TaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso fork spoon)
279
304
  end
280
305
 
281
306
  it 'should store same tag without validation conflict' do