acts-as-taggable-on 2.0.6 → 2.4.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 (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +9 -0
  5. data/CHANGELOG.md +35 -0
  6. data/Gemfile +2 -9
  7. data/Guardfile +5 -0
  8. data/{MIT-LICENSE → MIT-LICENSE.md} +1 -1
  9. data/README.md +297 -0
  10. data/Rakefile +9 -55
  11. data/UPGRADING +14 -0
  12. data/acts-as-taggable-on.gemspec +32 -0
  13. data/lib/acts-as-taggable-on/version.rb +4 -0
  14. data/lib/acts-as-taggable-on.rb +37 -4
  15. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +6 -6
  16. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +99 -45
  17. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +162 -45
  18. data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +37 -0
  19. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +40 -15
  20. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +28 -18
  21. data/lib/acts_as_taggable_on/tag.rb +41 -16
  22. data/lib/acts_as_taggable_on/tag_list.rb +19 -14
  23. data/lib/acts_as_taggable_on/taggable.rb +102 -0
  24. data/lib/acts_as_taggable_on/{acts_as_tagger.rb → tagger.rb} +3 -3
  25. data/lib/acts_as_taggable_on/tagging.rb +12 -2
  26. data/lib/acts_as_taggable_on/tags_helper.rb +2 -2
  27. data/lib/acts_as_taggable_on/utils.rb +34 -0
  28. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +9 -2
  29. data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +3 -1
  30. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +333 -54
  31. data/spec/acts_as_taggable_on/tag_list_spec.rb +117 -61
  32. data/spec/acts_as_taggable_on/tag_spec.rb +111 -14
  33. data/spec/acts_as_taggable_on/taggable_spec.rb +330 -34
  34. data/spec/acts_as_taggable_on/tagger_spec.rb +62 -15
  35. data/spec/acts_as_taggable_on/tagging_spec.rb +2 -5
  36. data/spec/acts_as_taggable_on/tags_helper_spec.rb +16 -0
  37. data/spec/acts_as_taggable_on/utils_spec.rb +21 -0
  38. data/spec/database.yml.sample +4 -2
  39. data/spec/generators/acts_as_taggable_on/migration/migration_generator_spec.rb +22 -0
  40. data/spec/models.rb +28 -1
  41. data/spec/schema.rb +18 -0
  42. data/spec/spec_helper.rb +30 -7
  43. data/uninstall.rb +1 -0
  44. metadata +174 -57
  45. data/CHANGELOG +0 -25
  46. data/README.rdoc +0 -221
  47. data/VERSION +0 -1
  48. data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +0 -7
  49. data/generators/acts_as_taggable_on_migration/templates/migration.rb +0 -29
  50. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +0 -53
  51. data/lib/acts_as_taggable_on/compatibility/Gemfile +0 -8
  52. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +0 -17
  53. data/lib/acts_as_taggable_on/compatibility/postgresql.rb +0 -44
  54. data/spec/database.yml +0 -17
@@ -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,29 @@ describe "Taggable" do
24
134
  @taggable.tag_counts_on(:tags).length.should == 2
25
135
  end
26
136
 
137
+ it "should have tags_on" do
138
+ TaggableModel.tags_on(:tags).all.should be_empty
139
+
140
+ @taggable.tag_list = ["awesome", "epic"]
141
+ @taggable.save
142
+
143
+ TaggableModel.tags_on(:tags).length.should == 2
144
+ @taggable.tags_on(:tags).length.should == 2
145
+ end
146
+
147
+ it "should return [] right after create" do
148
+ blank_taggable = TaggableModel.new(:name => "Bob Jones")
149
+ blank_taggable.tag_list.should == []
150
+ end
151
+
27
152
  it "should be able to create tags" do
28
153
  @taggable.skill_list = "ruby, rails, css"
29
154
  @taggable.instance_variable_get("@skill_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
30
-
155
+
31
156
  lambda {
32
157
  @taggable.save
33
158
  }.should change(ActsAsTaggableOn::Tag, :count).by(3)
34
-
159
+
35
160
  @taggable.reload
36
161
  @taggable.skill_list.sort.should == %w(ruby rails css).sort
37
162
  end
@@ -40,10 +165,10 @@ describe "Taggable" do
40
165
  @taggable.tag_list_on(:test).add("hello")
41
166
  @taggable.tag_list_cache_on(:test).should_not be_empty
42
167
  @taggable.tag_list_on(:test).should == ["hello"]
43
-
168
+
44
169
  @taggable.save
45
170
  @taggable.save_tags
46
-
171
+
47
172
  @taggable.reload
48
173
  @taggable.tag_list_on(:test).should == ["hello"]
49
174
  end
@@ -68,6 +193,22 @@ describe "Taggable" do
68
193
  @taggable.should have(2).skills
69
194
  end
70
195
 
196
+ it "should be able to select taggables by subset of tags using ActiveRelation methods" do
197
+ @taggables[0].tag_list = "bob"
198
+ @taggables[1].tag_list = "charlie"
199
+ @taggables[0].skill_list = "ruby"
200
+ @taggables[1].skill_list = "css"
201
+ @taggables.each{|taggable| taggable.save}
202
+
203
+ @found_taggables_by_tag = TaggableModel.joins(:tags).where(:tags => {:name => ["bob"]})
204
+ @found_taggables_by_skill = TaggableModel.joins(:skills).where(:tags => {:name => ["ruby"]})
205
+
206
+ @found_taggables_by_tag.should include @taggables[0]
207
+ @found_taggables_by_tag.should_not include @taggables[1]
208
+ @found_taggables_by_skill.should include @taggables[0]
209
+ @found_taggables_by_skill.should_not include @taggables[1]
210
+ end
211
+
71
212
  it "should be able to find by tag" do
72
213
  @taggable.skill_list = "ruby, rails, css"
73
214
  @taggable.save
@@ -111,20 +252,27 @@ describe "Taggable" do
111
252
  TaggableModel.all_tag_counts(:order => 'tags.id').first.count.should == 3 # ruby
112
253
  end
113
254
 
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
255
+ it "should be able to get all tags on model as whole" do
256
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
257
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
258
+ charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
259
+
260
+ TaggableModel.all_tags.all.should_not be_empty
261
+ TaggableModel.all_tags(:order => 'tags.id').first.name.should == "ruby"
262
+ end
263
+
264
+ it "should be able to use named scopes to chain tag finds by any tags by context" do
265
+ bob = TaggableModel.create(:name => "Bob", :need_list => "rails", :offering_list => "c++")
266
+ frank = TaggableModel.create(:name => "Frank", :need_list => "css", :offering_list => "css")
267
+ steve = TaggableModel.create(:name => 'Steve', :need_list => "c++", :offering_list => "java")
268
+
269
+ # Let's only find those who need rails or css and are offering c++ or java
270
+ TaggableModel.tagged_with(['rails, css'], :on => :needs, :any => true).tagged_with(['c++', 'java'], :on => :offerings, :any => true).to_a.should == [bob]
271
+ end
272
+
273
+ it "should not return read-only records" do
274
+ TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
275
+ TaggableModel.tagged_with("ruby").first.should_not be_readonly
128
276
  end
129
277
 
130
278
  it "should be able to get scoped tag counts" do
@@ -144,14 +292,22 @@ describe "Taggable" do
144
292
  TaggableModel.tagged_with("ruby").all_tag_counts(:order => 'tags.id').first.count.should == 3 # ruby
145
293
  end
146
294
 
295
+ it "should be able to get all scoped tags" do
296
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
297
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
298
+ charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
299
+
300
+ TaggableModel.tagged_with("ruby").all_tags(:order => 'tags.id').first.name.should == "ruby"
301
+ end
302
+
147
303
  it 'should only return tag counts for the available scope' do
148
304
  bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
149
305
  frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
150
306
  charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby, java")
151
-
307
+
152
308
  TaggableModel.tagged_with('rails').all_tag_counts.should have(3).items
153
309
  TaggableModel.tagged_with('rails').all_tag_counts.any? { |tag| tag.name == 'java' }.should be_false
154
-
310
+
155
311
  # Test specific join syntaxes:
156
312
  frank.untaggable_models.create!
157
313
  TaggableModel.tagged_with('rails').scoped(:joins => :untaggable_models).all_tag_counts.should have(2).items
@@ -159,6 +315,21 @@ describe "Taggable" do
159
315
  TaggableModel.tagged_with('rails').scoped(:joins => [:untaggable_models]).all_tag_counts.should have(2).items
160
316
  end
161
317
 
318
+ it 'should only return tags for the available scope' do
319
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
320
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
321
+ charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby, java")
322
+
323
+ TaggableModel.tagged_with('rails').all_tags.should have(3).items
324
+ TaggableModel.tagged_with('rails').all_tags.any? { |tag| tag.name == 'java' }.should be_false
325
+
326
+ # Test specific join syntaxes:
327
+ frank.untaggable_models.create!
328
+ TaggableModel.tagged_with('rails').scoped(:joins => :untaggable_models).all_tags.should have(2).items
329
+ TaggableModel.tagged_with('rails').scoped(:joins => { :untaggable_models => :taggable_model }).all_tags.should have(2).items
330
+ TaggableModel.tagged_with('rails').scoped(:joins => [:untaggable_models]).all_tags.should have(2).items
331
+ end
332
+
162
333
  it "should be able to set a custom tag context list" do
163
334
  bob = TaggableModel.create(:name => "Bob")
164
335
  bob.set_tag_list_on(:rotors, "spinning, jumping")
@@ -177,15 +348,15 @@ describe "Taggable" do
177
348
  TaggableModel.tagged_with("ruby, rails", :order => 'taggable_models.name').to_a.should == [bob, frank]
178
349
  TaggableModel.tagged_with(["ruby", "rails"], :order => 'taggable_models.name').to_a.should == [bob, frank]
179
350
  end
180
-
351
+
181
352
  it "should be able to find tagged with quotation marks" do
182
353
  bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive, 'I love the ,comma,'")
183
354
  TaggableModel.tagged_with("'I love the ,comma,'").should include(bob)
184
355
  end
185
-
356
+
186
357
  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)
358
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive")
359
+ TaggableModel.tagged_with("sad, happier").should_not include(bob)
189
360
  end
190
361
 
191
362
  it "should be able to find tagged with any tag" do
@@ -198,6 +369,19 @@ describe "Taggable" do
198
369
  TaggableModel.tagged_with(["depressed", "css"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, frank]
199
370
  end
200
371
 
372
+ context "wild: true" do
373
+ it "should use params as wildcards" do
374
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "bob, tricia")
375
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "bobby, jim")
376
+ steve = TaggableModel.create(:name => "Steve", :tag_list => "john, patricia")
377
+ jim = TaggableModel.create(:name => "Jim", :tag_list => "jim, steve")
378
+
379
+
380
+ TaggableModel.tagged_with(["bob", "tricia"], :wild => true, :any => true).to_a.sort_by{|o| o.id}.should == [bob, frank, steve]
381
+ TaggableModel.tagged_with(["bob", "tricia"], :wild => true, :exclude => true).to_a.should == [jim]
382
+ end
383
+ end
384
+
201
385
  it "should be able to find tagged on a custom tag context" do
