acts-as-taggable-on 1.0.13 → 2.0.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.
Files changed (40) hide show
  1. data/CHANGELOG +5 -2
  2. data/Gemfile +6 -0
  3. data/README.rdoc +61 -31
  4. data/Rakefile +46 -16
  5. data/VERSION +1 -1
  6. data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +7 -0
  7. data/generators/acts_as_taggable_on_migration/templates/migration.rb +29 -0
  8. data/lib/acts-as-taggable-on.rb +30 -7
  9. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +53 -0
  10. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +98 -0
  11. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +237 -0
  12. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +101 -0
  13. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +64 -0
  14. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +43 -373
  15. data/lib/acts_as_taggable_on/acts_as_tagger.rb +58 -43
  16. data/lib/acts_as_taggable_on/compatibility/Gemfile +6 -0
  17. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +17 -0
  18. data/lib/acts_as_taggable_on/tag.rb +47 -8
  19. data/lib/acts_as_taggable_on/tag_list.rb +45 -45
  20. data/lib/acts_as_taggable_on/tagging.rb +17 -2
  21. data/lib/acts_as_taggable_on/tags_helper.rb +8 -2
  22. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +31 -0
  23. data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +28 -0
  24. data/rails/init.rb +1 -7
  25. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +98 -53
  26. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +46 -4
  27. data/spec/acts_as_taggable_on/tag_list_spec.rb +18 -0
  28. data/spec/acts_as_taggable_on/tag_spec.rb +66 -13
  29. data/spec/acts_as_taggable_on/taggable_spec.rb +142 -70
  30. data/spec/acts_as_taggable_on/tagger_spec.rb +73 -5
  31. data/spec/acts_as_taggable_on/tagging_spec.rb +18 -3
  32. data/spec/acts_as_taggable_on/tags_helper_spec.rb +1 -3
  33. data/spec/bm.rb +52 -0
  34. data/spec/models.rb +30 -0
  35. data/spec/schema.rb +13 -2
  36. data/spec/spec.opts +1 -2
  37. data/spec/spec_helper.rb +39 -34
  38. metadata +28 -8
  39. data/lib/acts_as_taggable_on/group_helper.rb +0 -12
  40. data/spec/acts_as_taggable_on/group_helper_spec.rb +0 -18
@@ -1,19 +1,40 @@
1
1
  class TagList < Array
2
+
2
3
  cattr_accessor :delimiter
3
4
  self.delimiter = ','
4
-
5
+
6
+ attr_accessor :owner
7
+
5
8
  def initialize(*args)
6
9
  add(*args)
7
10
  end
8
11
 
9
- attr_accessor :owner
10
-
11
- # Add tags to the tag_list. Duplicate or blank tags will be ignored.
12
+ ##
13
+ # Returns a new TagList using the given tag string.
12
14
  #
13
- # tag_list.add("Fun", "Happy")
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)
20
+
21
+ new.tap do |tag_list|
22
+ string = string.to_s.dup
23
+
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 }
27
+
28
+ tag_list.add(string.split(delimiter))
29
+ end
30
+ end
31
+
32
+ ##
33
+ # Add tags to the tag_list. Duplicate or blank tags will be ignored.
15
34
  # Use the <tt>:parse</tt> option to add an unparsed tag string.
16
35
  #
36
+ # Example:
37
+ # tag_list.add("Fun", "Happy")
17
38
  # tag_list.add("Fun, Happy", :parse => true)
18
39
  def add(*names)
19
40
  extract_and_apply_options!(names)
@@ -21,75 +42,54 @@ class TagList < Array
21
42
  clean!
22
43
  self
23
44
  end
24
-
45
+
46
+ ##
25
47
  # Remove specific tags from the tag_list.
26
- #
27
- # tag_list.remove("Sad", "Lonely")
48
+ # Use the <tt>:parse</tt> option to add an unparsed tag string.
28
49
  #
29
- # Like #add, the <tt>:parse</tt> option can be used to remove multiple tags in a string.
30
- #
50
+ # Example:
51
+ # tag_list.remove("Sad", "Lonely")
31
52
  # tag_list.remove("Sad, Lonely", :parse => true)
32
53
  def remove(*names)
