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.
- data/CHANGELOG +5 -2
- data/Gemfile +10 -0
- data/README.rdoc +56 -26
- data/Rakefile +46 -16
- data/VERSION +1 -1
- data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +7 -0
- data/generators/acts_as_taggable_on_migration/templates/migration.rb +29 -0
- data/lib/acts-as-taggable-on.rb +30 -7
- data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +53 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +132 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +241 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +101 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +65 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +43 -373
- data/lib/acts_as_taggable_on/acts_as_tagger.rb +60 -45
- data/lib/acts_as_taggable_on/compatibility/Gemfile +8 -0
- data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +17 -0
- data/lib/acts_as_taggable_on/compatibility/postgresql.rb +44 -0
- data/lib/acts_as_taggable_on/tag.rb +70 -22
- data/lib/acts_as_taggable_on/tag_list.rb +81 -80
- data/lib/acts_as_taggable_on/tagging.rb +23 -7
- data/lib/acts_as_taggable_on/tags_helper.rb +14 -8
- data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +32 -0
- data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +28 -0
- data/rails/init.rb +1 -7
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +102 -55
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +48 -6
- data/spec/acts_as_taggable_on/tag_list_spec.rb +21 -3
- data/spec/acts_as_taggable_on/tag_spec.rb +77 -24
- data/spec/acts_as_taggable_on/taggable_spec.rb +173 -76
- data/spec/acts_as_taggable_on/tagger_spec.rb +74 -6
- data/spec/acts_as_taggable_on/tagging_spec.rb +22 -7
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +4 -6
- data/spec/bm.rb +52 -0
- data/spec/database.yml +17 -0
- data/spec/database.yml.sample +17 -0
- data/spec/models.rb +31 -0
- data/spec/schema.rb +13 -2
- data/spec/spec_helper.rb +52 -40
- metadata +31 -9
- data/lib/acts_as_taggable_on/group_helper.rb +0 -12
- data/spec/acts_as_taggable_on/group_helper_spec.rb +0 -18
- data/spec/spec.opts +0 -3
|
@@ -1,50 +1,62 @@
|
|
|
1
|
-
require File.
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
|
2
2
|
|
|
3
3
|
describe "Taggable" do
|
|
4
4
|
before(:each) do
|
|
5
|
-
|
|
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
|
-
|
|
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).
|
|
21
|
-
@taggable.tag_counts_on(:tags).
|
|
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
|
-
|
|
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.
|
|
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
|
|
45
|
-
@taggable.skill_list.include
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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.
|
|
141
|
-
TaggableModel.
|
|
142
|
-
TaggableModel.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
205
|
-
AlteredInheritingTaggableModel.
|
|
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.
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
|
2
2
|
|
|
3
3
|
describe "Tagger" do
|
|
4
4
|
before(:each) do
|
|
5
|
-
|
|
6
|
-
@user = TaggableUser.
|
|
7
|
-
@taggable = TaggableModel.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|