202
386
  bob = TaggableModel.create(:name => "Bob")
203
387
  bob.set_tag_list_on(:rotors, "spinning, jumping")
@@ -235,6 +419,13 @@ describe "Taggable" do
235
419
  TaggableModel.tagged_with("lazy", :exclude => true).to_a.should == [frank, steve]
236
420
  end
237
421
 
422
+ it "should return an empty scope for empty tags" do
423
+ TaggableModel.tagged_with('').should == []
424
+ TaggableModel.tagged_with(' ').should == []
425
+ TaggableModel.tagged_with(nil).should == []
426
+ TaggableModel.tagged_with([]).should == []
427
+ end
428
+
238
429
  it "should not create duplicate taggings" do
239
430
  bob = TaggableModel.create(:name => "Bob")
240
431
  lambda {
@@ -243,12 +434,12 @@ describe "Taggable" do
243
434
  bob.save
244
435
  }.should change(ActsAsTaggableOn::Tagging, :count).by(1)
245
436
  end
246
-
437
+
247
438
  describe "Associations" do
248
439
  before(:each) do
249
440
  @taggable = TaggableModel.create(:tag_list => "awesome, epic")
250
441
  end
251
-
442
+
252
443
  it "should not remove tags when creating associated objects" do
253
444
  @taggable.untaggable_models.create!
