acts-as-taggable-on 2.0.6 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|