33
54
  extract_and_apply_options!(names)
34
55
  delete_if { |name| names.include?(name) }
35
56
  self
36
57
  end
37
-
58
+
59
+ ##
38
60
  # Transform the tag_list into a tag string suitable for edting in a form.
39
61
  # The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
40
62
  #
63
+ # Example:
41
64
  # tag_list = TagList.new("Round", "Square,Cube")
42
65
  # tag_list.to_s # 'Round, "Square,Cube"'
43
66
  def to_s
44
- clean!
45
-
46
- map do |name|
67
+ tags = frozen? ? self.dup : self
68
+ tags.send(:clean!)
69
+
70
+ tags.map do |name|
47
71
  name.include?(delimiter) ? "\"#{name}\"" : name
48
72
  end.join(delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ")
49
73
  end
74
+
75
+ private
50
76
 
51
- private
52
77
  # Remove whitespace, duplicates, and blanks.
53
78
  def clean!
54
79
  reject!(&:blank?)
55
80
  map!(&:strip)
56
81
  uniq!
57
82
  end
58
-
83
+
59
84
  def extract_and_apply_options!(args)
60
85
  options = args.last.is_a?(Hash) ? args.pop : {}
61
86
  options.assert_valid_keys :parse
62
-
87
+
63
88
  if options[:parse]
64
89
  args.map! { |a| self.class.from(a) }
65
90
  end
66
-
91
+
67
92
  args.flatten!
68
93
  end
69
-
70
- class << self
71
- # Returns a new TagList using the given tag string.
72
- #
73
- # tag_list = TagList.from("One , Two, Three")
74
- # tag_list # ["One", "Two", "Three"]
75
- def from(string)
76
- string = string.join(", ") if string.respond_to?(:join)
77
94
 
78
- returning new do |tag_list|
79
- string = string.to_s.dup
80
-
81
- # Parse the quoted tags
82
- string.gsub!(/"(.*?)"\s*#{delimiter}?\s*/) { tag_list << $1; "" }
83
- string.gsub!(/'(.*?)'\s*#{delimiter}?\s*/) { tag_list << $1; "" }
84
-
85
- tag_list.add(string.split(delimiter))
86
- end
87
- end
88
-
89
- def from_owner(owner, *tags)
90
- returning from(*tags) do |taglist|
91
- taglist.owner = owner
92
- end
93
- end
94
- end
95
- end
95
+ end
@@ -1,8 +1,23 @@
1
1
  class Tagging < ActiveRecord::Base #:nodoc:
2
+ include ActsAsTaggableOn::ActiveRecord::Backports if ActiveRecord::VERSION::MAJOR < 3
3
+
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
13
+
2
14
  belongs_to :tag
3
15
  belongs_to :taggable, :polymorphic => true
4
- belongs_to :tagger, :polymorphic => true
5
-
16
+ belongs_to :tagger, :polymorphic => true
17
+
6
18
  validates_presence_of :context
7
19
  validates_presence_of :tag_id
20
+
21
+ validates_uniqueness_of :tag_id, :scope => [ :taggable_type, :taggable_id, :context, :tagger_id, :tagger_type ]
22
+
8
23
  end
@@ -1,11 +1,17 @@
1
1
  module TagsHelper
2
+
2
3
  # See the README for an example using tag_cloud.
3
4
  def tag_cloud(tags, classes)
5
+ tags = tags.all if tags.respond_to?(:all)
6
+
7
+ return [] if tags.empty?
8
+
4
9
  max_count = tags.sort_by(&:count).last.count.to_f
5
-
10
+
6
11
  tags.each do |tag|
7
12
  index = ((tag.count / max_count) * (classes.size - 1)).round
8
13
  yield tag, classes[index]
9
14
  end
10
15
  end
11
- end
16
+
17
+ end
@@ -0,0 +1,31 @@
1
+ require 'rails/generators/migration'
2
+
3
+ module ActsAsTaggableOn
4
+ class MigrationGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+
7
+ desc "Generates migration for Tag and Tagging models"
8
+
9
+ def self.orm
10
+ Rails::Generators.options[:rails][:orm]
11
+ end
12
+
13
+ def self.source_root
14
+ File.join(File.dirname(__FILE__), 'templates', orm)
15
+ end
16
+
17
+ def self.orm_has_migration?
18
+ [:active_record].include? orm
19
+ end
20
+
21
+ def self.next_migration_number(path)
22
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
23
+ end
24
+
25
+ def create_migration_file
26
+ if self.class.orm_has_migration?
27
+ migration_template 'migration.rb', 'db/migrate/acts_as_taggable_on_migration'
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ class ActsAsTaggableOnMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :tags do |t|
4
+ t.string :name
5
+ end
6
+
7
+ create_table :taggings do |t|
8
+ t.references :tag
9
+
10
+ # You should make sure that the column created is
11
+ # long enough to store the required class names.
12
+ t.references :taggable, :polymorphic => true
13
+ t.references :tagger, :polymorphic => true
14
+
15
+ t.string :context
16
+
17
+ t.datetime :created_at
18
+ end
19
+
20
+ add_index :taggings, :tag_id
21
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
22
+ end
23
+
24
+ def self.down
25
+ drop_table :taggings
26
+ drop_table :tags
27
+ end
28
+ end
data/rails/init.rb CHANGED
@@ -1,7 +1 @@
1
- require 'acts-as-taggable-on'
2
-
3
- ActiveRecord::Base.send :include, ActiveRecord::Acts::TaggableOn
4
- ActiveRecord::Base.send :include, ActiveRecord::Acts::Tagger
5
- ActionView::Base.send :include, TagsHelper if defined?(ActionView::Base)
6
-
7
- RAILS_DEFAULT_LOGGER.info "** acts_as_taggable_on: initialized properly."
1
+ require 'acts-as-taggable-on'
@@ -1,6 +1,10 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper'
2
2
 
3
3
  describe "Acts As Taggable On" do
4
+ before(:each) do
5
+ clean_database!
6
+ end
7
+
4
8
  it "should provide a class method 'taggable?' that is false for untaggable models" do
5
9
  UntaggableModel.should_not be_taggable
6
10
  end
@@ -18,32 +22,31 @@ describe "Acts As Taggable On" do
18
22
  it "should create a class attribute for tag types" do
19
23
  @taggable.class.should respond_to(:tag_types)
20
24
  end
21
-
25
+
22
26
  it "should create an instance attribute for tag types" do
23
27
  @taggable.should respond_to(:tag_types)
24
28
  end
29
+
30
+ it "should have all tag types" do
31
+ @taggable.tag_types.should == [:tags, :languages, :skills, :needs, :offerings]
32
+ end
25
33
 
26
34
  it "should generate an association for each tag type" do
27
35
  @taggable.should respond_to(:tags, :skills, :languages)
28
36
  end
29
37
 
30
- it "should generate a cached column checker for each tag type" do
31
- TaggableModel.should respond_to(:caching_tag_list?, :caching_skill_list?, :caching_language_list?)
32
- end
33
-
34
38
  it "should add tagged_with and tag_counts to singleton" do
35
- TaggableModel.should respond_to(:find_tagged_with, :tag_counts)
36
- end
37
-
38
- it "should add saving of tag lists and cached tag lists to the instance" do
39
- @taggable.should respond_to(:save_cached_tag_list)
40
- @taggable.should respond_to(:save_tags)
39
+ TaggableModel.should respond_to(:tagged_with, :tag_counts)
41
40
  end
42
41
 
43
42
  it "should generate a tag_list accessor/setter for each tag type" do
44
43
  @taggable.should respond_to(:tag_list, :skill_list, :language_list)
45
44
  @taggable.should respond_to(:tag_list=, :skill_list=, :language_list=)
46
45
  end
46
+
47
+ it "should generate a tag_list accessor, that includes owned tags, for each tag type" do
48
+ @taggable.should respond_to(:all_tags_list, :all_skills_list, :all_languages_list)
49
+ end
47
50
  end
48
51
 
49
52
  describe "Single Table Inheritance" do
@@ -52,17 +55,17 @@ describe "Acts As Taggable On" do
52
55
  @inherited_same = InheritingTaggableModel.new(:name => "inherited same")
53
56
  @inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
54
57
  end
55
-
58
+
56
59
  it "should pass on tag contexts to STI-inherited models" do
57
60
  @inherited_same.should respond_to(:tag_list, :skill_list, :language_list)
58
61
  @inherited_different.should respond_to(:tag_list, :skill_list, :language_list)
59
62
  end
60
-
63
+
61
64
  it "should have tag contexts added in altered STI models" do
62
65
  @inherited_different.should respond_to(:part_list)
63
66
  end
64
67
  end
65
-
68
+
66
69
  describe "Reloading" do
67
70
  it "should save a model instantiated by Model.find" do
68
71
  taggable = TaggableModel.create!(:name => "Taggable")
@@ -76,48 +79,48 @@ describe "Acts As Taggable On" do
76
79
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
77
80
  taggable2 = TaggableModel.create!(:name => "Taggable 2")
78
81
  taggable3 = TaggableModel.create!(:name => "Taggable 3")
79
-
82
+
80
83
  taggable1.tag_list = "one, two"
81
84
  taggable1.save
82
-
85
+
83
86
  taggable2.tag_list = "three, four"
84
87
  taggable2.save
85
-
88
+
86
89
  taggable3.tag_list = "one, four"
87
90
  taggable3.save
88
-
91
+
89
92
  taggable1.find_related_tags.should include(taggable3)
90
93
  taggable1.find_related_tags.should_not include(taggable2)
91
94
  end
92
-
95
+
93
96
  it "should find other related objects based on tag names on context" do
94
97
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
95
98
  taggable2 = OtherTaggableModel.create!(:name => "Taggable 2")
96
99
  taggable3 = OtherTaggableModel.create!(:name => "Taggable 3")
97
-
100
+
98
101
  taggable1.tag_list = "one, two"
99
102
  taggable1.save
100
-
103
+
101
104
  taggable2.tag_list = "three, four"
102
105
  taggable2.save
103
-
106
+
104
107
  taggable3.tag_list = "one, four"
105
108
  taggable3.save
106
-
109
+
107
110
  taggable1.find_related_tags_for(OtherTaggableModel).should include(taggable3)
108
111
  taggable1.find_related_tags_for(OtherTaggableModel).should_not include(taggable2)
109
112
  end
110
-
113
+
111
114
  it "should not include the object itself in the list of related objects" do
112
115
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
113
116
  taggable2 = TaggableModel.create!(:name => "Taggable 2")
114
-
117
+
115
118
  taggable1.tag_list = "one"
116
119
  taggable1.save
117
-
120
+
118
121
  taggable2.tag_list = "one, two"
119
122
  taggable2.save
120
-
123
+
121
124
  taggable1.find_related_tags.should include(taggable2)
122
125
  taggable1.find_related_tags.should_not include(taggable1)
123
126
  end
@@ -128,64 +131,54 @@ describe "Acts As Taggable On" do
128
131
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
129
132
  taggable2 = TaggableModel.create!(:name => "Taggable 2")
130
133
  taggable3 = TaggableModel.create!(:name => "Taggable 3")
131
-
134
+
132
135
  taggable1.offering_list = "one, two"
133
136
  taggable1.save!
134
-
137
+
135
138
  taggable2.need_list = "one, two"
136
139
  taggable2.save!
137
-
140
+
138
141
  taggable3.offering_list = "one, two"
139
142
  taggable3.save!
140
-
143
+
141
144
  taggable1.find_matching_contexts(:offerings, :needs).should include(taggable2)
142
145
  taggable1.find_matching_contexts(:offerings, :needs).should_not include(taggable3)
143
146
  end
144
-
147
+
145
148
  it "should find other related objects with tags of matching contexts" do
146
149
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
147
150
  taggable2 = OtherTaggableModel.create!(:name => "Taggable 2")
148
151
  taggable3 = OtherTaggableModel.create!(:name => "Taggable 3")
149
-
152
+
150
153
  taggable1.offering_list = "one, two"
151
154
  taggable1.save
152
-
155
+
153
156
  taggable2.need_list = "one, two"
154
157
  taggable2.save
155
-
158
+
156
159
  taggable3.offering_list = "one, two"
157
160
  taggable3.save
158
-
161
+
159
162
  taggable1.find_matching_contexts_for(OtherTaggableModel, :offerings, :needs).should include(taggable2)
160
163
  taggable1.find_matching_contexts_for(OtherTaggableModel, :offerings, :needs).should_not include(taggable3)
161
164
  end
162
-
165
+
163
166
  it "should not include the object itself in the list of related objects" do
164
167
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
165
168
  taggable2 = TaggableModel.create!(:name => "Taggable 2")
166
-
169
+
167
170
  taggable1.tag_list = "one"
168
171
  taggable1.save
169
-
172
+
170
173
  taggable2.tag_list = "one, two"
171
174
  taggable2.save
172
-
175
+
173
176
  taggable1.find_related_tags.should include(taggable2)
174
177
  taggable1.find_related_tags.should_not include(taggable1)
175
178
  end
176
179
  end
177
180
 
178
181
  describe 'Tagging Contexts' do
179
- before(:all) do
180
- class Array
181
- def freq
182
- k=Hash.new(0)
183
- self.each {|e| k[e]+=1}
184
- k
185
- end
186
- end
187
- end
188
-
189
182
  it 'should eliminate duplicate tagging contexts ' do
190
183
  TaggableModel.acts_as_taggable_on(:skills, :skills)
191
184
  TaggableModel.tag_types.freq[:skills].should_not == 3
@@ -212,10 +205,62 @@ describe "Acts As Taggable On" do
212
205
  TaggableModel.acts_as_taggable_on([nil])
213
206
  }.should_not raise_error
214
207
  end
208
+ end
209
+
210
+ describe 'Caching' do
211
+ before(:each) do
212
+ @taggable = CachedModel.new(:name => "Bob Jones")
213
+ end
214
+
215
+ it "should add saving of tag lists and cached tag lists to the instance" do
216
+ @taggable.should respond_to(:save_cached_tag_list)
217
+ @taggable.should respond_to(:save_tags)
218
+ end
215
219
 
216
- after(:all) do
217
- class Array; remove_method :freq; end
220
+ it "should generate a cached column checker for each tag type" do
221
+ CachedModel.should respond_to(:caching_tag_list?)
222
+ end
223
+
224
+ it 'should not have cached tags' do
225
+ @taggable.cached_tag_list.should be_blank
226
+ end
227
+
228
+ it 'should cache tags' do
229
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
230
+ @taggable.cached_tag_list.should == 'awesome, epic'
231
+ end
232
+
233
+ it 'should keep the cache' do
234
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
235
+ @taggable = CachedModel.find(@taggable)
236
+ @taggable.save!
237
+ @taggable.cached_tag_list.should == 'awesome, epic'
238
+ end
239
+
240
+ it 'should update the cache' do
241
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
242
+ @taggable.update_attributes(:tag_list => 'awesome')
243
+ @taggable.cached_tag_list.should == 'awesome'
244
+ end
245
+
246
+ it 'should remove the cache' do
247
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
248
+ @taggable.update_attributes(:tag_list => '')
249
+ @taggable.cached_tag_list.should be_blank
250
+ end
251
+
252
+ it 'should have a tag list' do
253
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
254
+ @taggable = CachedModel.find(@taggable.id)
255
+ @taggable.tag_list.sort.should == %w(awesome epic).sort
256
+ end
257
+
258
+ it 'should keep the tag list' do
259
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
260
+ @taggable = CachedModel.find(@taggable.id)
261
+ @taggable.save!
262
+ @taggable.tag_list.sort.should == %w(awesome epic).sort
218
263
  end
219
264
  end
220
265
 
221
- end
266
+ end
@@ -1,8 +1,11 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper'
2
2
 
3
3
  describe "acts_as_tagger" do
4
+ before(:each) do
5
+ clean_database!
6
+ end
7
+
4
8
  context "Tagger Method Generation" do
5
-
6
9
  before(:each) do
7
10
  @tagger = TaggableUser.new()
8
11
  end
@@ -48,8 +51,23 @@ describe "acts_as_tagger" do
48
51
 
49
52
  it 'should by default create the tag context on-the-fly' do
50
53
  @taggable.tag_list_on(:here_ond_now).should be_empty
51
- @tagger.tag(@taggable, :with=>'that', :on=>:here_ond_now)
52
- @taggable.tag_list_on(:here_ond_now).should include('that')
54
+ @tagger.tag(@taggable, :with=>'that', :on => :here_ond_now)
55
+ @taggable.tag_list_on(:here_ond_now).should_not include('that')
56
+ @taggable.all_tags_list_on(:here_ond_now).should include('that')
57
+ end
58
+
59
+ it "should show all the tag list when both public and owned tags exist" do
60
+ @taggable.tag_list = 'ruby, python'
61
+ @tagger.tag(@taggable, :with => 'java, lisp', :on => :tags)
62
+ @taggable.all_tags_on(:tags).map(&:name).sort.should == %w(ruby python java lisp).sort
63
+ end
64
+
65
+ it "should not add owned tags to the common list" do
66
+ @taggable.tag_list = 'ruby, python'
67
+ @tagger.tag(@taggable, :with => 'java, lisp', :on => :tags)
68
+ @taggable.tag_list.should == %w(ruby python)
69
+ @tagger.tag(@taggable, :with => '', :on => :tags)
70
+ @taggable.tag_list.should == %w(ruby python)
53
71
  end
54
72
 
55
73
  it "should throw an exception when the default is over-ridden" do
@@ -64,9 +82,33 @@ describe "acts_as_tagger" do
64
82
  @tagger.tag(@taggable, :with=>'this, and, that', :on=>:foo_boo, :force=>false) rescue
65
83
  @taggable.tag_list_on(:foo_boo).should be_empty
66
84
  end
85
+ end
86
+
87
+ context "when called by multiple tagger's" do
88
+ before(:each) do
89
+ @user_x = TaggableUser.create(:name => "User X")
90
+ @user_y = TaggableUser.create(:name => "User Y")
91
+ @taggable = TaggableModel.create(:name => 'acts_as_taggable_on', :tag_list => 'plugin')
92
+
93
+ @user_x.tag(@taggable, :with => 'ruby, rails', :on => :tags)
94
+ @user_y.tag(@taggable, :with => 'ruby, plugin', :on => :tags)
67
95
 
96
+ @user_y.tag(@taggable, :with => '', :on => :tags)
97
+ @user_y.tag(@taggable, :with => '', :on => :tags)
98
+ end
99
+
100
+ it "should delete owned tags" do
101
+ @user_y.owned_tags.should == []
102
+ end
103
+
104
+ it "should not delete other taggers tags" do
105
+ @user_x.owned_tags.should have(2).items
106
+ end
107
+
108
+ it "should not delete original tags" do
109
+ @taggable.all_tags_list_on(:tags).should include('plugin')
110
+ end
68
111
  end
69
-
70
112
  end
71
113
 
72
114
  end
@@ -20,6 +20,18 @@ describe TagList do
20
20
  @tag_list.include?("wicked").should be_true
21
21
  end
22
22
 
23
+ it "should be able to add delimited list of words with quoted delimiters" do
24
+ @tag_list.add("'cool, wicked', \"really cool, really wicked\"", :parse => true)
25
+ @tag_list.include?("cool, wicked").should be_true
26
+ @tag_list.include?("really cool, really wicked").should be_true
27
+ end
28
+
29
+ it "should be able to handle other uses of quotation marks correctly" do
30
+ @tag_list.add("john's cool car, mary's wicked toy", :parse => true)
31
+ @tag_list.include?("john's cool car").should be_true
32
+ @tag_list.include?("mary's wicked toy").should be_true
33
+ end
34
+
23
35
  it "should be able to add an array of words" do
24
36
  @tag_list.add(["cool", "wicked"], :parse => true)
25
37
  @tag_list.include?("cool").should be_true
@@ -49,4 +61,10 @@ describe TagList do
49
61
  @tag_list.add("cool","rad,bodacious")
50
62
  @tag_list.to_s.should == "awesome, radical, cool, \"rad,bodacious\""
51
63
  end
64
+
65
+ it "should be able to call to_s on a frozen tag list" do
66
+ @tag_list.freeze
67
+ lambda { @tag_list.add("cool","rad,bodacious") }.should raise_error
68
+ lambda { @tag_list.to_s }.should_not raise_error
69
+ end
52
70
  end