254
445
  @taggable.reload
@@ -264,6 +455,10 @@ describe "Taggable" do
264
455
  it "should return all column names joined for TaggableModel GROUP clause" do
265
456
  @taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.id, taggable_models.name, taggable_models.type"
266
457
  end
458
+
459
+ it "should return all column names joined for NonStandardIdTaggableModel GROUP clause" do
460
+ @taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.#{TaggableModel.primary_key}, taggable_models.name, taggable_models.type"
461
+ end
267
462
  end
268
463
 
269
464
  describe "Single Table Inheritance" do
@@ -272,45 +467,146 @@ describe "Taggable" do
272
467
  @inherited_same = InheritingTaggableModel.new(:name => "inherited same")
273
468
  @inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
274
469
  end
275
-
470
+
276
471
  it "should be able to save tags for inherited models" do
277
472
  @inherited_same.tag_list = "bob, kelso"
278
473
  @inherited_same.save
279
474
  InheritingTaggableModel.tagged_with("bob").first.should == @inherited_same
280
475
  end
281
-
476
+
282
477
  it "should find STI tagged models on the superclass" do
283
478
  @inherited_same.tag_list = "bob, kelso"
284
479
  @inherited_same.save
285
480
  TaggableModel.tagged_with("bob").first.should == @inherited_same
