acts-as-taggable-on 2.0.6 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +9 -0
- data/CHANGELOG +10 -0
- data/Gemfile +2 -9
- data/Guardfile +5 -0
- data/README.rdoc +89 -66
- data/Rakefile +9 -55
- data/acts-as-taggable-on.gemspec +28 -0
- data/lib/acts-as-taggable-on/version.rb +4 -0
- data/lib/acts-as-taggable-on.rb +33 -4
- data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +4 -4
- data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +38 -43
- data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +146 -38
- data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +37 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +36 -11
- data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +23 -15
- data/lib/acts_as_taggable_on/tag.rb +16 -13
- data/lib/acts_as_taggable_on/tag_list.rb +13 -12
- data/lib/acts_as_taggable_on/taggable.rb +102 -0
- data/lib/acts_as_taggable_on/{acts_as_tagger.rb → tagger.rb} +3 -3
- data/lib/acts_as_taggable_on/tagging.rb +12 -2
- data/lib/acts_as_taggable_on/tags_helper.rb +2 -2
- data/lib/acts_as_taggable_on/utils.rb +34 -0
- data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +9 -2
- data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +3 -1
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +300 -54
- data/spec/acts_as_taggable_on/tag_list_spec.rb +84 -61
- data/spec/acts_as_taggable_on/tag_spec.rb +51 -13
- data/spec/acts_as_taggable_on/taggable_spec.rb +261 -34
- data/spec/acts_as_taggable_on/tagger_spec.rb +36 -15
- data/spec/acts_as_taggable_on/tagging_spec.rb +2 -5
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +16 -0
- data/spec/acts_as_taggable_on/utils_spec.rb +21 -0
- data/spec/database.yml.sample +4 -2
- data/spec/generators/acts_as_taggable_on/migration/migration_generator_spec.rb +22 -0
- data/spec/models.rb +19 -1
- data/spec/schema.rb +18 -0
- data/spec/spec_helper.rb +30 -7
- data/uninstall.rb +1 -0
- metadata +137 -51
- data/VERSION +0 -1
- data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +0 -7
- data/generators/acts_as_taggable_on_migration/templates/migration.rb +0 -29
- data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +0 -53
- data/lib/acts_as_taggable_on/compatibility/Gemfile +0 -8
- data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +0 -17
- data/lib/acts_as_taggable_on/compatibility/postgresql.rb +0 -44
- data/spec/database.yml +0 -17
@@ -1,3 +1,5 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
1
3
|
require File.expand_path('../../spec_helper', __FILE__)
|
2
4
|
|
3
5
|
describe ActsAsTaggableOn::Tag do
|
@@ -9,12 +11,13 @@ describe ActsAsTaggableOn::Tag do
|
|
9
11
|
|
10
12
|
describe "named like any" do
|
11
13
|
before(:each) do
|
14
|
+
ActsAsTaggableOn::Tag.create(:name => "Awesome")
|
12
15
|
ActsAsTaggableOn::Tag.create(:name => "awesome")
|
13
16
|
ActsAsTaggableOn::Tag.create(:name => "epic")
|
14
17
|
end
|
15
18
|
|
16
19
|
it "should find both tags" do
|
17
|
-
ActsAsTaggableOn::Tag.named_like_any(["awesome", "epic"]).should have(
|
20
|
+
ActsAsTaggableOn::Tag.named_like_any(["awesome", "epic"]).should have(3).items
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
@@ -39,6 +42,23 @@ describe ActsAsTaggableOn::Tag do
|
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
45
|
+
unless ActsAsTaggableOn::Tag.using_sqlite?
|
46
|
+
describe "find or create by unicode name" do
|
47
|
+
before(:each) do
|
48
|
+
@tag.name = "привет"
|
49
|
+
@tag.save
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should find by name" do
|
53
|
+
ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("привет").should == @tag
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should find by name case insensitive" do
|
57
|
+
ActsAsTaggableOn::Tag.find_or_create_with_like_by_name("ПРИВЕТ").should == @tag
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
42
62
|
describe "find or create all by any name" do
|
43
63
|
before(:each) do
|
44
64
|
@tag.name = "awesome"
|
@@ -72,21 +92,23 @@ describe ActsAsTaggableOn::Tag do
|
|
72
92
|
|
73
93
|
it "should require a name" do
|
74
94
|
@tag.valid?
|
75
|
-
|
76
|
-
|
77
|
-
@tag.errors[:name].should == ["can't be blank"]
|
78
|
-
else
|
79
|
-
@tag.errors[:name].should == "can't be blank"
|
80
|
-
end
|
95
|
+
|
96
|
+
@tag.errors[:name].should == ["can't be blank"]
|
81
97
|
|
82
98
|
@tag.name = "something"
|
83
99
|
@tag.valid?
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
100
|
+
|
101
|
+
@tag.errors[:name].should == []
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should limit the name length to 255 or less characters" do
|
105
|
+
@tag.name = "fgkgnkkgjymkypbuozmwwghblmzpqfsgjasflblywhgkwndnkzeifalfcpeaeqychjuuowlacmuidnnrkprgpcpybarbkrmziqihcrxirlokhnzfvmtzixgvhlxzncyywficpraxfnjptxxhkqmvicbcdcynkjvziefqzyndxkjmsjlvyvbwraklbalykyxoliqdlreeykuphdtmzfdwpphmrqvwvqffojkqhlzvinqajsxbszyvrqqyzusxranr"
|
106
|
+
@tag.valid?
|
107
|
+
@tag.errors[:name].should == ["is too long (maximum is 255 characters)"]
|
108
|
+
|
109
|
+
@tag.name = "fgkgnkkgjymkypbuozmwwghblmzpqfsgjasflblywhgkwndnkzeifalfcpeaeqychjuuowlacmuidnnrkprgpcpybarbkrmziqihcrxirlokhnzfvmtzixgvhlxzncyywficpraxfnjptxxhkqmvicbcdcynkjvziefqzyndxkjmsjlvyvbwraklbalykyxoliqdlreeykuphdtmzfdwpphmrqvwvqffojkqhlzvinqajsxbszyvrqqyzusxran"
|
110
|
+
@tag.valid?
|
111
|
+
@tag.errors[:name].should == []
|
90
112
|
end
|
91
113
|
|
92
114
|
it "should equal a tag with the same name" do
|
@@ -112,4 +134,20 @@ describe ActsAsTaggableOn::Tag do
|
|
112
134
|
@another_tag = ActsAsTaggableOn::Tag.create!(:name => "coolip")
|
113
135
|
ActsAsTaggableOn::Tag.named_like('cool').should include(@tag, @another_tag)
|
114
136
|
end
|
137
|
+
|
138
|
+
describe "escape wildcard symbols in like requests" do
|
139
|
+
before(:each) do
|
140
|
+
@tag.name = "cool"
|
141
|
+
@tag.save
|
142
|
+
@another_tag = ActsAsTaggableOn::Tag.create!(:name => "coo%")
|
143
|
+
@another_tag2 = ActsAsTaggableOn::Tag.create!(:name => "coolish")
|
144
|
+
end
|
145
|
+
|
146
|
+
it "return escaped result when '%' char present in tag" do
|
147
|
+
ActsAsTaggableOn::Tag.named_like('coo%').should_not include(@tag)
|
148
|
+
ActsAsTaggableOn::Tag.named_like('coo%').should include(@another_tag)
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
115
153
|
end
|
@@ -1,9 +1,119 @@
|
|
1
1
|
require File.expand_path('../../spec_helper', __FILE__)
|
2
2
|
|
3
|
+
describe "Taggable To Preserve Order" do
|
4
|
+
before(:each) do
|
5
|
+
clean_database!
|
6
|
+
@taggable = OrderedTaggableModel.new(:name => "Bob Jones")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should have tag types" do
|
10
|
+
[:tags, :colours].each do |type|
|
11
|
+
OrderedTaggableModel.tag_types.should include type
|
12
|
+
end
|
13
|
+
|
14
|
+
@taggable.tag_types.should == OrderedTaggableModel.tag_types
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have tag associations" do
|
18
|
+
[:tags, :colours].each do |type|
|
19
|
+
@taggable.respond_to?(type).should be_true
|
20
|
+
@taggable.respond_to?("#{type.to_s.singularize}_taggings").should be_true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should have tag associations ordered by id" do
|
25
|
+
[:tags, :colours].each do |type|
|
26
|
+
OrderedTaggableModel.reflect_on_association(type).options[:order].should include('id')
|
27
|
+
OrderedTaggableModel.reflect_on_association("#{type.to_s.singularize}_taggings".to_sym).options[:order].should include('id')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should have tag methods" do
|
32
|
+
[:tags, :colours].each do |type|
|
33
|
+
@taggable.respond_to?("#{type.to_s.singularize}_list").should be_true
|
34
|
+
@taggable.respond_to?("#{type.to_s.singularize}_list=").should be_true
|
35
|
+
@taggable.respond_to?("all_#{type.to_s}_list").should be_true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should return tag list in the order the tags were created" do
|
40
|
+
# create
|
41
|
+
@taggable.tag_list = "rails, ruby, css"
|
42
|
+
@taggable.instance_variable_get("@tag_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
|
43
|
+
|
44
|
+
lambda {
|
45
|
+
@taggable.save
|
46
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(3)
|
47
|
+
|
48
|
+
@taggable.reload
|
49
|
+
@taggable.tag_list.should == %w(rails ruby css)
|
50
|
+
|
51
|
+
# update
|
52
|
+
@taggable.tag_list = "pow, ruby, rails"
|
53
|
+
@taggable.save
|
54
|
+
|
55
|
+
@taggable.reload
|
56
|
+
@taggable.tag_list.should == %w(pow ruby rails)
|
57
|
+
|
58
|
+
# update with no change
|
59
|
+
@taggable.tag_list = "pow, ruby, rails"
|
60
|
+
@taggable.save
|
61
|
+
|
62
|
+
@taggable.reload
|
63
|
+
@taggable.tag_list.should == %w(pow ruby rails)
|
64
|
+
|
65
|
+
# update to clear tags
|
66
|
+
@taggable.tag_list = ""
|
67
|
+
@taggable.save
|
68
|
+
|
69
|
+
@taggable.reload
|
70
|
+
@taggable.tag_list.should == []
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should return tag objects in the order the tags were created" do
|
74
|
+
# create
|
75
|
+
@taggable.tag_list = "pow, ruby, rails"
|
76
|
+
@taggable.instance_variable_get("@tag_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
|
77
|
+
|
78
|
+
lambda {
|
79
|
+
@taggable.save
|
80
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(3)
|
81
|
+
|
82
|
+
@taggable.reload
|
83
|
+
@taggable.tags.map{|t| t.name}.should == %w(pow ruby rails)
|
84
|
+
|
85
|
+
# update
|
86
|
+
@taggable.tag_list = "rails, ruby, css, pow"
|
87
|
+
@taggable.save
|
88
|
+
|
89
|
+
@taggable.reload
|
90
|
+
@taggable.tags.map{|t| t.name}.should == %w(rails ruby css pow)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should return tag objects in tagging id order" do
|
94
|
+
# create
|
95
|
+
@taggable.tag_list = "pow, ruby, rails"
|
96
|
+
@taggable.save
|
97
|
+
|
98
|
+
@taggable.reload
|
99
|
+
ids = @taggable.tags.map{|t| t.taggings.first.id}
|
100
|
+
ids.should == ids.sort
|
101
|
+
|
102
|
+
# update
|
103
|
+
@taggable.tag_list = "rails, ruby, css, pow"
|
104
|
+
@taggable.save
|
105
|
+
|
106
|
+
@taggable.reload
|
107
|
+
ids = @taggable.tags.map{|t| t.taggings.first.id}
|
108
|
+
ids.should == ids.sort
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
3
112
|
describe "Taggable" do
|
4
113
|
before(:each) do
|
5
114
|
clean_database!
|
6
115
|
@taggable = TaggableModel.new(:name => "Bob Jones")
|
116
|
+
@taggables = [@taggable, TaggableModel.new(:name => "John Doe")]
|
7
117
|
end
|
8
118
|
|
9
119
|
it "should have tag types" do
|
@@ -24,14 +134,19 @@ describe "Taggable" do
|
|
24
134
|
@taggable.tag_counts_on(:tags).length.should == 2
|
25
135
|
end
|
26
136
|
|
137
|
+
it "should return [] right after create" do
|
138
|
+
blank_taggable = TaggableModel.new(:name => "Bob Jones")
|
139
|
+
blank_taggable.tag_list.should == []
|
140
|
+
end
|
141
|
+
|
27
142
|
it "should be able to create tags" do
|
28
143
|
@taggable.skill_list = "ruby, rails, css"
|
29
144
|
@taggable.instance_variable_get("@skill_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
|
30
|
-
|
145
|
+
|
31
146
|
lambda {
|
32
147
|
@taggable.save
|
33
148
|
}.should change(ActsAsTaggableOn::Tag, :count).by(3)
|
34
|
-
|
149
|
+
|
35
150
|
@taggable.reload
|
36
151
|
@taggable.skill_list.sort.should == %w(ruby rails css).sort
|
37
152
|
end
|
@@ -40,10 +155,10 @@ describe "Taggable" do
|
|
40
155
|
@taggable.tag_list_on(:test).add("hello")
|
41
156
|
@taggable.tag_list_cache_on(:test).should_not be_empty
|
42
157
|
@taggable.tag_list_on(:test).should == ["hello"]
|
43
|
-
|
158
|
+
|
44
159
|
@taggable.save
|
45
160
|
@taggable.save_tags
|
46
|
-
|
161
|
+
|
47
162
|
@taggable.reload
|
48
163
|
@taggable.tag_list_on(:test).should == ["hello"]
|
49
164
|
end
|
@@ -68,6 +183,22 @@ describe "Taggable" do
|
|
68
183
|
@taggable.should have(2).skills
|
69
184
|
end
|
70
185
|
|
186
|
+
it "should be able to select taggables by subset of tags using ActiveRelation methods" do
|
187
|
+
@taggables[0].tag_list = "bob"
|
188
|
+
@taggables[1].tag_list = "charlie"
|
189
|
+
@taggables[0].skill_list = "ruby"
|
190
|
+
@taggables[1].skill_list = "css"
|
191
|
+
@taggables.each{|taggable| taggable.save}
|
192
|
+
|
193
|
+
@found_taggables_by_tag = TaggableModel.joins(:tags).where(:tags => {:name => ["bob"]})
|
194
|
+
@found_taggables_by_skill = TaggableModel.joins(:skills).where(:tags => {:name => ["ruby"]})
|
195
|
+
|
196
|
+
@found_taggables_by_tag.should include @taggables[0]
|
197
|
+
@found_taggables_by_tag.should_not include @taggables[1]
|
198
|
+
@found_taggables_by_skill.should include @taggables[0]
|
199
|
+
@found_taggables_by_skill.should_not include @taggables[1]
|
200
|
+
end
|
201
|
+
|
71
202
|
it "should be able to find by tag" do
|
72
203
|
@taggable.skill_list = "ruby, rails, css"
|
73
204
|
@taggable.save
|
@@ -111,20 +242,18 @@ describe "Taggable" do
|
|
111
242
|
TaggableModel.all_tag_counts(:order => 'tags.id').first.count.should == 3 # ruby
|
112
243
|
end
|
113
244
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
TaggableModel.tagged_with("ruby").first(:readonly => false).should_not be_readonly
|
127
|
-
end
|
245
|
+
it "should be able to use named scopes to chain tag finds by any tags by context" do
|
246
|
+
bob = TaggableModel.create(:name => "Bob", :need_list => "rails", :offering_list => "c++")
|
247
|
+
frank = TaggableModel.create(:name => "Frank", :need_list => "css", :offering_list => "css")
|
248
|
+
steve = TaggableModel.create(:name => 'Steve', :need_list => "c++", :offering_list => "java")
|
249
|
+
|
250
|
+
# Let's only find those who need rails or css and are offering c++ or java
|
251
|
+
TaggableModel.tagged_with(['rails, css'], :on => :needs, :any => true).tagged_with(['c++', 'java'], :on => :offerings, :any => true).to_a.should == [bob]
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should not return read-only records" do
|
255
|
+
TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
|
256
|
+
TaggableModel.tagged_with("ruby").first.should_not be_readonly
|
128
257
|
end
|
129
258
|
|
130
259
|
it "should be able to get scoped tag counts" do
|
@@ -148,10 +277,10 @@ describe "Taggable" do
|
|
148
277
|
bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
|
149
278
|
frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
|
150
279
|
charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby, java")
|
151
|
-
|
280
|
+
|
152
281
|
TaggableModel.tagged_with('rails').all_tag_counts.should have(3).items
|
153
282
|
TaggableModel.tagged_with('rails').all_tag_counts.any? { |tag| tag.name == 'java' }.should be_false
|
154
|
-
|
283
|
+
|
155
284
|
# Test specific join syntaxes:
|
156
285
|
frank.untaggable_models.create!
|
157
286
|
TaggableModel.tagged_with('rails').scoped(:joins => :untaggable_models).all_tag_counts.should have(2).items
|
@@ -177,15 +306,15 @@ describe "Taggable" do
|
|
177
306
|
TaggableModel.tagged_with("ruby, rails", :order => 'taggable_models.name').to_a.should == [bob, frank]
|
178
307
|
TaggableModel.tagged_with(["ruby", "rails"], :order => 'taggable_models.name').to_a.should == [bob, frank]
|
179
308
|
end
|
180
|
-
|
309
|
+
|
181
310
|
it "should be able to find tagged with quotation marks" do
|
182
311
|
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive, 'I love the ,comma,'")
|
183
312
|
TaggableModel.tagged_with("'I love the ,comma,'").should include(bob)
|
184
313
|
end
|
185
|
-
|
314
|
+
|
186
315
|
it "should be able to find tagged with invalid tags" do
|
187
|
-
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive")
|
188
|
-
TaggableModel.tagged_with("sad, happier").should_not include(bob)
|
316
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive")
|
317
|
+
TaggableModel.tagged_with("sad, happier").should_not include(bob)
|
189
318
|
end
|
190
319
|
|
191
320
|
it "should be able to find tagged with any tag" do
|
@@ -198,6 +327,19 @@ describe "Taggable" do
|
|
198
327
|
TaggableModel.tagged_with(["depressed", "css"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, frank]
|
199
328
|
end
|
200
329
|
|
330
|
+
context "wild: true" do
|
331
|
+
it "should use params as wildcards" do
|
332
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "bob, tricia")
|
333
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "bobby, jim")
|
334
|
+
steve = TaggableModel.create(:name => "Steve", :tag_list => "john, patricia")
|
335
|
+
jim = TaggableModel.create(:name => "Jim", :tag_list => "jim, steve")
|
336
|
+
|
337
|
+
|
338
|
+
TaggableModel.tagged_with(["bob", "tricia"], :wild => true, :any => true).to_a.sort_by{|o| o.id}.should == [bob, frank, steve]
|
339
|
+
TaggableModel.tagged_with(["bob", "tricia"], :wild => true, :exclude => true).to_a.should == [jim]
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
201
343
|
it "should be able to find tagged on a custom tag context" do
|
202
344
|
bob = TaggableModel.create(:name => "Bob")
|
203
345
|
bob.set_tag_list_on(:rotors, "spinning, jumping")
|
@@ -235,6 +377,13 @@ describe "Taggable" do
|
|
235
377
|
TaggableModel.tagged_with("lazy", :exclude => true).to_a.should == [frank, steve]
|
236
378
|
end
|
237
379
|
|
380
|
+
it "should return an empty scope for empty tags" do
|
381
|
+
TaggableModel.tagged_with('').should == []
|
382
|
+
TaggableModel.tagged_with(' ').should == []
|
383
|
+
TaggableModel.tagged_with(nil).should == []
|
384
|
+
TaggableModel.tagged_with([]).should == []
|
385
|
+
end
|
386
|
+
|
238
387
|
it "should not create duplicate taggings" do
|
239
388
|
bob = TaggableModel.create(:name => "Bob")
|
240
389
|
lambda {
|
@@ -243,12 +392,12 @@ describe "Taggable" do
|
|
243
392
|
bob.save
|
244
393
|
}.should change(ActsAsTaggableOn::Tagging, :count).by(1)
|
245
394
|
end
|
246
|
-
|
395
|
+
|
247
396
|
describe "Associations" do
|
248
397
|
before(:each) do
|
249
398
|
@taggable = TaggableModel.create(:tag_list => "awesome, epic")
|
250
399
|
end
|
251
|
-
|
400
|
+
|
252
401
|
it "should not remove tags when creating associated objects" do
|
253
402
|
@taggable.untaggable_models.create!
|
254
403
|
@taggable.reload
|
@@ -264,6 +413,10 @@ describe "Taggable" do
|
|
264
413
|
it "should return all column names joined for TaggableModel GROUP clause" do
|
265
414
|
@taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.id, taggable_models.name, taggable_models.type"
|
266
415
|
end
|
416
|
+
|
417
|
+
it "should return all column names joined for NonStandardIdTaggableModel GROUP clause" do
|
418
|
+
@taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.#{TaggableModel.primary_key}, taggable_models.name, taggable_models.type"
|
419
|
+
end
|
267
420
|
end
|
268
421
|
|
269
422
|
describe "Single Table Inheritance" do
|
@@ -272,45 +425,119 @@ describe "Taggable" do
|
|
272
425
|
@inherited_same = InheritingTaggableModel.new(:name => "inherited same")
|
273
426
|
@inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
|
274
427
|
end
|
275
|
-
|
428
|
+
|
276
429
|
it "should be able to save tags for inherited models" do
|
277
430
|
@inherited_same.tag_list = "bob, kelso"
|
278
431
|
@inherited_same.save
|
279
432
|
InheritingTaggableModel.tagged_with("bob").first.should == @inherited_same
|
280
433
|
end
|
281
|
-
|
434
|
+
|
282
435
|
it "should find STI tagged models on the superclass" do
|
283
436
|
@inherited_same.tag_list = "bob, kelso"
|
284
437
|
@inherited_same.save
|
285
438
|
TaggableModel.tagged_with("bob").first.should == @inherited_same
|
286
439
|
end
|
287
|
-
|
440
|
+
|
288
441
|
it "should be able to add on contexts only to some subclasses" do
|
289
442
|
@inherited_different.part_list = "fork, spoon"
|
290
443
|
@inherited_different.save
|
291
444
|
InheritingTaggableModel.tagged_with("fork", :on => :parts).should be_empty
|
292
445
|
AlteredInheritingTaggableModel.tagged_with("fork", :on => :parts).first.should == @inherited_different
|
293
446
|
end
|
294
|
-
|
447
|
+
|
295
448
|
it "should have different tag_counts_on for inherited models" do
|
296
449
|
@inherited_same.tag_list = "bob, kelso"
|
297
450
|
@inherited_same.save!
|
298
451
|
@inherited_different.tag_list = "fork, spoon"
|
299
452
|
@inherited_different.save!
|
300
|
-
|
453
|
+
|
301
454
|
InheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso)
|
302
455
|
AlteredInheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(fork spoon)
|
303
456
|
TaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso fork spoon)
|
304
457
|
end
|
305
|
-
|
458
|
+
|
306
459
|
it 'should store same tag without validation conflict' do
|
307
460
|
@taggable.tag_list = 'one'
|
308
461
|
@taggable.save!
|
309
|
-
|
462
|
+
|
310
463
|
@inherited_same.tag_list = 'one'
|
311
464
|
@inherited_same.save!
|
312
|
-
|
465
|
+
|
313
466
|
@inherited_same.update_attributes! :name => 'foo'
|
314
467
|
end
|
315
468
|
end
|
469
|
+
|
470
|
+
describe "NonStandardIdTaggable" do
|
471
|
+
before(:each) do
|
472
|
+
clean_database!
|
473
|
+
@taggable = NonStandardIdTaggableModel.new(:name => "Bob Jones")
|
474
|
+
@taggables = [@taggable, NonStandardIdTaggableModel.new(:name => "John Doe")]
|
475
|
+
end
|
476
|
+
|
477
|
+
it "should have tag types" do
|
478
|
+
[:tags, :languages, :skills, :needs, :offerings].each do |type|
|
479
|
+
NonStandardIdTaggableModel.tag_types.should include type
|
480
|
+
end
|
481
|
+
|
482
|
+
@taggable.tag_types.should == NonStandardIdTaggableModel.tag_types
|
483
|
+
end
|
484
|
+
|
485
|
+
it "should have tag_counts_on" do
|
486
|
+
NonStandardIdTaggableModel.tag_counts_on(:tags).all.should be_empty
|
487
|
+
|
488
|
+
@taggable.tag_list = ["awesome", "epic"]
|
489
|
+
@taggable.save
|
490
|
+
|
491
|
+
NonStandardIdTaggableModel.tag_counts_on(:tags).length.should == 2
|
492
|
+
@taggable.tag_counts_on(:tags).length.should == 2
|
493
|
+
end
|
494
|
+
|
495
|
+
it "should be able to create tags" do
|
496
|
+
@taggable.skill_list = "ruby, rails, css"
|
497
|
+
@taggable.instance_variable_get("@skill_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
|
498
|
+
|
499
|
+
lambda {
|
500
|
+
@taggable.save
|
501
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(3)
|
502
|
+
|
503
|
+
@taggable.reload
|
504
|
+
@taggable.skill_list.sort.should == %w(ruby rails css).sort
|
505
|
+
end
|
506
|
+
|
507
|
+
it "should be able to create tags through the tag list directly" do
|
508
|
+
@taggable.tag_list_on(:test).add("hello")
|
509
|
+
@taggable.tag_list_cache_on(:test).should_not be_empty
|
510
|
+
@taggable.tag_list_on(:test).should == ["hello"]
|
511
|
+
|
512
|
+
@taggable.save
|
513
|
+
@taggable.save_tags
|
514
|
+
|
515
|
+
@taggable.reload
|
516
|
+
@taggable.tag_list_on(:test).should == ["hello"]
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
describe "Dirty Objects" do
|
521
|
+
before(:each) do
|
522
|
+
@taggable = TaggableModel.create(:tag_list => "awesome, epic")
|
523
|
+
end
|
524
|
+
|
525
|
+
it 'should show changes of dirty object' do
|
526
|
+
@taggable.changes.should == {}
|
527
|
+
@taggable.tag_list = 'one'
|
528
|
+
@taggable.changes.should == {"tag_list"=>["awesome, epic", ["one"]]}
|
529
|
+
|
530
|
+
@taggable.tag_list_changed?.should be_true
|
531
|
+
@taggable.tag_list_was.should == "awesome, epic"
|
532
|
+
@taggable.tag_list_change.should == ["awesome, epic", ["one"]]
|
533
|
+
end
|
534
|
+
|
535
|
+
it 'should show no changes if the same tag_list' do
|
536
|
+
@taggable.tag_list = "awesome, epic"
|
537
|
+
@taggable.tag_list_changed?.should be_false
|
538
|
+
@taggable.changes.should == {}
|
539
|
+
end
|
540
|
+
end
|
316
541
|
end
|
542
|
+
|
543
|
+
|
@@ -16,7 +16,21 @@ describe "Tagger" do
|
|
16
16
|
@user.tag(@taggable, :with=>'ruby,scheme', :on=>:tags)
|
17
17
|
@user.owned_tags.size == 2
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
|
+
it "should scope objects returned by tagged_with by owners" do
|
21
|
+
@taggable2 = TaggableModel.create(:name => "Jim Jones")
|
22
|
+
@taggable3 = TaggableModel.create(:name => "Jane Doe")
|
23
|
+
|
24
|
+
@user2 = TaggableUser.new
|
25
|
+
@user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
|
26
|
+
@user2.tag(@taggable2, :with => 'ruby, scheme', :on => :tags)
|
27
|
+
@user2.tag(@taggable3, :with => 'ruby, scheme', :on => :tags)
|
28
|
+
|
29
|
+
TaggableModel.tagged_with(%w(ruby scheme), :owned_by => @user).count.should == 1
|
30
|
+
TaggableModel.tagged_with(%w(ruby scheme), :owned_by => @user2).count.should == 2
|
31
|
+
|
32
|
+
end
|
33
|
+
|
20
34
|
it "should not overlap tags from different taggers" do
|
21
35
|
@user2 = TaggableUser.new
|
22
36
|
lambda{
|
@@ -28,35 +42,35 @@ describe "Tagger" do
|
|
28
42
|
|
29
43
|
@user.owned_tags.map(&:name).sort.should == %w(ruby scheme).sort
|
30
44
|
@user2.owned_tags.map(&:name).sort.should == %w(java python lisp ruby).sort
|
31
|
-
|
45
|
+
|
32
46
|
@taggable.tags_from(@user).sort.should == %w(ruby scheme).sort
|
33
47
|
@taggable.tags_from(@user2).sort.should == %w(java lisp python ruby).sort
|
34
|
-
|
48
|
+
|
35
49
|
@taggable.all_tags_list.sort.should == %w(ruby scheme java python lisp).sort
|
36
50
|
@taggable.all_tags_on(:tags).size.should == 5
|
37
51
|
end
|
38
|
-
|
52
|
+
|
39
53
|
it "should not lose tags from different taggers" do
|
40
54
|
@user2 = TaggableUser.create
|
41
55
|
@user2.tag(@taggable, :with => 'java, python, lisp, ruby', :on => :tags)
|
42
|
-
@user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
|
43
|
-
|
56
|
+
@user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
|
57
|
+
|
44
58
|
lambda {
|
45
59
|
@user2.tag(@taggable, :with => 'java, python, lisp', :on => :tags)
|
46
60
|
}.should change(ActsAsTaggableOn::Tagging, :count).by(-1)
|
47
61
|
|
48
62
|
[@user, @user2, @taggable].each(&:reload)
|
49
|
-
|
63
|
+
|
50
64
|
@taggable.tags_from(@user).sort.should == %w(ruby scheme).sort
|
51
65
|
@taggable.tags_from(@user2).sort.should == %w(java python lisp).sort
|
52
|
-
|
66
|
+
|
53
67
|
@taggable.all_tags_list.sort.should == %w(ruby scheme java python lisp).sort
|
54
68
|
@taggable.all_tags_on(:tags).length.should == 5
|
55
69
|
end
|
56
70
|
|
57
71
|
it "should not lose tags" do
|
58
72
|
@user2 = TaggableUser.create
|
59
|
-
|
73
|
+
|
60
74
|
@user.tag(@taggable, :with => 'awesome', :on => :tags)
|
61
75
|
@user2.tag(@taggable, :with => 'awesome, epic', :on => :tags)
|
62
76
|
|
@@ -64,28 +78,35 @@ describe "Tagger" do
|
|
64
78
|
@user2.tag(@taggable, :with => 'epic', :on => :tags)
|
65
79
|
}.should change(ActsAsTaggableOn::Tagging, :count).by(-1)
|
66
80
|
|
67
|
-
@taggable.reload
|
81
|
+
@taggable.reload
|
68
82
|
@taggable.all_tags_list.should include('awesome')
|
69
83
|
@taggable.all_tags_list.should include('epic')
|
70
84
|
end
|
71
|
-
|
85
|
+
|
72
86
|
it "should not lose tags" do
|
73
87
|
@taggable.update_attributes(:tag_list => 'ruby')
|
74
88
|
@user.tag(@taggable, :with => 'ruby, scheme', :on => :tags)
|
75
|
-
|
89
|
+
|
76
90
|
[@taggable, @user].each(&:reload)
|
77
91
|
@taggable.tag_list.should == %w(ruby)
|
78
92
|
@taggable.all_tags_list.sort.should == %w(ruby scheme).sort
|
79
|
-
|
93
|
+
|
80
94
|
lambda {
|
81
95
|
@taggable.update_attributes(:tag_list => "")
|
82
96
|
}.should change(ActsAsTaggableOn::Tagging, :count).by(-1)
|
83
|
-
|
97
|
+
|
84
98
|
@taggable.tag_list.should == []
|
85
99
|
@taggable.all_tags_list.sort.should == %w(ruby scheme).sort
|
86
100
|
end
|
87
101
|
|
88
102
|
it "is tagger" do
|
89
103
|
@user.is_tagger?.should(be_true)
|
90
|
-
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should skip save if skip_save is passed as option" do
|
107
|
+
lambda {
|
108
|
+
@user.tag(@taggable, :with => 'epic', :on => :tags, :skip_save => true)
|
109
|
+
}.should_not change(ActsAsTaggableOn::Tagging, :count)
|
110
|
+
|
111
|
+
end
|
91
112
|
end
|
@@ -13,11 +13,7 @@ describe ActsAsTaggableOn::Tagging do
|
|
13
13
|
|
14
14
|
@tagging.should_not be_valid
|
15
15
|
|
16
|
-
|
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
|