acts-as-taggable-on 2.0.6 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
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