acts-as-taggable-on 2.0.6 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +9 -0
  4. data/CHANGELOG +10 -0
  5. data/Gemfile +2 -9
  6. data/Guardfile +5 -0
  7. data/README.rdoc +89 -66
  8. data/Rakefile +9 -55
  9. data/acts-as-taggable-on.gemspec +28 -0
  10. data/lib/acts-as-taggable-on/version.rb +4 -0
  11. data/lib/acts-as-taggable-on.rb +33 -4
  12. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +4 -4
  13. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +38 -43
  14. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +146 -38
  15. data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +37 -0
  16. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +36 -11
  17. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +23 -15
  18. data/lib/acts_as_taggable_on/tag.rb +16 -13
  19. data/lib/acts_as_taggable_on/tag_list.rb +13 -12
  20. data/lib/acts_as_taggable_on/taggable.rb +102 -0
  21. data/lib/acts_as_taggable_on/{acts_as_tagger.rb → tagger.rb} +3 -3
  22. data/lib/acts_as_taggable_on/tagging.rb +12 -2
  23. data/lib/acts_as_taggable_on/tags_helper.rb +2 -2
  24. data/lib/acts_as_taggable_on/utils.rb +34 -0
  25. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +9 -2
  26. data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +3 -1
  27. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +300 -54
  28. data/spec/acts_as_taggable_on/tag_list_spec.rb +84 -61
  29. data/spec/acts_as_taggable_on/tag_spec.rb +51 -13
  30. data/spec/acts_as_taggable_on/taggable_spec.rb +261 -34
  31. data/spec/acts_as_taggable_on/tagger_spec.rb +36 -15
  32. data/spec/acts_as_taggable_on/tagging_spec.rb +2 -5
  33. data/spec/acts_as_taggable_on/tags_helper_spec.rb +16 -0
  34. data/spec/acts_as_taggable_on/utils_spec.rb +21 -0
  35. data/spec/database.yml.sample +4 -2
  36. data/spec/generators/acts_as_taggable_on/migration/migration_generator_spec.rb +22 -0
  37. data/spec/models.rb +19 -1
  38. data/spec/schema.rb +18 -0
  39. data/spec/spec_helper.rb +30 -7
  40. data/uninstall.rb +1 -0
  41. metadata +137 -51
  42. data/VERSION +0 -1
  43. data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +0 -7
  44. data/generators/acts_as_taggable_on_migration/templates/migration.rb +0 -29
  45. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +0 -53
  46. data/lib/acts_as_taggable_on/compatibility/Gemfile +0 -8
  47. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +0 -17
  48. data/lib/acts_as_taggable_on/compatibility/postgresql.rb +0 -44
  49. data/spec/database.yml +0 -17
@@ -1,3 +1,5 @@
1
+ #encoding: utf-8
2
+
1
3
  require File.expand_path('../../spec_helper', __FILE__)
2
4
 
3
5
  describe ActsAsTaggableOn::Tag do
@@ -9,12 +11,13 @@ describe ActsAsTaggableOn::Tag do
9
11
 
10
12
  describe "named like any" do
11
13
  before(:each) do
14
+ ActsAsTaggableOn::Tag.create(:name => "Awesome")
12
15
  ActsAsTaggableOn::Tag.create(:name => "awesome")
13
16
  ActsAsTaggableOn::Tag.create(:name => "epic")
14
17
  end
15
18
 
16
19
  it "should find both tags" do
17
- ActsAsTaggableOn::Tag.named_like_any(["awesome", "epic"]).should have(2).items
20
+ ActsAsTaggableOn::Tag.named_like_any(["awesome", "epic"]).should have(3).items
18
21
  end
19
22
  end
20
23
 
@@ -39,6 +42,23 @@ describe ActsAsTaggableOn::Tag do
39
42
  end
40
43
  end
41
44
 
