acts-as-taggable-on 1.0.13 → 2.0.6

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 (43) hide show
  1. data/CHANGELOG +5 -2
  2. data/Gemfile +10 -0
  3. data/README.rdoc +56 -26
  4. data/Rakefile +46 -16
  5. data/VERSION +1 -1
  6. data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +7 -0
  7. data/generators/acts_as_taggable_on_migration/templates/migration.rb +29 -0
  8. data/lib/acts-as-taggable-on.rb +30 -7
  9. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +53 -0
  10. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +132 -0
  11. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +241 -0
  12. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +101 -0
  13. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +65 -0
  14. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +43 -373
  15. data/lib/acts_as_taggable_on/acts_as_tagger.rb +60 -45
  16. data/lib/acts_as_taggable_on/compatibility/Gemfile +8 -0
  17. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +17 -0
  18. data/lib/acts_as_taggable_on/compatibility/postgresql.rb +44 -0
  19. data/lib/acts_as_taggable_on/tag.rb +70 -22
  20. data/lib/acts_as_taggable_on/tag_list.rb +81 -80
  21. data/lib/acts_as_taggable_on/tagging.rb +23 -7
  22. data/lib/acts_as_taggable_on/tags_helper.rb +14 -8
  23. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +32 -0
  24. data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +28 -0
  25. data/rails/init.rb +1 -7
  26. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +102 -55
  27. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +48 -6
  28. data/spec/acts_as_taggable_on/tag_list_spec.rb +21 -3
  29. data/spec/acts_as_taggable_on/tag_spec.rb +77 -24
  30. data/spec/acts_as_taggable_on/taggable_spec.rb +173 -76
  31. data/spec/acts_as_taggable_on/tagger_spec.rb +74 -6
  32. data/spec/acts_as_taggable_on/tagging_spec.rb +22 -7
  33. data/spec/acts_as_taggable_on/tags_helper_spec.rb +4 -6
  34. data/spec/bm.rb +52 -0
  35. data/spec/database.yml +17 -0
  36. data/spec/database.yml.sample +17 -0
  37. data/spec/models.rb +31 -0
  38. data/spec/schema.rb +13 -2
  39. data/spec/spec_helper.rb +52 -40
  40. metadata +31 -9
  41. data/lib/acts_as_taggable_on/group_helper.rb +0 -12
  42. data/spec/acts_as_taggable_on/group_helper_spec.rb +0 -18
  43. data/spec/spec.opts +0 -3
@@ -1,50 +1,62 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
2
 
3
3
  describe "Taggable" do
4
4
  before(:each) do
5
- [TaggableModel, Tag, Tagging, TaggableUser].each(&:delete_all)
5
+ clean_database!
6
6
  @taggable = TaggableModel.new(:name => "Bob Jones")
7
7
  end
8
8
 
9
9
  it "should have tag types" do
10
- TaggableModel.tag_types.should == [:tags, :languages, :skills, :needs, :offerings]
10
+ [:tags, :languages, :skills, :needs, :offerings].each do |type|
11
+ TaggableModel.tag_types.should include type
12
+ end
13
+
11
14
  @taggable.tag_types.should == TaggableModel.tag_types
12
15
  end
13
16
 
14
17
  it "should have tag_counts_on" do
15
- TaggableModel.tag_counts_on(:tags).should be_empty
16
-
18
+ TaggableModel.tag_counts_on(:tags).all.should be_empty
19
+
17
20
  @taggable.tag_list = ["awesome", "epic"]
18
21
  @taggable.save
19
22
 
20
- TaggableModel.tag_counts_on(:tags).count.should == 2
21
- @taggable.tag_counts_on(:tags).count.should == 2
23
+ TaggableModel.tag_counts_on(:tags).length.should == 2
24
+ @taggable.tag_counts_on(:tags).length.should == 2
22
25
  end
23
-
26
+
24
27
  it "should be able to create tags" do