286
481
  end
287
-
482
+
288
483
  it "should be able to add on contexts only to some subclasses" do
289
484
  @inherited_different.part_list = "fork, spoon"
290
485
  @inherited_different.save
291
486
  InheritingTaggableModel.tagged_with("fork", :on => :parts).should be_empty
292
487
  AlteredInheritingTaggableModel.tagged_with("fork", :on => :parts).first.should == @inherited_different
293
488
  end
294
-
489
+
295
490
  it "should have different tag_counts_on for inherited models" do
296
491
  @inherited_same.tag_list = "bob, kelso"
297
492
  @inherited_same.save!
298
493
  @inherited_different.tag_list = "fork, spoon"
299
494
  @inherited_different.save!
300
-
495
+
301
496
  InheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso)
302
497
  AlteredInheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(fork spoon)
303
498
  TaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso fork spoon)
304
499
  end
305
-
500
+
501
+ it "should have different tags_on for inherited models" do
502
+ @inherited_same.tag_list = "bob, kelso"
503
+ @inherited_same.save!
504
+ @inherited_different.tag_list = "fork, spoon"
505
+ @inherited_different.save!
506
+
507
+ InheritingTaggableModel.tags_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso)
508
+ AlteredInheritingTaggableModel.tags_on(:tags, :order => 'tags.id').map(&:name).should == %w(fork spoon)
509
+ TaggableModel.tags_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso fork spoon)
510
+ end
511
+
306
512
  it 'should store same tag without validation conflict' do
307
513
  @taggable.tag_list = 'one'
308
514
  @taggable.save!
309
-
515
+
310
516
  @inherited_same.tag_list = 'one'
311
517
  @inherited_same.save!
312
-
518
+
313
519
  @inherited_same.update_attributes! :name => 'foo'
314
520
  end
315
521
  end