45
+ unless ActsAsTaggableOn::Tag.using_sqlite?
46
+ describe "find or create by unicode name" do
47
+ before(:each) do
48
+ @tag.name = "привет"
49
+ @tag.save
50
+ end
51
+
52
+ it "should find by name" do
53
+ ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("привет").should == @tag
54
+ end
55
+
56
+ it "should find by name case insensitive" do
57
+ ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("ПРИВЕТ").should == @tag
58
+ end
59
+ end
60
+ end
61
+
42
62
  describe "find or create all by any name" do
43
63
  before(:each) do
44
64
  @tag.name = "awesome"
@@ -72,21 +92,23 @@ describe ActsAsTaggableOn::Tag do
72
92
 
73
93
  it "should require a name" do
74
94
  @tag.valid?
75
-
76
- if ActiveRecord::VERSION::MAJOR >= 3
77
- @tag.errors[:name].should == ["can't be blank"]
78
- else
79
- @tag.errors[:name].should == "can't be blank"
80
- end
95
+
96
+ @tag.errors[:name].should == ["can't be blank"]
81
97
 
82
98
  @tag.name = "something"
83
99
  @tag.valid?
84
-
85
- if ActiveRecord::VERSION::MAJOR >= 3
86
- @tag.errors[:name].should == []
87
- else
88
- @tag.errors[:name].should be_nil
89
- end
100
+
101
+ @tag.errors[:name].should == []
102
+ end
103
+
104
+ it "should limit the name length to 255 or less characters" do
105
+ @tag.name = "fgkgnkkgjymkypbuozmwwghblmzpqfsgjasflblywhgkwndnkzeifalfcpeaeqychjuuowlacmuidnnrkprgpcpybarbkrmziqihcrxirlokhnzfvmtzixgvhlxzncyywficpraxfnjptxxhkqmvicbcdcynkjvziefqzyndxkjmsjlvyvbwraklbalykyxoliqdlreeykuphdtmzfdwpphmrqvwvqffojkqhlzvinqajsxbszyvrqqyzusxranr"
106
+ @tag.valid?
107
+ @tag.errors[:name].should == ["is too long (maximum is 255 characters)"]
108
+
109
+ @tag.name = "fgkgnkkgjymkypbuozmwwghblmzpqfsgjasflblywhgkwndnkzeifalfcpeaeqychjuuowlacmuidnnrkprgpcpybarbkrmziqihcrxirlokhnzfvmtzixgvhlxzncyywficpraxfnjptxxhkqmvicbcdcynkjvziefqzyndxkjmsjlvyvbwraklbalykyxoliqdlreeykuphdtmzfdwpphmrqvwvqffojkqhlzvinqajsxbszyvrqqyzusxran"
110
+ @tag.valid?
111
+ @tag.errors[:name].should == []
90
112
  end
91
113
 
92
114
  it "should equal a tag with the same name" do
@@ -112,4 +134,20 @@ describe ActsAsTaggableOn::Tag do
112
134
  @another_tag = ActsAsTaggableOn::Tag.create!(:name => "coolip")
113
135
  ActsAsTaggableOn::Tag.named_like('cool').should include(@tag, @another_tag)
114
136
  end
137
+
138
+ describe "escape wildcard symbols in like requests" do
139
+ before(:each) do
140
+ @tag.name = "cool"
141
+ @tag.save
142
+ @another_tag = ActsAsTaggableOn::Tag.create!(:name => "coo%")
143
+ @another_tag2 = ActsAsTaggableOn::Tag.create!(:name => "coolish")
144
+ end
145
+
146
+ it "return escaped result when '%' char present in tag" do
147
+ ActsAsTaggableOn::Tag.named_like('coo%').should_not include(@tag)
148
+ ActsAsTaggableOn::Tag.named_like('coo%').should include(@another_tag)
149
+ end
150
+
151
+ end
152
+
115
153
  end
@@ -1,9 +1,119 @@
1
1
  require File.expand_path('../../spec_helper', __FILE__)
2
2
 