25
28
  @taggable.skill_list = "ruby, rails, css"
26
- @taggable.instance_variable_get("@skill_list").instance_of?(TagList).should be_true
27
- @taggable.save
29
+ @taggable.instance_variable_get("@skill_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
28
30
 
29
- Tag.find(:all).size.should == 3
31
+ lambda {
32
+ @taggable.save
33
+ }.should change(ActsAsTaggableOn::Tag, :count).by(3)
34
+
35
+ @taggable.reload
36
+ @taggable.skill_list.sort.should == %w(ruby rails css).sort
30
37
  end
31
-
38
+
32
39
  it "should be able to create tags through the tag list directly" do
33
40
  @taggable.tag_list_on(:test).add("hello")
34
- @taggable.save
41
+ @taggable.tag_list_cache_on(:test).should_not be_empty
42
+ @taggable.tag_list_on(:test).should == ["hello"]
43
+
44
+ @taggable.save
45
+ @taggable.save_tags
46
+
35
47
  @taggable.reload
36
48
  @taggable.tag_list_on(:test).should == ["hello"]
37
49
  end
38
-
50
+
39
51
  it "should differentiate between contexts" do
40
52
  @taggable.skill_list = "ruby, rails, css"
41
53
  @taggable.tag_list = "ruby, bob, charlie"
42
54
  @taggable.save
43
55
  @taggable.reload
44
- @taggable.skill_list.include?("ruby").should be_true
45
- @taggable.skill_list.include?("bob").should be_false
56
+ @taggable.skill_list.should include("ruby")
57
+ @taggable.skill_list.should_not include("bob")
46
58
  end
47
-
59
+
48
60
  it "should be able to remove tags through list alone" do
49
61
  @taggable.skill_list = "ruby, rails, css"
50
62
  @taggable.save
@@ -55,74 +67,98 @@ describe "Taggable" do
55
67
  @taggable.reload
56
68
  @taggable.should have(2).skills
57
69
  end
58
-
70
+
59
71
  it "should be able to find by tag" do
60
72
  @taggable.skill_list = "ruby, rails, css"
61
73
  @taggable.save
62
- TaggableModel.find_tagged_with("ruby").first.should == @taggable
74
+
75
+ TaggableModel.tagged_with("ruby").first.should == @taggable
63
76
  end
64
-
77
+
65
78
  it "should be able to find by tag with context" do
66
79
  @taggable.skill_list = "ruby, rails, css"
67
80
  @taggable.tag_list = "bob, charlie"
68
81
  @taggable.save
69
- TaggableModel.find_tagged_with("ruby").first.should == @taggable
70
- TaggableModel.find_tagged_with("bob", :on => :skills).first.should_not == @taggable
71
- TaggableModel.find_tagged_with("bob", :on => :tags).first.should == @taggable
72
- end
73
-
74
- it "should be able to use the tagged_with named scope" do
75
- @taggable.skill_list = "ruby, rails, css"
76
- @taggable.tag_list = "bob, charlie"
77
- @taggable.save
78
-
82
+
79
83
  TaggableModel.tagged_with("ruby").first.should == @taggable
84
+ TaggableModel.tagged_with("ruby, css").first.should == @taggable
80
85
  TaggableModel.tagged_with("bob", :on => :skills).first.should_not == @taggable
81
86
  TaggableModel.tagged_with("bob", :on => :tags).first.should == @taggable
82
87
  end
83
-
88
+
84
89
  it "should not care about case" do
85
90
  bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby")
86
91
  frank = TaggableModel.create(:name => "Frank", :tag_list => "Ruby")
87
-
88
- Tag.find(:all).size.should == 1
89
- TaggableModel.find_tagged_with("ruby").should == TaggableModel.find_tagged_with("Ruby")
92
+
93
+ ActsAsTaggableOn::Tag.find(:all).size.should == 1
94
+ TaggableModel.tagged_with("ruby").to_a.should == TaggableModel.tagged_with("Ruby").to_a
90
95
  end
91
-
96
+
92
97
  it "should be able to get tag counts on model as a whole" do
93
98
  bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
94
99
  frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
95
100
  charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
96
- TaggableModel.tag_counts.should_not be_empty
97
- TaggableModel.skill_counts.should_not be_empty
101
+ TaggableModel.tag_counts.all.should_not be_empty
102
+ TaggableModel.skill_counts.all.should_not be_empty
98
103
  end
99
-
104
+
100
105
  it "should be able to get all tag counts on model as whole" do
101
106
  bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
102
107
  frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
103
108
  charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
109
+
110
+ TaggableModel.all_tag_counts.all.should_not be_empty
111
+ TaggableModel.all_tag_counts(:order => 'tags.id').first.count.should == 3 # ruby
112
+ end
113
+
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
104
123
 
105
- TaggableModel.all_tag_counts.should_not be_empty
106
- TaggableModel.all_tag_counts.first.count.should == 3 # ruby
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
107
128
  end
108
-
129
+
109
130
  it "should be able to get scoped tag counts" do
110
131
  bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
111
132
  frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
112
133
  charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
113
-
114
- TaggableModel.tagged_with("ruby").tag_counts.first.count.should == 2 # ruby
134
+
135
+ TaggableModel.tagged_with("ruby").tag_counts(:order => 'tags.id').first.count.should == 2 # ruby
115
136
  TaggableModel.tagged_with("ruby").skill_counts.first.count.should == 1 # ruby
116
137
  end
117
-
138
+
118
139
  it "should be able to get all scoped tag counts" do
119
140
  bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
120
141
  frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
121
142
  charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
143
+
144
+ TaggableModel.tagged_with("ruby").all_tag_counts(:order => 'tags.id').first.count.should == 3 # ruby
145
+ end
146
+
147
+ it 'should only return tag counts for the available scope' do
148
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
149
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
150
+ charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby, java")
151
+
152
+ TaggableModel.tagged_with('rails').all_tag_counts.should have(3).items
153
+ TaggableModel.tagged_with('rails').all_tag_counts.any? { |tag| tag.name == 'java' }.should be_false
122
154
 
123
- TaggableModel.tagged_with("ruby").all_tag_counts.first.count.should == 3 # ruby
155
+ # Test specific join syntaxes:
156
+ frank.untaggable_models.create!
157
+ TaggableModel.tagged_with('rails').scoped(:joins => :untaggable_models).all_tag_counts.should have(2).items
158
+ TaggableModel.tagged_with('rails').scoped(:joins => { :untaggable_models => :taggable_model }).all_tag_counts.should have(2).items
159
+ TaggableModel.tagged_with('rails').scoped(:joins => [:untaggable_models]).all_tag_counts.should have(2).items
124
160
  end
125
-
161
+
126
162
  it "should be able to set a custom tag context list" do
127
163
  bob = TaggableModel.create(:name => "Bob")
128
164
  bob.set_tag_list_on(:rotors, "spinning, jumping")
@@ -131,89 +167,150 @@ describe "Taggable" do
131
167
  bob.reload
132
168
  bob.tags_on(:rotors).should_not be_empty
133
169
  end
134
-
170
+
135
171
  it "should be able to find tagged" do
136
172
  bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive", :skill_list => "ruby, rails, css")
137
173
  frank = TaggableModel.create(:name => "Frank", :tag_list => "weaker, depressed, inefficient", :skill_list => "ruby, rails, css")
138
174
  steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, ruby')
139
-
140
- TaggableModel.find_tagged_with("ruby", :order => 'taggable_models.name').should == [bob, frank, steve]
141
- TaggableModel.find_tagged_with("ruby, rails", :order => 'taggable_models.name').should == [bob, frank]
142
- TaggableModel.find_tagged_with(["ruby", "rails"], :order => 'taggable_models.name').should == [bob, frank]
175
+
176
+ TaggableModel.tagged_with("ruby", :order => 'taggable_models.name').to_a.should == [bob, frank, steve]
177
+ TaggableModel.tagged_with("ruby, rails", :order => 'taggable_models.name').to_a.should == [bob, frank]
178
+ TaggableModel.tagged_with(["ruby", "rails"], :order => 'taggable_models.name').to_a.should == [bob, frank]
179
+ end
180
+
181
+ it "should be able to find tagged with quotation marks" do
182
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive, 'I love the ,comma,'")
183
+ TaggableModel.tagged_with("'I love the ,comma,'").should include(bob)
143
184
  end
144
185
 
186
+ 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)
189
+ end
190
+
191
+ it "should be able to find tagged with any tag" do
192
+ bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive", :skill_list => "ruby, rails, css")
193
+ frank = TaggableModel.create(:name => "Frank", :tag_list => "weaker, depressed, inefficient", :skill_list => "ruby, rails, css")
194
+ steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, ruby')
195
+
196
+ TaggableModel.tagged_with(["ruby", "java"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, frank, steve]
197
+ TaggableModel.tagged_with(["c++", "fitter"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, steve]
198
+ TaggableModel.tagged_with(["depressed", "css"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, frank]
199
+ end
200
+
145
201
  it "should be able to find tagged on a custom tag context" do
146
202
  bob = TaggableModel.create(:name => "Bob")
147
203
  bob.set_tag_list_on(:rotors, "spinning, jumping")
148
204
  bob.tag_list_on(:rotors).should == ["spinning","jumping"]
149
205
  bob.save
150
- TaggableModel.find_tagged_with("spinning", :on => :rotors).should == [bob]
206
+
207
+ TaggableModel.tagged_with("spinning", :on => :rotors).to_a.should == [bob]
151
208
  end
152
209
 
153
210
  it "should be able to use named scopes to chain tag finds" do
154
211
  bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive", :skill_list => "ruby, rails, css")
155
212
  frank = TaggableModel.create(:name => "Frank", :tag_list => "weaker, depressed, inefficient", :skill_list => "ruby, rails, css")
156
213
  steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, python')
157
-
214
+
158
215
  # Let's only find those productive Rails developers
159
- TaggableModel.tagged_with('rails', :on => :skills, :order => 'taggable_models.name').should == [bob, frank]
160
- TaggableModel.tagged_with('happier', :on => :tags, :order => 'taggable_models.name').should == [bob, steve]
161
- TaggableModel.tagged_with('rails', :on => :skills).tagged_with('happier', :on => :tags).should == [bob]
162
- TaggableModel.tagged_with('rails').tagged_with('happier', :on => :tags).should == [bob]
216
+ TaggableModel.tagged_with('rails', :on => :skills, :order => 'taggable_models.name').to_a.should == [bob, frank]
217
+ TaggableModel.tagged_with('happier', :on => :tags, :order => 'taggable_models.name').to_a.should == [bob, steve]
218
+ TaggableModel.tagged_with('rails', :on => :skills).tagged_with('happier', :on => :tags).to_a.should == [bob]
219
+ TaggableModel.tagged_with('rails').tagged_with('happier', :on => :tags).to_a.should == [bob]
163
220
  end
164
-
221
+
165
222
  it "should be able to find tagged with only the matching tags" do
166
223
  bob = TaggableModel.create(:name => "Bob", :tag_list => "lazy, happier")
167
224
  frank = TaggableModel.create(:name => "Frank", :tag_list => "fitter, happier, inefficient")
168
225
  steve = TaggableModel.create(:name => 'Steve', :tag_list => "fitter, happier")
169
-
170
- TaggableModel.find_tagged_with("fitter, happier", :match_all => true).should == [steve]
226
+
227
+ TaggableModel.tagged_with("fitter, happier", :match_all => true).to_a.should == [steve]
171
228
  end
172
-
229
+
173
230
  it "should be able to find tagged with some excluded tags" do
174
231
  bob = TaggableModel.create(:name => "Bob", :tag_list => "happier, lazy")
175
232
  frank = TaggableModel.create(:name => "Frank", :tag_list => "happier")
176
233
  steve = TaggableModel.create(:name => 'Steve', :tag_list => "happier")
234
+
235
+ TaggableModel.tagged_with("lazy", :exclude => true).to_a.should == [frank, steve]
236
+ end
237
+
238
+ it "should not create duplicate taggings" do
239
+ bob = TaggableModel.create(:name => "Bob")
240
+ lambda {
241
+ bob.tag_list << "happier"
242
+ bob.tag_list << "happier"
243
+ bob.save
244
+ }.should change(ActsAsTaggableOn::Tagging, :count).by(1)
245
+ end
246
+
247
+ describe "Associations" do
248
+ before(:each) do
249
+ @taggable = TaggableModel.create(:tag_list => "awesome, epic")
250
+ end
177
251
 
178
- TaggableModel.find_tagged_with("lazy", :exclude => true).should == [frank, steve]
252
+ it "should not remove tags when creating associated objects" do
253
+ @taggable.untaggable_models.create!
254
+ @taggable.reload
255
+ @taggable.tag_list.should have(2).items
256
+ end
179
257
  end
180
-
258
+
259
+ describe "grouped_column_names_for method" do
260
+ it "should return all column names joined for Tag GROUP clause" do
261
+ @taggable.grouped_column_names_for(ActsAsTaggableOn::Tag).should == "tags.id, tags.name"
262
+ end
263
+
264
+ it "should return all column names joined for TaggableModel GROUP clause" do
265
+ @taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.id, taggable_models.name, taggable_models.type"
266
+ end
267
+ end
268
+
181
269
  describe "Single Table Inheritance" do
182
270
  before do
183
- [TaggableModel, Tag, Tagging, TaggableUser].each(&:delete_all)
184
271
  @taggable = TaggableModel.new(:name => "taggable")
185
272
  @inherited_same = InheritingTaggableModel.new(:name => "inherited same")
186
273
  @inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
187
274
  end
188
-
275
+
189
276
  it "should be able to save tags for inherited models" do
190
277
  @inherited_same.tag_list = "bob, kelso"
191
278
  @inherited_same.save
192
- InheritingTaggableModel.find_tagged_with("bob").first.should == @inherited_same
279
+ InheritingTaggableModel.tagged_with("bob").first.should == @inherited_same
193
280
  end
194
-
281
+
195
282
  it "should find STI tagged models on the superclass" do
196
283
  @inherited_same.tag_list = "bob, kelso"
197
284
  @inherited_same.save
198
- TaggableModel.find_tagged_with("bob").first.should == @inherited_same
285
+ TaggableModel.tagged_with("bob").first.should == @inherited_same
199
286
  end
200
-
287
+
201
288
  it "should be able to add on contexts only to some subclasses" do
202
289
  @inherited_different.part_list = "fork, spoon"
203
290
  @inherited_different.save
204
- InheritingTaggableModel.find_tagged_with("fork", :on => :parts).should be_empty
205
- AlteredInheritingTaggableModel.find_tagged_with("fork", :on => :parts).first.should == @inherited_different
291
+ InheritingTaggableModel.tagged_with("fork", :on => :parts).should be_empty
292
+ AlteredInheritingTaggableModel.tagged_with("fork", :on => :parts).first.should == @inherited_different
206
293
  end
207
-
294
+
208
295
  it "should have different tag_counts_on for inherited models" do
209
296
  @inherited_same.tag_list = "bob, kelso"
210
297
  @inherited_same.save!
211
298
  @inherited_different.tag_list = "fork, spoon"
212
299
  @inherited_different.save!
213
-
214
- InheritingTaggableModel.tag_counts_on(:tags).map(&:name).should == %w(bob kelso)
215
- AlteredInheritingTaggableModel.tag_counts_on(:tags).map(&:name).should == %w(fork spoon)
216
- TaggableModel.tag_counts_on(:tags).map(&:name).should == %w(bob kelso fork spoon)
300
+
301
+ InheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso)
302
+ AlteredInheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(fork spoon)
303
+ TaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso fork spoon)
304
+ end
305
+
306
+ it 'should store same tag without validation conflict' do
307
+ @taggable.tag_list = 'one'
308
+ @taggable.save!
309
+
310
+ @inherited_same.tag_list = 'one'
311
+ @inherited_same.save!
312
+
313
+ @inherited_same.update_attributes! :name => 'foo'
217
314
  end
218
315
  end
219
316
  end
@@ -1,22 +1,90 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
2
 
3
3
  describe "Tagger" do
4
4
  before(:each) do
5
- [TaggableModel, Tag, Tagging, TaggableUser].each(&:delete_all)
6
- @user = TaggableUser.new
7
- @taggable = TaggableModel.new(:name => "Bob Jones")
5
+ clean_database!
6
+ @user = TaggableUser.create
7
+ @taggable = TaggableModel.create(:name => "Bob Jones")
8
8
  end
9
-
9
+
10
10
  it "should have taggings" do
11
11
  @user.tag(@taggable, :with=>'ruby,scheme', :on=>:tags)
12
12
  @user.owned_taggings.size == 2
13
13
  end
14
-
14
+
15
15
  it "should have tags" 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 not overlap tags from different taggers" do
21
+ @user2 = TaggableUser.new
22
+ lambda{
23
+ @user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
24
+ @user2.tag(@taggable, :with => 'java, python, lisp, ruby', :on => :tags)
25
+ }.should change(ActsAsTaggableOn::Tagging, :count).by(6)
26
+
27
+ [@user, @user2, @taggable].each(&:reload)
28
+
29
+ @user.owned_tags.map(&:name).sort.should == %w(ruby scheme).sort
30
+ @user2.owned_tags.map(&:name).sort.should == %w(java python lisp ruby).sort
31
+
32
+ @taggable.tags_from(@user).sort.should == %w(ruby scheme).sort
33
+ @taggable.tags_from(@user2).sort.should == %w(java lisp python ruby).sort
34
+
35
+ @taggable.all_tags_list.sort.should == %w(ruby scheme java python lisp).sort
36
+ @taggable.all_tags_on(:tags).size.should == 5
37
+ end
38
+
39
+ it "should not lose tags from different taggers" do
40
+ @user2 = TaggableUser.create
41
+ @user2.tag(@taggable, :with => 'java, python, lisp, ruby', :on => :tags)
42
+ @user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
43
+
44
+ lambda {
45
+ @user2.tag(@taggable, :with => 'java, python, lisp', :on => :tags)
46
+ }.should change(ActsAsTaggableOn::Tagging, :count).by(-1)
47
+
48
+ [@user, @user2, @taggable].each(&:reload)
49
+
50
+ @taggable.tags_from(@user).sort.should == %w(ruby scheme).sort
51
+ @taggable.tags_from(@user2).sort.should == %w(java python lisp).sort
52
+
53
+ @taggable.all_tags_list.sort.should == %w(ruby scheme java python lisp).sort
54
+ @taggable.all_tags_on(:tags).length.should == 5
55
+ end
56
+
57
+ it "should not lose tags" do
58
+ @user2 = TaggableUser.create
59
+
60
+ @user.tag(@taggable, :with => 'awesome', :on => :tags)
61
+ @user2.tag(@taggable, :with => 'awesome, epic', :on => :tags)
62
+
63
+ lambda {
64
+ @user2.tag(@taggable, :with => 'epic', :on => :tags)
65
+ }.should change(ActsAsTaggableOn::Tagging, :count).by(-1)
66
+
67
+ @taggable.reload
68
+ @taggable.all_tags_list.should include('awesome')
69
+ @taggable.all_tags_list.should include('epic')
70
+ end
71
+
72
+ it "should not lose tags" do
73
+ @taggable.update_attributes(:tag_list => 'ruby')
74
+ @user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
75
+
76
+ [@taggable, @user].each(&:reload)
77
+ @taggable.tag_list.should == %w(ruby)
78
+ @taggable.all_tags_list.sort.should == %w(ruby scheme).sort
79
+
80
+ lambda {
81
+ @taggable.update_attributes(:tag_list => "")
82
+ }.should change(ActsAsTaggableOn::Tagging, :count).by(-1)
83
+
84
+ @taggable.tag_list.should == []
85
+ @taggable.all_tags_list.sort.should == %w(ruby scheme).sort
86
+ end
87
+
20
88
  it "is tagger" do
21
89
  @user.is_tagger?.should(be_true)
22
90
  end
@@ -1,16 +1,31 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
2
 
3
- describe Tagging do
3
+ describe ActsAsTaggableOn::Tagging do
4
4
  before(:each) do
5
- @tagging = Tagging.new
5
+ clean_database!
6
+ @tagging = ActsAsTaggableOn::Tagging.new
6
7
  end
7
-
8
+
8
9
  it "should not be valid with a invalid tag" do
9
10
  @tagging.taggable = TaggableModel.create(:name => "Bob Jones")
10
- @tagging.tag = Tag.new(:name => "")
11
+ @tagging.tag = ActsAsTaggableOn::Tag.new(:name => "")
11
12
  @tagging.context = "tags"
12
13
 
13
14
  @tagging.should_not be_valid
14
- @tagging.errors.on(:tag_id).should == "can't be blank"
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
21
+ end
22
+
23
+ it "should not create duplicate taggings" do
24
+ @taggable = TaggableModel.create(:name => "Bob Jones")
25
+ @tag = ActsAsTaggableOn::Tag.create(:name => "awesome")
26
+
27
+ lambda {
28
+ 2.times { ActsAsTaggableOn::Tagging.create(:taggable => @taggable, :tag => @tag, :context => 'tags') }
29
+ }.should change(ActsAsTaggableOn::Tagging, :count).by(1)
15
30
  end
16
- end
31
+ end
@@ -1,18 +1,16 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
2
 
3
- describe TagsHelper do
3
+ describe ActsAsTaggableOn::TagsHelper do
4
4
  before(:each) do
5
- [TaggableModel, Tag, Tagging].each(&:delete_all)
5
+ clean_database!
6
6
 
7
7
  @bob = TaggableModel.create(:name => "Bob Jones", :language_list => "ruby, php")
8
8
  @tom = TaggableModel.create(:name => "Tom Marley", :language_list => "ruby, java")
9
9
  @eve = TaggableModel.create(:name => "Eve Nodd", :language_list => "ruby, c++")
10
10
 
11
11
  @helper = class Helper
12
- include TagsHelper
12
+ include ActsAsTaggableOn::TagsHelper
13
13
  end.new
14
-
15
-
16
14
  end
17
15
 
18
16
  it "should yield the proper css classes" do
data/spec/bm.rb ADDED
@@ -0,0 +1,52 @@
1
+ require 'active_record'
2
+ require 'action_view'
3
+ require File.expand_path('../../lib/acts-as-taggable-on', __FILE__)
4
+
5
+ if defined?(ActiveRecord::Acts::TaggableOn)
6
+ ActiveRecord::Base.send :include, ActiveRecord::Acts::TaggableOn
7
+ ActiveRecord::Base.send :include, ActiveRecord::Acts::Tagger
8
+ ActionView::Base.send :include, TagsHelper if defined?(ActionView::Base)
9
+ end
10
+
11
+ TEST_DATABASE_FILE = File.join(File.dirname(__FILE__), '..', 'test.sqlite3')
12
+ File.unlink(TEST_DATABASE_FILE) if File.exist?(TEST_DATABASE_FILE)
13
+ ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => TEST_DATABASE_FILE
14
+
15
+ ActiveRecord::Base.silence do
16
+ ActiveRecord::Migration.verbose = false
17
+ ActiveRecord::Schema.define :version => 0 do
18
+ create_table "taggings", :force => true do |t|
19
+ t.integer "tag_id", :limit => 11
20
+ t.integer "taggable_id", :limit => 11
21
+ t.string "taggable_type"
22
+ t.string "context"
23
+ t.datetime "created_at"
24
+ t.integer "tagger_id", :limit => 11
25
+ t.string "tagger_type"
26
+ end
27
+
28
+ add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
29
+ add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context"
30
+
31
+ create_table "tags", :force => true do |t|
32
+ t.string "name"
33
+ end
34
+
35
+ create_table :taggable_models, :force => true do |t|
36
+ t.column :name, :string
37
+ t.column :type, :string
38
+ t.column :cached_tag_list, :string
39
+ end
40
+ end
41
+
42
+ class TaggableModel < ActiveRecord::Base
43
+ acts_as_taggable
44
+ acts_as_taggable_on :languages
45
+ acts_as_taggable_on :skills
46
+ acts_as_taggable_on :needs, :offerings
47
+ end
48
+ end
49
+
50
+ puts Benchmark.measure {
51
+ 1000.times { TaggableModel.create :tag_list => "awesome, epic, neat" }
52
+ }
data/spec/database.yml ADDED
@@ -0,0 +1,17 @@
1
+ sqlite3:
2
+ adapter: sqlite3
3
+ database: acts_as_taggable_on.sqlite3
4
+
5
+ mysql:
6
+ adapter: mysql
7
+ hostname: localhost
8
+ username: root
9
+ password:
10
+ database: acts_as_taggable_on
11
+
12
+ postgresql:
13
+ adapter: postgresql
14
+ hostname: localhost
15
+ username: postgres
16
+ password:
17
+ database: acts_as_taggable_on
@@ -0,0 +1,17 @@
1
+ sqlite3:
2
+ adapter: sqlite3
3
+ database: acts_as_taggable_on.sqlite3
4
+
5
+ mysql:
6
+ adapter: mysql
7
+ hostname: localhost
8
+ username: root
9
+ password:
10
+ database: acts_as_taggable_on
11
+
12
+ postgresql:
13
+ adapter: postgresql
14
+ hostname: localhost
15
+ username: postgres
16
+ password:
17
+ database: acts_as_taggable_on
data/spec/models.rb ADDED
@@ -0,0 +1,31 @@
1
+ class TaggableModel < ActiveRecord::Base
2
+ acts_as_taggable
3
+ acts_as_taggable_on :languages
4
+ acts_as_taggable_on :skills
5
+ acts_as_taggable_on :needs, :offerings
6
+ has_many :untaggable_models
7
+ end
8
+
9
+ class CachedModel < ActiveRecord::Base
10
+ acts_as_taggable
11
+ end
12
+
13
+ class OtherTaggableModel < ActiveRecord::Base
14
+ acts_as_taggable_on :tags, :languages
15
+ acts_as_taggable_on :needs, :offerings
16
+ end
17
+
18
+ class InheritingTaggableModel < TaggableModel
19
+ end
20
+
21
+ class AlteredInheritingTaggableModel < TaggableModel
22
+ acts_as_taggable_on :parts
23
+ end
24
+
25
+ class TaggableUser < ActiveRecord::Base
26
+ acts_as_tagger
27
+ end
28
+
29
+ class UntaggableModel < ActiveRecord::Base
30
+ belongs_to :taggable_model
31
+ end