522
+
523
+ describe "NonStandardIdTaggable" do
524
+ before(:each) do
525
+ clean_database!
526
+ @taggable = NonStandardIdTaggableModel.new(:name => "Bob Jones")
527
+ @taggables = [@taggable, NonStandardIdTaggableModel.new(:name => "John Doe")]
528
+ end
529
+
530
+ it "should have tag types" do
531
+ [:tags, :languages, :skills, :needs, :offerings].each do |type|
532
+ NonStandardIdTaggableModel.tag_types.should include type
533
+ end
534
+
535
+ @taggable.tag_types.should == NonStandardIdTaggableModel.tag_types
536
+ end
537
+
538
+ it "should have tag_counts_on" do
539
+ NonStandardIdTaggableModel.tag_counts_on(:tags).all.should be_empty
540
+
541
+ @taggable.tag_list = ["awesome", "epic"]
542
+ @taggable.save
543
+
544
+ NonStandardIdTaggableModel.tag_counts_on(:tags).length.should == 2
545
+ @taggable.tag_counts_on(:tags).length.should == 2
546
+ end
547
+
548
+ it "should have tags_on" do
549
+ NonStandardIdTaggableModel.tags_on(:tags).all.should be_empty
550
+
551
+ @taggable.tag_list = ["awesome", "epic"]
552
+ @taggable.save
553
+
554
+ NonStandardIdTaggableModel.tags_on(:tags).length.should == 2
555
+ @taggable.tags_on(:tags).length.should == 2
556
+ end
557
+
558
+ it "should be able to create tags" do
559
+ @taggable.skill_list = "ruby, rails, css"
560
+ @taggable.instance_variable_get("@skill_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
561
+
562
+ lambda {
563
+ @taggable.save
564
+ }.should change(ActsAsTaggableOn::Tag, :count).by(3)
565
+
566
+ @taggable.reload
567
+ @taggable.skill_list.sort.should == %w(ruby rails css).sort
568
+ end
569
+
570
+ it "should be able to create tags through the tag list directly" do
571
+ @taggable.tag_list_on(:test).add("hello")
572
+ @taggable.tag_list_cache_on(:test).should_not be_empty
573
+ @taggable.tag_list_on(:test).should == ["hello"]
574
+
575
+ @taggable.save
576
+ @taggable.save_tags
577
+
578
+ @taggable.reload
579
+ @taggable.tag_list_on(:test).should == ["hello"]
580
+ end
581
+ end
582
+
583
+ describe "Dirty Objects" do
584
+ before(:each) do
585
+ @taggable = TaggableModel.create(:tag_list => "awesome, epic")
586
+ end
587
+
588
+ it 'should show changes of dirty object' do
589
+ @taggable.changes.should == {}
590
+ @taggable.tag_list = 'one'
591
+ @taggable.changes.should == {"tag_list"=>["awesome, epic", ["one"]]}
592
+
593
+ @taggable.tag_list_changed?.should be_true
594
+ @taggable.tag_list_was.should == "awesome, epic"
595
+ @taggable.tag_list_change.should == ["awesome, epic", ["one"]]
596
+ end
597
+
598
+ it 'should show no changes if the same tag_list' do
599
+ @taggable.tag_list = "awesome, epic"
600
+ @taggable.tag_list_changed?.should be_false
601
+ @taggable.changes.should == {}
602
+ end
603
+ end
604
+
605
+ describe "Autogenerated methods" do
606
+ it "should be overridable" do
607
+ TaggableModel.create(:tag_list=>'woo').tag_list_submethod_called.should be_true
608
+ end
609
+ end
316
610
  end
611
+
612
+
@@ -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,61 @@ 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
+ end
111
+
112
+ describe "Single Table Inheritance" do
113
+ before do
114
+ @user3 = InheritingTaggableUser.create
115
+ end
116
+
117
+ it "should have taggings" do
118
+ @user3.tag(@taggable, :with=>'ruby,scheme', :on=>:tags)
119
+ @user3.owned_taggings.size == 2
120
+ end
121
+
122
+ it "should have tags" do
123
+ @user3.tag(@taggable, :with=>'ruby,scheme', :on=>:tags)
124
+ @user3.owned_tags.size == 2
125
+ end
126
+
127
+ it "should return tags for the inheriting tagger" do
128
+ @user3.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
129
+ @taggable.tags_from(@user3).sort.should == %w(ruby scheme).sort
130
+ end
131
+
132
+ it "should scope objects returned by tagged_with by owners" do
133
+ @user3.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
134
+ TaggableModel.tagged_with(%w(ruby scheme), :owned_by => @user3).count.should == 1
135
+ end
136
+ end
137
+
91
138
  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