3
+ describe "Taggable To Preserve Order" do
4
+ before(:each) do
5
+ clean_database!
6
+ @taggable = OrderedTaggableModel.new(:name => "Bob Jones")
7
+ end
8
+
9
+ it "should have tag types" do
10
+ [:tags, :colours].each do |type|
11
+ OrderedTaggableModel.tag_types.should include type
12
+ end
13
+
14
+ @taggable.tag_types.should == OrderedTaggableModel.tag_types
15
+ end
16
+
17
+ it "should have tag associations" do
18
+ [:tags, :colours].each do |type|
19
+ @taggable.respond_to?(type).should be_true
20
+ @taggable.respond_to?("#{type.to_s.singularize}_taggings").should be_true
21
+ end
22
+ end
23
+
24
+ it "should have tag associations ordered by id" do
25
+ [:tags, :colours].each do |type|
26
+ OrderedTaggableModel.reflect_on_association(type).options[:order].should include('id')
27
+ OrderedTaggableModel.reflect_on_association("#{type.to_s.singularize}_taggings".to_sym).options[:order].should include('id')
28
+ end
29
+ end
30
+
31
+ it "should have tag methods" do
32
+ [:tags, :colours].each do |type|
33
+ @taggable.respond_to?("#{type.to_s.singularize}_list").should be_true
34
+ @taggable.respond_to?("#{type.to_s.singularize}_list=").should be_true
35
+ @taggable.respond_to?("all_#{type.to_s}_list").should be_true
36
+ end
37
+ end
38
+
39
+ it "should return tag list in the order the tags were created" do
40
+ # create
41
+ @taggable.tag_list = "rails, ruby, css"
42
+ @taggable.instance_variable_get("@tag_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
43
+
44
+ lambda {
45
+ @taggable.save
46
+ }.should change(ActsAsTaggableOn::Tag, :count).by(3)
47
+
48
+ @taggable.reload
49
+ @taggable.tag_list.should == %w(rails ruby css)
50
+
51
+ # update
52
+ @taggable.tag_list = "pow, ruby, rails"
53
+ @taggable.save
54
+
55
+ @taggable.reload
56
+ @taggable.tag_list.should == %w(pow ruby rails)
57
+
58
+ # update with no change
59
+ @taggable.tag_list = "pow, ruby, rails"
60
+ @taggable.save
61
+
62
+ @taggable.reload
63
+ @taggable.tag_list.should == %w(pow ruby rails)
64
+
65
+ # update to clear tags
66
+ @taggable.tag_list = ""
67
+ @taggable.save
68
+
69
+ @taggable.reload
70
+ @taggable.tag_list.should == []
71
+ end
72
+
73
+ it "should return tag objects in the order the tags were created" do
74
+ # create
75
+ @taggable.tag_list = "pow, ruby, rails"
76
+ @taggable.instance_variable_get("@tag_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
77
+
78
+ lambda {
79
+ @taggable.save
80
+ }.should change(ActsAsTaggableOn::Tag, :count).by(3)
81
+
82
+ @taggable.reload
83
+ @taggable.tags.map{|t| t.name}.should == %w(pow ruby rails)
84
+
85
+ # update
86
+ @taggable.tag_list = "rails, ruby, css, pow"
87
+ @taggable.save
88
+
89
+ @taggable.reload
90
+ @taggable.tags.map{|t| t.name}.should == %w(rails ruby css pow)
91
+ end
92
+
93
+ it "should return tag objects in tagging id order" do
94
+ # create
95
+ @taggable.tag_list = "pow, ruby, rails"
96
+ @taggable.save
97
+
98
+ @taggable.reload
99
+ ids = @taggable.tags.map{|t| t.taggings.first.id}
100
+ ids.should == ids.sort
101
+
102
+ # update
103
+ @taggable.tag_list = "rails, ruby, css, pow"
104
+ @taggable.save
105
+
106
+ @taggable.reload
107
+ ids = @taggable.tags.map{|t| t.taggings.first.id}
108
+ ids.should == ids.sort
109
+ end
110
+ end
111
+
3
112
  describe "Taggable" do
4
113
  before(:each) do
5
114
  clean_database!
6
115
  @taggable = TaggableModel.new(:name => "Bob Jones")
116
+ @taggables = [@taggable, TaggableModel.new(:name => "John Doe")]
7
117
  end
8
118
 
9
119
  it "should have tag types" do
@@ -24,14 +134,19 @@ describe "Taggable" do
24
134
  @taggable.tag_counts_on(:tags).length.should == 2
25
135
  end
26
136
 
137
+ it "should return [] right after create" do
138
+ blank_taggable = TaggableModel.new(:name => "Bob Jones")
139
+ blank_taggable.tag_list.should == []
140
+ end
141
+
27
142
  it "should be able to create tags" do
28
143
  @taggable.skill_list = "ruby, rails, css"
29
144
  @taggable.instance_variable_get("@skill_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
30
-
145
+
31
146
  lambda {
32
147
  @taggable.save
33
148
  }.should change(ActsAsTaggableOn::Tag, :count).by(3)
34
-
149
+
35
150
  @taggable.reload
36
151
  @taggable.skill_list.sort.should == %w(ruby rails css).sort
37
152
  end
@@ -40,10 +155,10 @@ describe "Taggable" do
40
155
  @taggable.tag_list_on(:test).add("hello")
41
156
  @taggable.tag_list_cache_on(:test).should_not be_empty
42
157
  @taggable.tag_list_on(:test).should == ["hello"]
43
-
158
+
44
159
  @taggable.save
45
160
  @taggable.save_tags
46
-
161
+
47
162
  @taggable.reload
48
163
  @taggable.tag_list_on(:test).should == ["hello"]
49
164
  end
@@ -68,6 +183,22 @@ describe "Taggable" do
68
183
  @taggable.should have(2).skills
69
184
  end
70
185
 
186
+ it "should be able to select taggables by subset of tags using ActiveRelation methods" do
187
+ @taggables[0].tag_list = "bob"
188
+ @taggables[1].tag_list = "charlie"
189
+ @taggables[0].skill_list = "ruby"
190
+ @taggables[1].skill_list = "css"
191
+ @taggables.each{|taggable| taggable.save}
192
+
193
+ @found_taggables_by_tag = TaggableModel.joins(:tags).where(:tags => {:name => ["bob"]})
194
+ @found_taggables_by_skill = TaggableModel.joins(:skills).where(:tags => {:name => ["ruby"]})
195
+
196
+ @found_taggables_by_tag.should include @taggables[0]
197
+ @found_taggables_by_tag.should_not include @taggables[1]
198
+ @found_taggables_by_skill.should include @taggables[0]
199
+ @found_taggables_by_skill.should_not include @taggables[1]
200
+ end
201
+
71
202
  it "should be able to find by tag" do
72
203
  @taggable.skill_list = "ruby, rails, css"
73
204
  @taggable.save
@@ -111,20 +242,18 @@ describe "Taggable" do
111
242
  TaggableModel.all_tag_counts(:order => 'tags.id').first.count.should == 3 # ruby
112
243
  end
113
244
 
114
- if ActiveRecord::VERSION::MAJOR >= 3
115
- it "should not return read-only records" do
116
- TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
117
- TaggableModel.tagged_with("ruby").first.should_not be_readonly
118
- end
119
- else
120
- xit "should not return read-only records" do
121
- # apparantly, there is no way to set readonly to false in a scope if joins are made
122
- end
123
-
124
- it "should be possible to return writable records" do
125
- TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
126
- TaggableModel.tagged_with("ruby").first(:readonly => false).should_not be_readonly
127
- end
245
+ it "should be able to use named scopes to chain tag finds by any tags by context" do
246
+ bob = TaggableModel.create(:name => "Bob", :need_list => "rails", :offering_list => "c++")
247
+ frank = TaggableModel.create(:name => "Frank", :need_list => "css", :offering_list => "css")
248
+ steve = TaggableModel.create(:name => 'Steve', :need_list => "c++", :offering_list => "java")
249
+
250
+ # Let's only find those who need rails or css and are offering c++ or java
251
+ TaggableModel.tagged_with(['rails, css'], :on => :needs, :any => true).tagged_with(['c++', 'java'], :on => :offerings, :any => true).to_a.should == [bob]
252
+ end
253
+
254
+ it "should not return read-only records" do
255
+ TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
256
+ TaggableModel.tagged_with("ruby").first.should_not be_readonly
128
257
  end
129
258
 
130
259
  it "should be able to get scoped tag counts" do
@@ -148,10 +277,10 @@ describe "Taggable" do
148
277
  bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
149
278
  frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
150
279
  charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby, java")
151
-
280
+
152
281
  TaggableModel.tagged_with('rails').all_tag_counts.should have(3).items
153
282
  TaggableModel.tagged_with('rails').all_tag_counts.any? { |tag| tag.name == 'java' }.should be_false
154
-
283
+
155
284
  # Test specific join syntaxes:
156
285
  frank.untaggable_models.create!
157
286
  TaggableModel.tagged_with('rails').scoped(:joins => :untaggable_models).all_tag_counts.should have(2).items
@@ -177,15 +306,15 @@ describe "Taggable" do
177
306
  TaggableModel.tagged_with("ruby, rails", :order => 'taggable_models.name').to_a.should == [bob, frank]
178
307
  TaggableModel.tagged_with(["ruby", "rails"], :order => 'taggable_models.name').to_a.should == [bob, frank]
179
308
  end
180
-
309
+
181
310
  it "should be able to find tagged with quotation marks" do
182
311
  bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive, 'I love the ,comma,'")
183
312
  TaggableModel.tagged_with("'I love the ,comma,'").should include(bob)
184
313
  end
185
-
314
+
186
315
  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)
316
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive")
317
+ TaggableModel.tagged_with("sad, happier").should_not include(bob)
189
318
  end
190
319
 
191
320
  it "should be able to find tagged with any tag" do
@@ -198,6 +327,19 @@ describe "Taggable" do
198
327
  TaggableModel.tagged_with(["depressed", "css"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, frank]
199
328
  end
200
329
 
330
+ context "wild: true" do
331
+ it "should use params as wildcards" do
332
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "bob, tricia")
333
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "bobby, jim")
334
+ steve = TaggableModel.create(:name => "Steve", :tag_list => "john, patricia")
335
+ jim = TaggableModel.create(:name => "Jim", :tag_list => "jim, steve")
336
+
337
+
338
+ TaggableModel.tagged_with(["bob", "tricia"], :wild => true, :any => true).to_a.sort_by{|o| o.id}.should == [bob, frank, steve]
339
+ TaggableModel.tagged_with(["bob", "tricia"], :wild => true, :exclude => true).to_a.should == [jim]
340
+ end
341
+ end
342
+
201
343
  it "should be able to find tagged on a custom tag context" do
202
344
  bob = TaggableModel.create(:name => "Bob")
203
345
  bob.set_tag_list_on(:rotors, "spinning, jumping")
@@ -235,6 +377,13 @@ describe "Taggable" do
235
377
  TaggableModel.tagged_with("lazy", :exclude => true).to_a.should == [frank, steve]
236
378
  end
237
379
 
380
+ it "should return an empty scope for empty tags" do
381
+ TaggableModel.tagged_with('').should == []
382
+ TaggableModel.tagged_with(' ').should == []
383
+ TaggableModel.tagged_with(nil).should == []
384
+ TaggableModel.tagged_with([]).should == []
385
+ end
386
+
238
387
  it "should not create duplicate taggings" do
239
388
  bob = TaggableModel.create(:name => "Bob")
240
389
  lambda {
@@ -243,12 +392,12 @@ describe "Taggable" do
243
392
  bob.save
244
393
  }.should change(ActsAsTaggableOn::Tagging, :count).by(1)
245
394
  end
246
-
395
+
247
396
  describe "Associations" do
248
397
  before(:each) do
249
398
  @taggable = TaggableModel.create(:tag_list => "awesome, epic")
250
399
  end
251
-
400
+
252
401
  it "should not remove tags when creating associated objects" do
253
402
  @taggable.untaggable_models.create!
254
403
  @taggable.reload
@@ -264,6 +413,10 @@ describe "Taggable" do
264
413
  it "should return all column names joined for TaggableModel GROUP clause" do
265
414
  @taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.id, taggable_models.name, taggable_models.type"
266
415
  end
416
+
417
+ it "should return all column names joined for NonStandardIdTaggableModel GROUP clause" do
418
+ @taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.#{TaggableModel.primary_key}, taggable_models.name, taggable_models.type"
419
+ end
267
420
  end
268
421
 
269
422
  describe "Single Table Inheritance" do
@@ -272,45 +425,119 @@ describe "Taggable" do
272
425
  @inherited_same = InheritingTaggableModel.new(:name => "inherited same")
273
426
  @inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
274
427
  end
275
-
428
+
276
429
  it "should be able to save tags for inherited models" do
277
430
  @inherited_same.tag_list = "bob, kelso"
278
431
  @inherited_same.save
279
432
  InheritingTaggableModel.tagged_with("bob").first.should == @inherited_same
280
433
  end
281
-
434
+
282
435
  it "should find STI tagged models on the superclass" do
283
436
  @inherited_same.tag_list = "bob, kelso"
284
437
  @inherited_same.save
285
438
  TaggableModel.tagged_with("bob").first.should == @inherited_same
286
439
  end
287
-
440
+
288
441
  it "should be able to add on contexts only to some subclasses" do
289
442
  @inherited_different.part_list = "fork, spoon"
290
443
  @inherited_different.save
291
444
  InheritingTaggableModel.tagged_with("fork", :on => :parts).should be_empty
292
445
  AlteredInheritingTaggableModel.tagged_with("fork", :on => :parts).first.should == @inherited_different
293
446
  end
294
-
447
+
295
448
  it "should have different tag_counts_on for inherited models" do
296
449
  @inherited_same.tag_list = "bob, kelso"
297
450
  @inherited_same.save!
298
451
  @inherited_different.tag_list = "fork, spoon"
299
452
  @inherited_different.save!
300
-
453
+
301
454
  InheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso)
