acts-as-taggable-on 2.0.6 → 2.3.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 (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