302
455
  AlteredInheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(fork spoon)
303
456
  TaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso fork spoon)
304
457
  end
305
-
458
+
306
459
  it 'should store same tag without validation conflict' do
307
460
  @taggable.tag_list = 'one'
308
461
  @taggable.save!
309
-
462
+
310
463
  @inherited_same.tag_list = 'one'
311
464
  @inherited_same.save!
312
-
465
+
313
466
  @inherited_same.update_attributes! :name => 'foo'
314
467
  end
315
468
  end
469
+
470
+ describe "NonStandardIdTaggable" do
471
+ before(:each) do
472
+ clean_database!
473
+ @taggable = NonStandardIdTaggableModel.new(:name => "Bob Jones")
474
+ @taggables = [@taggable, NonStandardIdTaggableModel.new(:name => "John Doe")]
475
+ end
476
+
477
+ it "should have tag types" do
478
+ [:tags, :languages, :skills, :needs, :offerings].each do |type|
479
+ NonStandardIdTaggableModel.tag_types.should include type
480
+ end
481
+
482
+ @taggable.tag_types.should == NonStandardIdTaggableModel.tag_types
483
+ end
484
+
485
+ it "should have tag_counts_on" do
486
+ NonStandardIdTaggableModel.tag_counts_on(:tags).all.should be_empty
487
+
488
+ @taggable.tag_list = ["awesome", "epic"]
489
+ @taggable.save
490
+
491
+ NonStandardIdTaggableModel.tag_counts_on(:tags).length.should == 2
492
+ @taggable.tag_counts_on(:tags).length.should == 2
493
+ end
494
+
495
+ it "should be able to create tags" do
496
+ @taggable.skill_list = "ruby, rails, css"
497
+ @taggable.instance_variable_get("@skill_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
498
+
499
+ lambda {
500
+ @taggable.save
501
+ }.should change(ActsAsTaggableOn::Tag, :count).by(3)
502
+
503
+ @taggable.reload
504
+ @taggable.skill_list.sort.should == %w(ruby rails css).sort
505
+ end
506
+
507
+ it "should be able to create tags through the tag list directly" do
508
+ @taggable.tag_list_on(:test).add("hello")
509
+ @taggable.tag_list_cache_on(:test).should_not be_empty
510
+ @taggable.tag_list_on(:test).should == ["hello"]
511
+
512
+ @taggable.save
513
+ @taggable.save_tags
514
+
515
+ @taggable.reload
516
+ @taggable.tag_list_on(:test).should == ["hello"]
517
+ end
518
+ end
519
+
520
+ describe "Dirty Objects" do
521
+ before(:each) do
522
+ @taggable = TaggableModel.create(:tag_list => "awesome, epic")
523
+ end
524
+
525
+ it 'should show changes of dirty object' do
526
+ @taggable.changes.should == {}
527
+ @taggable.tag_list = 'one'
528
+ @taggable.changes.should == {"tag_list"=>["awesome, epic", ["one"]]}
529
+
530
+ @taggable.tag_list_changed?.should be_true
531
+ @taggable.tag_list_was.should == "awesome, epic"
532
+ @taggable.tag_list_change.should == ["awesome, epic", ["one"]]
533
+ end
534
+
535
+ it 'should show no changes if the same tag_list' do
536
+ @taggable.tag_list = "awesome, epic"
537
+ @taggable.tag_list_changed?.should be_false
538
+ @taggable.changes.should == {}
539
+ end
540
+ end
316
541
  end
542
+
543
+
@@ -16,7 +16,21 @@ describe "Tagger" do
16
16
  @user.tag(@taggable, :with=>'ruby,scheme', :on=>:tags)
17
17
  @user.owned_tags.size == 2
18
18
  end
19
-
19
+
20
+ it "should scope objects returned by tagged_with by owners" do
21
+ @taggable2 = TaggableModel.create(:name => "Jim Jones")
22
+ @taggable3 = TaggableModel.create(:name => "Jane Doe")
23
+
24
+ @user2 = TaggableUser.new
25
+ @user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
26
+ @user2.tag(@taggable2, :with => 'ruby, scheme', :on => :tags)
27
+ @user2.tag(@taggable3, :with => 'ruby, scheme', :on => :tags)
28
+
29
+ TaggableModel.tagged_with(%w(ruby scheme), :owned_by => @user).count.should == 1
30
+ TaggableModel.tagged_with(%w(ruby scheme), :owned_by => @user2).count.should == 2
31
+
32
+ end
33
+
20
34
  it "should not overlap tags from different taggers" do
21
35
  @user2 = TaggableUser.new
22
36
  lambda{
@@ -28,35 +42,35 @@ describe "Tagger" do
28
42
 
29
43
  @user.owned_tags.map(&:name).sort.should == %w(ruby scheme).sort
30
44
  @user2.owned_tags.map(&:name).sort.should == %w(java python lisp ruby).sort
31
-
45
+
32
46
  @taggable.tags_from(@user).sort.should == %w(ruby scheme).sort
33
47
  @taggable.tags_from(@user2).sort.should == %w(java lisp python ruby).sort
34
-
48
+
35
49
  @taggable.all_tags_list.sort.should == %w(ruby scheme java python lisp).sort
36
50
  @taggable.all_tags_on(:tags).size.should == 5
37
51
  end
38
-
52
+
39
53
  it "should not lose tags from different taggers" do
40
54
  @user2 = TaggableUser.create
41
55
  @user2.tag(@taggable, :with => 'java, python, lisp, ruby', :on => :tags)
42
- @user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
43
-
56
+ @user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
57
+
44
58
  lambda {
45
59
  @user2.tag(@taggable, :with => 'java, python, lisp', :on => :tags)
46
60
  }.should change(ActsAsTaggableOn::Tagging, :count).by(-1)
47
61
 
48
62
  [@user, @user2, @taggable].each(&:reload)
49
-
63
+
50
64
  @taggable.tags_from(@user).sort.should == %w(ruby scheme).sort
51
65
  @taggable.tags_from(@user2).sort.should == %w(java python lisp).sort
52
-
66
+
53
67
  @taggable.all_tags_list.sort.should == %w(ruby scheme java python lisp).sort
54
68
  @taggable.all_tags_on(:tags).length.should == 5
55
69
  end
56
70
 
57
71
  it "should not lose tags" do
58
72
  @user2 = TaggableUser.create
59
-
73
+
60
74
  @user.tag(@taggable, :with => 'awesome', :on => :tags)
61
75
  @user2.tag(@taggable, :with => 'awesome, epic', :on => :tags)
62
76
 
@@ -64,28 +78,35 @@ describe "Tagger" do
64
78
  @user2.tag(@taggable, :with => 'epic', :on => :tags)
65
79
  }.should change(ActsAsTaggableOn::Tagging, :count).by(-1)
66
80
 
67
- @taggable.reload
81
+ @taggable.reload
68
82
  @taggable.all_tags_list.should include('awesome')
69
83
  @taggable.all_tags_list.should include('epic')
70
84
  end
71
-
85
+
72
86
  it "should not lose tags" do
73
87
  @taggable.update_attributes(:tag_list => 'ruby')
74
88
  @user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
75
-
89
+
76
90
  [@taggable, @user].each(&:reload)
77
91
  @taggable.tag_list.should == %w(ruby)
78
92
  @taggable.all_tags_list.sort.should == %w(ruby scheme).sort
79
-
93
+
80
94
  lambda {
81
95
  @taggable.update_attributes(:tag_list => "")
82
96
  }.should change(ActsAsTaggableOn::Tagging, :count).by(-1)
83
-
97
+
84
98
  @taggable.tag_list.should == []
85
99
  @taggable.all_tags_list.sort.should == %w(ruby scheme).sort
86
100
  end
87
101
 
88
102
  it "is tagger" do
89
103
  @user.is_tagger?.should(be_true)
90
- end
104
+ end
105
+
106
+ it "should skip save if skip_save is passed as option" do
107
+ lambda {
108
+ @user.tag(@taggable, :with => 'epic', :on => :tags, :skip_save => true)
109
+ }.should_not change(ActsAsTaggableOn::Tagging, :count)
110
+
111
+ end
91
112
  end
@@ -13,11 +13,7 @@ describe ActsAsTaggableOn::Tagging do
13
13
 
14
14
  @tagging.should_not be_valid
15
15
 
16
- if ActiveRecord::VERSION::MAJOR >= 3
17
- @tagging.errors[:tag_id].should == ["can't be blank"]
18
- else
19
- @tagging.errors[:tag_id].should == "can't be blank"
20
- end
16
+ @tagging.errors[:tag_id].should == ["can't be blank"]
21
17
  end
22
18
 
23
19
  it "should not create duplicate taggings" do
@@ -28,4 +24,5 @@ describe ActsAsTaggableOn::Tagging do
28
24
  2.times { ActsAsTaggableOn::Tagging.create(:taggable => @taggable, :tag => @tag, :context => 'tags') }
29
25
  }.should change(ActsAsTaggableOn::Tagging, :count).by(1)
30
26
  end
27
+
31
28
  end
@@ -25,4 +25,20 @@ describe ActsAsTaggableOn::TagsHelper do
25
25
  tags["c++"].should == "sucky"
26
26
  tags["php"].should == "sucky"
27
27
  end
28
+
29
+ it "should handle tags with zero counts (build for empty)" do
30
+ bob = ActsAsTaggableOn::Tag.create(:name => "php")
31
+ tom = ActsAsTaggableOn::Tag.create(:name => "java")
32
+ eve = ActsAsTaggableOn::Tag.create(:name => "c++")
33
+
34
+ tags = { }
35
+
36
+ @helper.tag_cloud(ActsAsTaggableOn::Tag.all, ["sucky", "awesome"]) do |tag, css_class|
37
+ tags[tag.name] = css_class
38
+ end
39
+
40
+ tags["java"].should == "sucky"
41
+ tags["c++"].should == "sucky"
42
+ tags["php"].should == "sucky"
43
+ end
28
44
  end